Related
I made a chat application using sockets in c language. When i run server and client on same device it works fine. But, when i run client and server on different device client shows connect error. is this problem related to ip address.
server side code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <signal.h>
#define MAX_CLIENTS 100
#define BUFFER_SZ 2048
static _Atomic unsigned int cli_count = 0;
static int uid = 10;
/* Client structure */
typedef struct{
struct sockaddr_in address;
int sockfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void str_overwrite_stdout() {
printf("\r%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* Add clients to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(!clients[i]){
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Remove clients to queue */
void queue_remove(int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Send message to all clients except sender */
void send_message(char *s, int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i<MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid != uid){
if(write(clients[i]->sockfd, s, strlen(s)) < 0){
perror("ERROR: write to descriptor failed");
break;
}
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Handle all communication with the client */
void *handle_client(void *arg){
char buff_out[BUFFER_SZ];
char name[32];
int leave_flag = 0;
cli_count++;
client_t *cli = (client_t *)arg;
// Name
if(recv(cli->sockfd, name, 32, 0) <= 0 || strlen(name) < 2 || strlen(name) >= 32-1){
printf("Didn't enter the name.\n");
leave_flag = 1;
} else{
strcpy(cli->name, name);
sprintf(buff_out, "%s has joined\n", cli->name);
printf("%s", buff_out);
send_message(buff_out, cli->uid);
}
bzero(buff_out, BUFFER_SZ);
while(1){
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buff_out, BUFFER_SZ, 0);
if (receive > 0){
if(strlen(buff_out) > 0){
send_message(buff_out, cli->uid);
str_trim_lf(buff_out, strlen(buff_out));
printf("%s -> %s\n", buff_out, cli->name);
}
} else if (receive == 0 || strcmp(buff_out, "exit") == 0){
sprintf(buff_out, "%s has left\n", cli->name);
printf("%s", buff_out);
send_message(buff_out, cli->uid);
leave_flag = 1;
} else {
printf("ERROR: -1\n");
leave_flag = 1;
}
bzero(buff_out, BUFFER_SZ);
}
/* Delete client from queue and yield thread */
close(cli->sockfd);
queue_remove(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
int option = 1;
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* Socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
/* Ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
if(setsockopt(listenfd, SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option)) < 0){
perror("ERROR: setsockopt failed");
return EXIT_FAILURE;
}
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR: Socket binding failed");
return EXIT_FAILURE;
}
/* Listen */
if (listen(listenfd, 10) < 0) {
perror("ERROR: Socket listening failed");
return EXIT_FAILURE;
}
printf("=== WELCOME TO THE CHATROOM ===\n");
while(1){
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* Check if max clients is reached */
if((cli_count + 1) == MAX_CLIENTS){
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\n", cli_addr.sin_port);
close(connfd);
continue;
}
/* Client settings */
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* Reduce CPU usage */
sleep(1);
}
return EXIT_SUCCESS;
}
client side code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define LENGTH 2048
// Global variables
volatile sig_atomic_t flag = 0;
int sockfd = 0;
unsigned char name[32];
void str_overwrite_stdout() {
printf("%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void catch_ctrl_c_and_exit(int sig) {
flag = 1;
}
void send_msg_handler() {
unsigned char message[LENGTH] = {};
unsigned char buffer[3000] = {};
while(1) {
str_overwrite_stdout();
fgets(message, LENGTH, stdin);
str_trim_lf(message, LENGTH);
if (strcmp(message, "exit") == 0) {
break;
} else {
sprintf(buffer, "%s: %s\n", name, message);
send(sockfd, buffer, strlen(buffer), 0);
}
bzero(message, LENGTH);
bzero(buffer, LENGTH + 32);
}
catch_ctrl_c_and_exit(2);
}
void recv_msg_handler() {
char message[LENGTH] = {};
while (1) {
int receive = recv(sockfd, message, LENGTH, 0);
if (receive > 0) {
printf("%s", message);
str_overwrite_stdout();
} else if (receive == 0) {
break;
} else {
// -1
}
memset(message, 0, sizeof(message));
}
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "152.168.0.100";
int port = atoi(argv[1]);
signal(SIGINT, catch_ctrl_c_and_exit);
printf("Please enter your name: ");
fgets(name, 32, stdin);
str_trim_lf(name, strlen(name));
if (strlen(name) > 32 || strlen(name) < 2){
printf("Name must be less than 30 and more than 2 characters.\n");
return EXIT_FAILURE;
}
struct sockaddr_in server_addr;
/* Socket settings */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// Connect to Server
int err = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
printf("ERROR: connect\n");
return EXIT_FAILURE;
}
// Send name
send(sockfd, name, 32, 0);
printf("=== WELCOME TO THE CHATROOM ===\n");
pthread_t send_msg_thread;
if(pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
pthread_t recv_msg_thread;
if(pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
while (1){
if(flag){
printf("\nBye\n");
break;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
it is showing connect error when i run it on different machines.
So as far as I can tell, the codes that you posted are incomplete, but let's assume that, as you say, everything is working fine, it work in the local machine, but when you run the two programs in two different devices, the client fails to connect to the server.
Then I would ask you the question, as I also asked it in the comment: are the two devices in the same subnet? You have a home router, and both devices are connected to this router?
If not, then that will be the problem. I am not sure how deep you understand IP addresses and subnets, but here is a simple summary. When you look at the IP address on your PC, you see your private IP address that was given you by the router. Let's say, that you have two machines connected to the same router, one has the IP address of 192.168.0.101, the other 192.168.0.102. If 192.168.0.101 is the client and wants to connect to the server that runs on 192.168.0.102, it should work without problems.
But let's say that you send the server code to me. I run the server on my computer, which has the IP address of 192.168.0.103, you still run the client at home on 192.168.0.101. In this case, you won't be able to connect to my machine, because the 192.168.0.103 is the private IP address given out by my own router. For that to work, I would have to at least configure my router to open up a port and forward the packets coming to that port to my PC. Then I would have to give you the public IP and the port. And so if the firewall is configured so that it allows your packets into my network, then it might work.
Of course it is a bit more complicated than this, but this is the general idea.
If this is not the case for you, and the devices are on the same subnet, then we would simply need more information and the comlete code.
In our project we have to make a game (that is already done) and allow a multiplayer way to play (with a P2P method). I have made "client" and "server" programs to begin (I think that in the future the programs will be the same) using the select method in order to allow them to send and receive messages to each other. When I tried to send a message (a number written in the terminal) from the server to the client, it's ok, but, when I tried to do the same from the client to the server, nothing happened. Moreover, when I start sending a message from the client to the server, it works but the server can't send nothing (more precisely the client doesn't receive anything).
I don't understand this behaviour, and so I've tried to use threads but my profesor said that I don't need it if I use well select...
You will find below the code for the "server"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#define FALSE 0
#define TRUE 1
typedef struct Element Element;
struct Element
{
int data_i;
Element *suivant;
};
typedef struct
{
Element *premier;
} File;
void initialise(File *file)
{
file->premier = NULL;
}
int enfiler(File *file, long long nbre)
{
if (file == NULL)
{
printf("Il faut initialiser la file");
return EXIT_FAILURE;
}
Element *new_el = malloc(sizeof(Element));
new_el->suivant = NULL;
new_el->data_i = nbre;
if (file->premier != NULL)
{
Element *elementActuel = file->premier;
while (elementActuel->suivant != NULL)
{
elementActuel = elementActuel->suivant;
}
elementActuel->suivant = new_el;
printf("j'ai bien enfilé le message\n");
return EXIT_SUCCESS;
}
else
{
file->premier = new_el;
printf("j'ai bien enfilé le message\n");
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
Element defiler(File *file)
{
if (file != NULL && file->premier != NULL)
{
Element *el = malloc(sizeof(Element));
el = file->premier;
file->premier = file->premier->suivant;
free (el);
return *el;
}
//voir quoi retourner lors d'erreurs
}
void stop(char * msg){
perror(msg);
exit(EXIT_FAILURE);
}
typedef struct {
int fd;
}client_info;
int main(int argc,char *argv[]){
//Variables
int opt = TRUE;
int master_socket=0, new_socket=0, max_client = 30, activity=0, i=0;
int max_sd = 0;
client_info client_tab[max_client];
struct sockaddr_in address;
int addr_len=0;
fd_set readfds;
long long message=0;
int64_t buffer=0;
int smess = 0;
int sd =0;
write(0,"initialisation\n",15);
//initializing the "file"
File file_mess, file_recus;
initialise(&file_mess);
initialise(&file_recus);
//Initializing files descriptors
for (i = 0; i < max_client; i++)
{
client_tab[i].fd = -1;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//Initializing address
address.sin_family = AF_INET;
inet_aton("127.0.0.1", &address.sin_addr);
address.sin_port = htons( 1234 );
addr_len = sizeof(address);
//Configurating master_socket to listen to new connections .
if(bind(master_socket,(const struct sockaddr *)&address,addr_len)<0) stop("binding error");
if(listen(master_socket,3)<0) stop("Problem initializing master_socket");
printf("Enterring while\n");
while(TRUE){
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
FD_SET(STDIN_FILENO,&readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_client ; i++)
{
//socket descriptor
sd = client_tab[i].fd;
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR)) stop("select error");
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addr_len))<0) stop("accepting problem");
for (i = 0; i < max_client; i++)
{
//if position is empty
if( client_tab[i].fd == -1 )
{
client_tab[i].fd = new_socket;
printf("Adding to list of sockets as %d\n\n" , i);
// Après avoir ajouté le client dans le tableau, le serveur lui renvoie un id (valeur de i)
if(send(client_tab[i].fd, &i, sizeof(int), 0) == -1) stop("recv() : envoi de l'id");
break;
}
}
}
//else its some IO operation on some other socket :)
for (i = 0; i < max_client; i++)
{
sd = client_tab[i].fd;
if (FD_ISSET( sd , &readfds) && sd!=-1)
{
//Check if it was for closing , and also read the incoming message
if ((smess = recv( sd,(char *)&buffer,__SIZEOF_LONG_LONG__,0)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&address , (socklen_t*)&addr_len);
printf("%i disconnected.\n",i);
//Close the socket and mark as 0 in list for reuse
close( sd );
client_tab[i].fd = -1;
}
else{
message = ntohl(buffer);
buffer=0;
printf("Message reçu de %i : %lli\n",i,message);
enfiler (&file_recus, message);
message = 0;
}
}
}
if(FD_ISSET(STDIN_FILENO,&readfds)){
scanf("%lli",&message);
enfiler(&file_mess,message);
buffer = htonl(file_mess.premier->data_i);
for (i = 0; i < max_client; i++)
{
if(client_tab[i].fd != -1) send(client_tab[i].fd,&buffer,sizeof(buffer),0);
}
printf("J'ai bien envoyé : %lli\n",message);
defiler(&file_mess);
buffer = 0;
message=0;
}
if (file_mess.premier != NULL)
{
printf("regarde si des trucs à envoyer\n");
buffer = htonl(file_mess.premier->data_i);
//envoie à tout le monde
for (i = 0; i < max_client; i++)
{
if (client_tab[i].fd != -1)
{
send(client_tab[i].fd, &buffer, sizeof(buffer), 0);
}
}
printf("envoyé\n");
defiler(&file_mess);
buffer=0;
}
}
}
And here the "client" :
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define BUFSIZE 100
#define TRUE 1
#define FALSE 0
typedef struct Element Element;
struct Element
{
int data_i;
Element *suivant;
};
typedef struct
{
Element *premier;
} File;
void initialise(File *file)
{
file->premier = NULL;
}
int enfiler(File *file, long long nbre)
{
if (file == NULL)
{
printf("Il faut initialiser la file");
return EXIT_FAILURE;
}
Element *new_el = malloc(sizeof(Element));
new_el->suivant = NULL;
new_el->data_i = nbre;
if (file->premier != NULL)
{
Element *elementActuel = file->premier;
while (elementActuel->suivant != NULL)
{
elementActuel = elementActuel->suivant;
}
elementActuel->suivant = new_el;
printf("j'ai bien enfilé le message\n");
return EXIT_SUCCESS;
}
else
{
file->premier = new_el;
printf("j'ai bien enfilé le message\n");
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
Element defiler(File *file)
{
if (file != NULL && file->premier != NULL)
{
Element *el = malloc(sizeof(Element));
el = file->premier;
file->premier = file->premier->suivant;
free(el);
return *el;
}
}
void stop(char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
//Creating a TCP|IPV4 socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
stop("Probleme de socket");
//Creating the socket
struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(1234);
inet_aton("127.0.0.1", &serv_addr.sin_addr);
//initializing the "file"
File file_mess, file_recus;
initialise(&file_mess);
initialise(&file_recus);
//initiliazing the file descriptor set
fd_set readfds;
FD_ZERO(&readfds);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
stop("Connection error");
int id = 0;
if(recv(sockfd, &id, sizeof(int), 0) == -1) stop ("recv() : reception de l'id"); // Le client récupère son id
printf("Mon id : %i\n", id);
FD_SET(sockfd, &readfds);
FD_SET(STDIN_FILENO, &readfds);
//Variables
int nSelect = 0;
int n;
long long mess = 0;
int64_t fmess = 0;
while (TRUE)
{
nSelect = select(sockfd + 1, &readfds, NULL, NULL, NULL);
if (nSelect < 0)
stop("Select error");
if (FD_ISSET(STDIN_FILENO, &readfds))
{
scanf("%lli", &mess);
fmess = htonl(mess);
send(sockfd, &fmess, sizeof(fmess), 0);
printf("J'ai bien envoyé : %lli\n", mess);
mess = 0;
}
if (file_mess.premier != NULL)
{
send(sockfd, &file_mess.premier->data_i, sizeof(file_mess.premier->data_i), 0);
defiler(&file_mess);
}
if (FD_ISSET(sockfd, &readfds))
{
printf("reçu\n");
if ((n=recv(sockfd, &mess, sizeof(long long), 0)) < 0)
stop("recv(");
else if (n==0)
{
printf("déconnexion...\n");
close(sockfd);
return EXIT_SUCCESS;
}
else
{
printf("cc\n");
mess = ntohl(fmess);
fmess = 0;
printf("Message reçu: %lli\n", mess);
enfiler(&file_recus, mess);
mess = 0;
}
}
}
exit(0);
}
Here, you will find my tests:
1-The client begins the conversation by sending 13, but doesn't receive what the server tries to send
client
server
2-Then, the server begins the conversation sending 15, the client doesn't receive it and he sends nothing
client, before stopping
server, before stopping
client, stopped
server, stopped
PS: the use of file is to allow a good treatment for the data (I will made a protocol [using long long numbers] and so I will have to process the data before sending and after receiving). And to finish, this code also permits to give a number to identify the players (and to know who the data receive is for).
I have a question about socket.I send N-size data from client to server, N-size less than 100 byte.So I think my data should not be split to multiple tcp packet.In my opinion, Client send data should be done at one times and Server can receive data at one time.But The result is not satisfactory.Real situation is the server need call read data.I don't understand it.Follow code:
epoll_server.cpp(only receive data.)
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <netdb.h>
#define BUFSIZE 1024
#define INITSIZE 1024
#define MAXEVENTCOUNT 10240
// add non-blocking to sockfd
int make_socket_non_blocking(int fd)
{
// get initial flag
int src_flags;
src_flags= fcntl(fd, F_GETFL,0);
if(src_flags == -1)
{
perror("fcntl get error.");
return-1;
}
// add non-blocking
int new_flags = src_flags | O_NONBLOCK;
int ret_value;
ret_value = fcntl(fd, F_SETFL, new_flags);
if(ret_value == -1)
{
perror("fcntl set error.");
return-1;
}
return 0;
}
// main function
int main(int argc, char* argv[])
{
int server_sockfd, client_sockfd;
int server_len;
struct sockaddr_in server_address;
// create server socket fd
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
// init server address struct
bzero(&server_address, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9567);
server_address.sin_addr.s_addr = INADDR_ANY;
server_len = sizeof(server_address);
// bind server address info for server fd
if((bind(server_sockfd, (struct sockaddr*)&server_address, server_len)) == -1)
{
perror("bind error");
exit(EXIT_FAILURE);
}
// let server is listened state
listen(server_sockfd, 5);
printf("server start waiting for connect...\r\n");
// only suggestion
int efd = epoll_create(INITSIZE);
if(-1 == efd)
{
printf("epoll_create error happen.\n");
return -1;
}
// set server_sockfd
struct epoll_event server_event, event;
server_event.data.fd = server_sockfd;
server_event.events = EPOLLIN | EPOLLET;
int ret_epollctl = epoll_ctl(efd, EPOLL_CTL_ADD, server_sockfd, &server_event);
if(-1 == ret_epollctl)
{
printf("epoll_ctl error happen when efd is adding server_sockfd.\n");
return -1;
}
/* event loop */
struct epoll_event* return_events;
// set timeout is 3000 ms
int timeout_msecond = 3000;
return_events = (struct epoll_event*)malloc(MAXEVENTCOUNT*sizeof(struct epoll_event));
int count = 0;
while(1)
{
int ret_epollwait = epoll_wait(efd, return_events, MAXEVENTCOUNT, timeout_msecond);
// part_1:epoll_wait error happen
if(-1 == ret_epollwait)
{
printf("logged epoll_wait error happen.\n");
continue;
}
// part_2:epoll_wait timeout
if(0 == ret_epollwait)
{
printf("logged epoll_wait timeout.\n");
continue;
}
// part_3:do some other event
int index = 0;
for(index = 0; index < MAXEVENTCOUNT; index++)
{
// part_3-1:hup ...
if((return_events[index].events & EPOLLERR)
|| (return_events[index].events & EPOLLHUP)
|| !(return_events[index].events & EPOLLIN) )
{
continue;
}
// part_3-2:is connection
if(return_events[index].data.fd == server_sockfd)
{
struct sockaddr_in client_address;
int client_len = sizeof(client_address);
// server accept connection from client
int client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, (socklen_t*)&client_len);
// part_3-2-1:connection error happen
if(-1 == client_sockfd)
{
if((EAGAIN == errno)
|| (EWOULDBLOCK == errno) )
{
continue;
}
else
{
printf("accept error occured.\n");
continue;
}
}
else // part_3-2-2:normal connection
{
// get clinet some information
char hostinfo_buf[BUFSIZE] = {0};
char servname_buf[BUFSIZE] = {0};
int tmp_ret = getnameinfo((struct sockaddr*)&client_address, client_len, hostinfo_buf, sizeof(hostinfo_buf), servname_buf, sizeof(servname_buf), NI_NUMERICHOST| NI_NUMERICSERV);
if(0 == tmp_ret)
{
printf("Accepted connection on descriptor %d:ip=%s, port=%s.\n", client_sockfd, hostinfo_buf, servname_buf);
}
// set client_sockfd to non-blocking
tmp_ret = make_socket_non_blocking(client_sockfd);
if(-1 == tmp_ret)
{
printf("set client_sockfd=%d to non-blocking error occured.\n", client_sockfd);
abort();
}
// set client_sockfd is EPOLLIN, EPOLLET
event.data.fd = client_sockfd;
event.events = EPOLLIN | EPOLLET;
tmp_ret = epoll_ctl(efd, EPOLL_CTL_ADD, client_sockfd, &event);
if(tmp_ret == -1)
{
printf("efd add %d has a error.\n", client_sockfd);
continue;
}
printf("add descriptor %d:ip=%s, port=%s successfully.\n", client_sockfd, hostinfo_buf, servname_buf);
}
continue;
}
// part_3-3:read data from client
printf("read data start++++\n");
int temp = 0;
// get recv_cache size start
int recvsize = 0;
socklen_t optlen = sizeof(recvsize);
int err = getsockopt(return_events[index].data.fd, SOL_SOCKET, SO_RCVBUF, &recvsize, &optlen);
printf("recv cache size :%d\n", recvsize);
// get recv_cache size end
while(1) // start while(1)
{
printf("%d times read data\n", ++temp);
char* recv_buffer = (char*)malloc(1024+1);
memset(recv_buffer, 0, 1025);
// int ret_read = read(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer));
int ret_read = recv(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer), 0);
// part_3-3-1:read return error
if(-1 == ret_read)
{
if(EAGAIN != errno)
{
printf("read data from %d error occured, errno=%d, %s.\n", return_events[index].data.fd, errno, strerror(errno));
}
break;
}
// part_3-3-2:no data
if(0 == ret_read)
{
continue;
}
// part_3-3-3:output data. If data is 'bye', connection will close.
if(ret_read > 0)
{
printf("%d client's data:size=%dbyte, content=%s\n", return_events[index].data.fd, ret_read, recv_buffer);
// part_3-3-3-1:close connection and remove client_sockfd
if((recv_buffer[0] == 'b')
&& (recv_buffer[1] == 'y')
&& (recv_buffer[2] == 'e') )
{
close(return_events[index].data.fd);
printf("close %d, ", return_events[index].data.fd);
int tmp_ret = epoll_ctl(efd, EPOLL_CTL_DEL, return_events[index].data.fd, NULL);
if(tmp_ret == -1)
{
printf("efd del %d has a error.\n", client_sockfd);
}
printf("remove descriptor %d successfully.\n", return_events[index].data.fd);
}
}
} // end of while(1)
printf("read data finish------\n");
}
}
free(return_events);
// close server_sockfd
shutdown(server_sockfd, 2);
return 0;
}
epoll_client.cpp(only send data.)
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFSIZE 1024
int main(int argc, char* argv[])
{
int sock_clientfd, ret_recvsize, i;
struct sockaddr_in dest, mine;
char send_buffer[BUFSIZE + 1];
// create socket fd
if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Socket");
exit(EXIT_FAILURE);
}
// init server address that client will connetct to.
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(9567);
if(argc != 2)
{
printf("Usage: %s <dest ip>\n", argv[0]);
printf("Usage: %s 127.0.0.1\n", argv[0]);
return -1;
}
printf("-----\n");
if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
{
perror(argv[1]);
exit(1);
}
// connect to server
printf("will connect!\n");
if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
{
perror("Connect ");
exit(EXIT_FAILURE);
}
while(1)
{
bzero(send_buffer, BUFSIZE + 1);
printf("input message:");
fgets(send_buffer, BUFSIZE, stdin);
send_buffer[strlen(send_buffer) - 1] = '\0';
printf("%d\n", strlen(send_buffer));
int send_retsize = send(sock_clientfd, send_buffer, strlen(send_buffer), 0);
if(send_retsize == -1)
{
perror("send data to client error happen!");
exit(EXIT_FAILURE);
}
printf("send succ data:%s\n", send_buffer);
if((send_buffer[0] == 'b')
&& (send_buffer[1] == 'y')
&& (send_buffer[2] == 'e') )
{
printf("client active close connect.\n");
break;
}
}
// close sock_clientfd
close(sock_clientfd);
return 0;
}
Follow pircture is some run info:
epoll_server.png
epoll_client.png
The server read data is only 8 byte, Is the kernel design epoll is this?
I guess the reasons are as follows pirture:
The reason you don't receive everything that is available in one read is because you only read 8 bytes at a time.
char* recv_buffer = (char*)malloc(1024+1);
int ret_read = recv(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer), 0);
// part_3-3-1:read return error
recv_buffer is a char* not an array, so sizeof recv_buffer equals the size of a pointer which in your case is 8.
Note that you should never rely on data arriving in packages. If your message protocol states that you should be getting 10 bytes never expect all 10 bytes to be available at once. You should always code in a way that can handle data being split up into multiple reads.
If the thread handles a single socket then a simple do { read... } while (total_bytes_received < expected_bytes); will suffice.
If the thread handles multiple connections, then you need to save the bytes you have read and then continue to manage other sockets that are ready before returning to your handling loop that will use select/epoll to wait for more data.
The code below is a chatroom. First I run the server, and then the clients. The clients reach the server with sockets. Then the server accepts the connection and the client has to enter his nickname.
I use threads because this way, the fgets is not blocking and multiple clients can reach the server even if a client is slow to enter his nickname.
Until here, there is no problem and everything works, even the select.
After this, the client can introduce a message which is send to all the clients (this is the principle of a chat :P). The problem is that from here, the select doesn't react and stay blocked. Maybe it's because of the FD_set or the threads but actually I don't know and that is the problem.
server.c :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#define MYPORT 5555
#define PSEUDO_MAX 30
#define MESSAGE_MAX 1000
#define BACKLOG 10
struct User {
char pseudo[PSEUDO_MAX];
int socket;
struct User *next;
};
struct Thread {
struct User* lst;
fd_set* readfds;
int* max;
int socket;
pthread_mutex_t* mutex;
};
void add(struct User** pp, const char *t, const int sock)
{
struct User *p = malloc(sizeof(*p));
strcpy(p->pseudo, t);
p->socket = sock;
p->next = NULL;
while (*pp)
pp = &(*pp)->next;
*pp = p;
}
void print(const struct User* n)
{
for ( ; n; n = n->next ){
printf("LISTE : %s\n", n->pseudo);
printf("LISTE : %d\n", n->socket);
}
printf("\n");
}
void *thread_pseudo(void *arg)
{
struct Thread* ps_thread = (struct Thread*)arg;
int nsock = ps_thread->socket;
struct User* lst = ps_thread->lst;
int* max = ps_thread->max;
pthread_mutex_t* mutex = ps_thread->mutex;
pthread_mutex_lock(mutex);
fd_set* readfds = ps_thread->readfds;
pthread_mutex_unlock(mutex);
char pseudo[PSEUDO_MAX];
if (send(nsock, "Bienvenue sur le chat !\n",24,0)==-1){
perror("Serveur: send");
}
if (recv(nsock, pseudo, PSEUDO_MAX, 0) == -1) {
perror("Serveur: recv 2: ");
}
pthread_mutex_lock(mutex);
FD_SET(nsock, readfds); // Ajout du nouveau socket au select
*max = (nsock < *max ? *max : nsock);
pthread_mutex_unlock(mutex);
add(&lst, pseudo, nsock);
print(lst);
pthread_exit(NULL);
}
int findSender(fd_set* readfds, const struct User* n){
int socket = 0;
for ( ; n; n = n->next ){
if(FD_ISSET(n->socket, readfds)){
socket = n->socket;
}
}
return socket;
}
int main()
{
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int sockfd2, new_fd; // listen on sock_fd, new connection on new_fd
int max;
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
unsigned int sin_size;
int yes=1;
struct User *lst = NULL; // Initialisation de la liste des Users
struct Thread *ps_thread = malloc(sizeof(*ps_thread)); // Initialisation du struct qui contient les paramètres pour le thread
fd_set readfds;
if ((sockfd2 = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("Serveur: socket 2");
return EXIT_FAILURE;
}
if (setsockopt(sockfd2,SOL_SOCKET,SO_REUSEADDR, &yes,sizeof(int)) == -1) {
perror("Serveur: setsockopt 2");
return EXIT_FAILURE;
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', 8);
if (bind(sockfd2, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
perror("Serveur: bind 2");
return EXIT_FAILURE;
}
if (listen(sockfd2, BACKLOG) == -1) {
perror("Serveur: listen 2");
return EXIT_FAILURE;
}
//Initialisation du fd_set et du max
FD_ZERO(&readfds);
FD_SET(sockfd2, &readfds);
max = sockfd2;
while(1){
if (select(max+1, &readfds, NULL, NULL, NULL) < 0){
printf("error select\n");
}
int sock = findSender(&readfds, lst);
if(FD_ISSET(sockfd2, &readfds)){
new_fd = accept(sockfd2, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("Serveur: accept");
}
pthread_t thread;
ps_thread->lst = lst;
ps_thread->readfds = &readfds;
ps_thread->max = &max;
ps_thread->socket = new_fd;
ps_thread->mutex = &mutex;
if (pthread_create(&thread, NULL, thread_pseudo, ps_thread)) {
perror("pthread_create");
return EXIT_FAILURE;
}
}
else if(FD_ISSET(sock, &readfds)) {
char* message;
int sock = findSender(&readfds, lst);
if (recv(sock, message, MESSAGE_MAX, 0) == -1) {
perror("Serveur: recv 2: ");
}
}
else{
printf("ERROR\n");
}
}
return EXIT_SUCCESS;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>
#define PORT 5555
#define PSEUDO_MAX 30
#define MESSAGE_MAX 1000
void getPseudo(char* pseudo) {
size_t ln = -1;
while (ln <= 0 || ln > PSEUDO_MAX-1) {
printf("Entrez votre pseudo : ");
fgets(pseudo, PSEUDO_MAX, stdin);
ln = strlen(pseudo) - 1;
}
if (pseudo[ln] == '\n')
pseudo[ln] = '\0';
}
void getMessage(char* message) {
size_t ln = -1;
while (ln <= 0 || ln > MESSAGE_MAX-1) {
printf(">");
fgets(message, MESSAGE_MAX, stdin);
ln = strlen(message) - 1;
}
if (message[ln] == '\n')
message[ln] = '\0';
}
void *thread_message(void *arg)
{
printf("Bonjour\n");
char* message;
message = malloc (sizeof(char) * MESSAGE_MAX);
int* sockfd = (int*)arg;
printf("%d\n", *sockfd);
while (1) {
getMessage(message);
if (send(*sockfd, message, strlen(message), 0) == -1){
perror("Client: send message");
}
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int sockfd, numbytes;
struct sockaddr_in their_addr;
// connector's address information
struct hostent *he;
char buf[MESSAGE_MAX];
long int term1, term2, res;
int demande_pseudo = 0;
char* pseudo;
pseudo = malloc (sizeof(char) * PSEUDO_MAX);
if (argc != 2) {
fprintf(stderr, "Donner le nom du serveur.");
return EXIT_FAILURE;
}
if ((he=gethostbyname(argv[1])) == NULL) {
perror("Client: gethostbyname");
return EXIT_FAILURE;
}
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("Client: socket");
return EXIT_FAILURE;
}
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(PORT);
their_addr.sin_addr = *((struct in_addr*)he->h_addr);
memset(&(their_addr.sin_zero), '\0', 8);
if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
perror("Client: connect");
return EXIT_FAILURE;
}
while (1) {
if ((numbytes=recv(sockfd, buf, MESSAGE_MAX-1, 0)) == -1) {
perror("Client: recv");
return EXIT_FAILURE;
}
if (demande_pseudo == 0) {
buf[numbytes] = '\0';
printf("%s",buf);
getPseudo(pseudo);
printf("Tu as choisi comme pseudo : %s\n", pseudo);
demande_pseudo++;
printf("%d\n", sockfd);
if (send(sockfd, pseudo, strlen(pseudo), 0) == -1){
perror("Client: send pseudo");
return EXIT_FAILURE;
}
pthread_t thread;
if (pthread_create(&thread, NULL, thread_message, &sockfd)) {
perror("pthread_create");
return EXIT_FAILURE;
}
}
else
{
}
}
close(sockfd);
return EXIT_SUCCESS;
}
Thank you so much for your help
First, your question is confusing, try to be more specific giving some examples.
If I understood your question, your problem is that your select is configured to operate on blocking mode.
This is an example on how to set your socket flags to non-blocking mode.
flags = fcntl(sock, F_GETFL, 0);
if(flags < 0)
err(1, "%s: fcntl", __FUNCTION__);
ret = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
if(ret < 0)
err(1, "%s: fcntl:", __FUNCTION__);
Answer Update
Well, one of your problems is that your main function doesn't wait your thread update readfds and max, thus getting blocked on select with the wrong values for those variables. You could solve that by either adding pthread_join(thread, NULL); after you create your thread OR , as I said first, setting your select as non-blocking mode.
With the second method(non-blocking mode) there will be times you
have nothing to process from select so you need to handle this and not crash. I would use that.
Using the first method (pthread_join) you'll not be able to process incoming
requests while waiting for your thread to finish,hence invalidating the whole purpose of having threads: being available to process incoming requests.
Try to develop those ideas and you'll make that code work! (:
I've made a message queue for my fileserver (runs on linux) and it all seems to go well when I upload (from windows client) a file to the the server through the client. Once the file is uploaded though I get all these vague symbols on the serverside.
I've got the following code on the client side
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stddef.h>
#include "mailbox.h"
#define MAXCLIENTS 5
#define PORTNR 5002
#define MAXUSERS 1024
/*
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
void *memset(void *s, int c, size_t n);
int close(int fd);
*/
//Prototyping van de functies
void *childThread(void *ad);
void uploadFile(int nsockfd);
void downloadFile(int nsockfd);
void establishConnection();
int login(char *username, char *password);
int get_queue_ds( int qid, struct msqid_ds *qbuf);
int change_queue_mode(int qid, char *mode);
//Upload files= 0, Download files= 1
int serverState = 0;
int numberLoggedInUsers = 0;
struct sockaddr_in client; // Struct for Server addr
struct message req;
struct msqid_ds mq_id = {0};
int clientUpload;
ssize_t msgLen;
char *username;
char *password;
int main(int argc, char** argv) {
// create message queue key
key_t key;
if((key = ftok("/home/MtFS/Iteraties/ftokDing", 13)) < 0) {
perror("ftok");
exit(1);
}
// create queue, if not succesfull, remove old queue
// and try to make a new one.
while ((clientUpload = msgget(key, 0666 | IPC_CREAT| IPC_EXCL)) < 0) { //| S_IRGRP | S_IWUSR
perror("msgget");
// delete message queue if it exists
if (msgctl(clientUpload, IPC_RMID, &mq_id) == -1) {
perror("msgctl1");
exit(1);
}
}
change_queue_mode(clientUpload, "0666");
/*
if (msgctl(clientUpload, IPC_STAT, &mq_id) == -1) {
perror("msgctl2");
exit(1);
}
if (msgctl(clientUpload, IPC_SET, &mq_id) == -1) {
perror("msgctl3");
exit(1);
}
*/
establishConnection();
return 0;
}
int get_queue_ds(int qid, struct msqid_ds *qbuf) {
if (msgctl(qid, IPC_STAT, qbuf) == -1) {
perror("msgctl IPC_STAT");
exit(1);
}
return 0;
}
int change_queue_mode(int qid, char *mode) {
struct msqid_ds tmpbuf;
/* Retrieve a current copy of the internal data structure */
get_queue_ds(qid, &tmpbuf);
/* Change the permissions using an old trick */
sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);
/* Update the internal data structure */
if (msgctl(qid, IPC_SET, &tmpbuf) == -1) {
perror("msgctl IPC_SET");
exit(1);
}
return (0);
}
void establishConnection() {
pthread_t child; //Thread ID of created thread
int sockfd; //Integer for socket
int nsockfd; //Integer for client socket
socklen_t sizeAddr; //Length of socket
struct sockaddr_in addr; //Struct for client addr
int optValue = 1; //Int for setsockoptions
char ipAdres[32] = "192.168.80.2"; //IP-adres of server
sizeAddr = sizeof (struct sockaddr_in);
// create socket and errorhandling if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("[socket()]");
exit(1);
} else {
printf("================================================================\n\n");
printf("Socket created succesfully.\n\n");
}
// Fill socket with portno and ip address
addr.sin_family = AF_INET; // Protocol Family
addr.sin_port = htons(PORTNR); // Portnumber
inet_aton(ipAdres, &addr.sin_addr); // Local IP- adres
bzero(&(addr.sin_zero), 8); // empty rest of struct
// int setsockopt (int fd, int level, int optname, const void *optval, socklen_t optlen)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof (int)) == -1) {
perror("[setsockopt()]");
exit(1);
}
// Fil socket with portnr and ip adress, also implemented error handling
if (bind(sockfd, (struct sockaddr*) &addr, sizeof (struct sockaddr)) == -1) {
perror("[bind()]");
exit(1);
} else {
printf("================================================================\n\n");
printf("Portnr %d is succesfully connected %s to.\n\n", PORTNR, ipAdres);
}
// Listen to incoming connections and errorhandling
if (listen(sockfd, MAXCLIENTS) == -1) {
perror("[listen()]");
exit(1);
} else {
printf("================================================================\n\n");
printf("Listen to port %d successfull.\n\n", PORTNR);
}
//Connectionloop to process connection requests from clients
while (1) {
//Accept incoming clients with error handling.
if ((nsockfd = accept(sockfd, (struct sockaddr *) &client, &sizeAddr)) == -1) {
perror("[accept()]");
exit(1);
} else {
//create child thread
pthread_create(&child, NULL, childThread, (void *) nsockfd);
/*
// wait untill other child is ready
pthread_join(child, &status);
*/
}
}
}
void *childThread(void *nsockfd) {
int sizeReceivedFile = 0;
char receiveBuffer[PACKETSIZE]; //Buffer voor de ontvangen bestanden
//char sendBuffer[PACKETSIZE]; //Buffer voor de te zenden bestanden
//int sizeSendFile = 0; //Grootte van het te zenden bestand
//char yolocol[PACKETSIZE];
char *clientRequest; //Char pointer voor het type request client, permissie en bestandsnaam
int loginStatus = 0; // 0 = uitgelogd, 1 = ingelogd
char *loggedInAs;
printf("================================================================\n\n");
printf("Connected with a client on IP-Adres: %s.\n\n", inet_ntoa(client.sin_addr));
bzero(receiveBuffer, PACKETSIZE);
while ((sizeReceivedFile = recv((int) nsockfd, receiveBuffer, PACKETSIZE, 0)) > 0) {
// receive from client
printf("Ontvangen buffer: %s\n",receiveBuffer);
if (sizeReceivedFile == 0) {
break;
}
// flags
// retreive flag with strtok
clientRequest = strtok(receiveBuffer, "#");
printf("packet type: %s\n", clientRequest);
// 2 = list
// 3 = download
// 4 = upload
// 5 = login
// 6 = logout
if (strcmp(clientRequest, "2") == 0) {
printf("execute list on current directory!\n");
} else if (strcmp(clientRequest, "3") == 0) {
downloadFile((int) nsockfd);
} else if (strcmp(clientRequest, "4") == 0) {
uploadFile((int) nsockfd);
} else if (strcmp(clientRequest, "5") == 0){
username = strtok(NULL,"#");
password = strtok(NULL,"#");
printf("Username = %s \n password = %s \n",username,password);
int test;
if((test= login(username,password))== 1){
printf("login success, %i\n", test);
loginStatus = 1;
}
else{
printf("Inloggen mislukt, %i\n", test);
loginStatus = 0;
}
} else if (strcmp(clientRequest, "6")== 0) {
loginStatus = 0;
printf("%s logged out\n", loggedInAs);
loggedInAs = "";
}
}
return 0;
}
void uploadFile(int nsockfd) {
/*
printf("execute download!\n");
fileToDownload = strtok(NULL,"#");
printf("%s",fileToDownload);
int sizeReceivedFile = 0;
// if relcv() returns 0 then the connection is gone
while (sizeReceivedFile != 0) {
//Upload of files
if (serverState == 0) {
sizeReceivedFile = recv((int) nsockfd, req.pakket.buffer, PACKETSIZE, 0);
if (sizeReceivedFile < 0) {
perror("[receive()]");
exit(0);
} else if (sizeReceivedFile == 0) {
printf("The client has dropped the connection \n");
close((int) nsockfd);
pthread_exit(NULL);
}
// put the packet in the mailbox
req.mtype = RESP_MT_DATA; // has to be positive
req.pakket.clientID = clientUpload;
if (msgsnd(clientUpload, &req, PACKETSIZE, 0) == -1) {
perror("msgsnd");
}
}
}
req.mtype = RESP_MT_END;
msgsnd(clientUpload, &req, 0, 0);
close((int) nsockfd);
printf("================================================================\n\n");
printf("Connection with client has been lost. Server is waiting for new clients clients...\n\n");
}
void downloadFile(int nsockfd) {
/*
printf("execute download!\n");
fileToDownload = strtok(NULL,"#");
printf("%s",fileToDownload);
*/
char sendBuffer[PACKETSIZE];
int sizeSendFile = 0;
if (send((int) nsockfd, sendBuffer, sizeSendFile, 0) < 0) {
perror("[send()]");
//exit(1);
}
bzero(sendBuffer, PACKETSIZE);
}
And this is the server side. I made one process for handling the connection, which transfers all incoming packets that say 'upload' in one of my custom made protocol flags to a message queue. This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stddef.h>
#include "mailbox.h" // self made header file
#define MAXCLIENTS 5
#define PORT 5002
#define MAXUSERS 1024
//Prototyping
void *childThread(void *ad);
void uploadFile(int nSockfd);
void buildConnection();
int get_queue_ds( int qid, struct msqid_ds *qbuf);
int change_queue_mode(int qid, char *mode);
// Upload files= 0, Download files= 1
int serverState = 0;
struct sockaddr_in client;
struct bericht req;
struct msqid_ds mq_id = {0};
int messageQueue;
ssize_t msgLen;
int main(int argc, char** argv) {
// message queue key aanmaken
key_t key;
if((key = ftok("/home/file", 13)) < 0) {
perror("ftok");
exit(1);
}
// queue aanmaken, als dit niet lukt de eventueel oude queue verwijderen
// en queue opnieuw proberen aan te maken.
while ((messageQueue = msgget(key, 0666 | IPC_CREAT| IPC_EXCL)) < 0) {
perror("msgget");
// message queue verwijderen als deze al bestaat
if (msgctl(messageQueue, IPC_RMID, &mq_id) == -1) {
perror("msgctl1");
exit(1);
}
}
change_queue_mode(messageQueue, "0666");
buildConnection();
return 0;
}
int get_queue_ds(int qid, struct msqid_ds *qbuf) {
if (msgctl(qid, IPC_STAT, qbuf) == -1) {
perror("msgctl IPC_STAT");
exit(1);
}
return 0;
}
int change_queue_mode(int qid, char *mode) {
struct msqid_ds tmpbuf;
// Retrieve a current copy of the internal data structure
get_queue_ds(qid, &tmpbuf);
// Change the permissions using an old trick
sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);
// Update the internal data structure
if (msgctl(qid, IPC_SET, &tmpbuf) == -1) {
perror("msgctl IPC_SET");
exit(1);
}
return (0);
}
void buildConnection() {
pthread_t child;
int sockfd;
int nSockfd;
socklen_t sockaddrSize;
struct sockaddr_in addr;
int optValue = 1;
char ipAdres[32] = "192.168.80.2";
sockaddrSize = sizeof (struct sockaddr_in);
// create socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("[socket()]");
exit(1);
} else {
printf("================================================================\n\n");
printf("Socket is succesfully created.\n\n");
}
// fill dat socket
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
inet_aton(ipAdres, &addr.sin_addr);
bzero(&(addr.sin_zero), 8);
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof (int)) == -1) {
perror("[setsockopt()]");
exit(1);
}
if (bind(sockfd, (struct sockaddr*) &addr, sizeof (struct sockaddr)) == -1) {
perror("[bind()]");
exit(1);
} else {
printf("================================================================\n\n");
printf("bind succesful");
}
if (listen(sockfd, MAXCLIENTS) == -1) {
perror("[listen()]");
exit(1);
} else {
printf("================================================================\n\n");
printf("Listening on port %d\n\n", PORT);
}
// Connection loop
while (1) {
// accept incoming clients
if ((nSockfd = accept(sockfd, (struct sockaddr *) &client, &sockaddrSize)) == -1) {
perror("[accept()]");
exit(1);
} else {
pthread_create(&child, NULL, childThread, (void *) nSockfd);
}
}
}
void *childThread(void *nSockfd) {
int sizeOfRecvFile = 0;
char recvBuffer[PACKETSIZE];
char *clientCommand; // request type
printf("================================================================\n\n");
printf("connected to client with IP: %s.\n\n", inet_ntoa(client.sin_addr));
bzero(recvBuffer, PACKETSIZE);
// get dem datas
while ((sizeOfRecvFile = recv((int) nSockfd, recvBuffer, PACKETSIZE, 0)) > 0) {
if (sizeOfRecvFile == 0) {
break;
}
printf("received buffer: %s\n", recvBuffer);
// handle protocol flag
// chop protocol into pieces to check packet data and flags
clientCommand = strtok(recvBuffer, "#");
printf("packet type: %s\n", clientCommand);
// if clientCommand == 4
// incoming file!
if (strcmp(clientCommand, "4") == 0) {
uploadFile((int) nSockfd);
}
}
return 0;
}
void uploadFile(int nSockfd) {
int sizeOfRecvFile = 0;
// if recv() is 0 close connection
while (sizeOfRecvFile != 0) {
if (serverStaat == 0) {
sizeOfRecvFile = recv((int) nSockfd, req.pakket.buffer, PACKETSIZE, 0);
if (sizeOfRecvFile < 0) {
perror("[receive()]");
exit(0);
} else if (sizeOfRecvFile == 0) {
printf("Client disconnected\n");
close((int) nSockfd);
pthread_exit(NULL);
}
// send packet to message queue
req.mtype = RESP_MT_DATA;
req.pakket.clientID = messageQueue;
if (msgsnd(messageQueue, &req, PACKETSIZE, 0) == -1) {
perror("msgsnd");
}
}
}
req.mtype = RESP_MT_END;
msgsnd(messageQueue, &req, 0, 0);
close((int) nSockfd);
printf("================================================================\n\n");
printf("Disconnected, now waiting for other clients...\n\n");
}
The above program uses a custom made header file:
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <stddef.h>
#include <limits.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#define PACKETSIZE 65535
struct message {
long mtype;
struct packet {
int clientID;
char buffer[PACKETSIZE];
} packet;
};
#define REQ_MSG_SIZE (offsetof(struct message.pakket, buffer) - \
offsetof(struct message.pakket, clientID) - PACKETSIZE)
struct responseMsg { // Responses (server to client)
long mtype; // One of RESP_MT_* values below
char data[PACKETSIZE]; // File content / response message
};
// Types for response messages sent from server to client
#define RESP_MT_FAILURE 1 // File couldn't be opened
#define RESP_MT_DATA 2 // Message contains file data
#define RESP_MT_END 3 // File data complete
I also made a process which writes the uploaded files to the hdd. This process gets data from the message queue that was created in the connection process. The code:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include "/home/CommProces/mailbox.h"
#define POORTNR 5002
#define MAXCLIENTS 5
// prototyping
void writeFiles();
struct message resp;
int uploadMessage;
ssize_t msgLen;
int main () {
key_t key;
if(key = ftok("/home/CommProces/ftokDing", 13) < 0) {
perror("ftok");
exit(1);
}
uploadMessage = msgget(key, 0666);
if (uploadMessage == -1) {
perror("msgget");
exit(1);
}
while(1) {
writeFiles();
}
}
void writeFiles() {
char recvBuffer[PACKETSIZE];
char *rights, *pathname, *filename, *temp;
char *pathOfRecvFile; // received files will go here
FILE* theFile;
bzero(recvBuffer, PACKETSIZE);
int numMsgs, totBytes;
int sizeOfRecvFile = 0;
int nBytesToDisk = 0;
totBytes = msgLen; // Count first message
for (numMsgs = 1; resp.mtype == RESP_MT_DATA; numMsgs++) {
msgLen = msgrcv(uploadMessage, &resp, PACKETSIZE, 0, 0);
if (msgLen == -1) {
perror("msgrcv");
totBytes += msgLen;
}
*recvBuffer = *resp.pakket.buffer;
//temp = strtok(recvBuffer,"#");
rights = strtok(NULL,"#");
if(strcmp(rights, "private") == 0) {
temp = strtok(NULL,"#");
pathname = strcat("/home/MtFS/UploadedFiles/private/", temp);
} else {
pathname = "/home/MtFS/UploadedFiles/public";
}
filename = strcat("/", strtok(NULL,"#"));
pathOfRecvFile = strcat(filename, pathname);
theFile = fopen(pathOfRecvFile, "wb");
if(theFile == NULL) {
printf("[Open_File] unable to create file %s\n", pathOfRecvFile);
} else {
nBytesToDisk = fwrite(recvBuffer, sizeof(char), sizeOfRecvFile, theFile);
if(nBytesToDisk < sizeOfRecvFile) {
perror("fwrite");
}
printf("=============================================================================================================================\n\n");
printf("Files received and placed on HDD\n\n");
bzero(recvBuffer, PACKETSIZE);
}
if (resp.mtype == RESP_MT_FAILURE) {
printf("mtype = fail");
} else if(resp.mtype == RESP_MT_END) {
printf("mtype = end of data");
fclose(theFile);
}
}
}
I've been sifting through breakpoints with the debugger but I can't lay my finger on what's causing the problem :(
For starters, the 3rd parameter to msgrcv() gives the size of the message's payload.
So this line
msgLen = msgrcv(uploadMessage, &resp, PACKETSIZE, 0, 0);
should be
msgLen = msgrcv(uploadMessage, &resp, sizeof(resp)-sizeof(resp.mtype), 0, 0);
or
msgLen = msgrcv(uploadMessage, &resp, sizeof(resp.packet), 0, 0);
Also calling strtok() with the 1st argument set to NULL, does not make sense. It initialy needs to be called with the 1st agrment pointing to some 0 -terminated char-array.
Also^2: Trying to concatenate to a string literal uinsg strcat() like here:
pathname = strcat("/home/MtFS/UploadedFiles/private/", temp);
invokes undefined behaviour.
To fix this make pathname a buffer, instead of a pointer:
char pathname[PATHMAX] = "";
...
if(strcmp(rights, "private") == 0) {
temp = strtok(NULL,"#");
strcpy(pathname, "/home/MtFS/UploadedFiles/private/");
strcat(pathname, temp);
} else {
strcpy(pathname, "/home/MtFS/UploadedFiles/public");
}
The code you posted is describing a non-trivial system, involving a tcp-to-mq proxy (the client) and a mq-server. I strongly advise to debug those compoments separately.