I am working on a Multi-threaded Server application in C using Pthreads(Linux). My system is dual boot.Windows 7 as well as Ubuntu. I restarted my PC and booted into Windows from Ubuntu, Before restarting my server application was working Fine.After i again booted to ubuntu from windows. and started my server i start getting following Error when a client connects.
recv failed: Bad file descriptor
Here is My code:
Main.C
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> //for socket,AF_INET,SOCK_STREAM
#include <arpa/inet.h> //for sockaddr_in
#include <string.h> //for memset()
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include "extern.h"
#define MAX_CONNECTIONS 5
int main(int argc, char** argv) {
int sock_desc = 0, connfd = 0,listenfd =0;
struct sockaddr_in serv_addr;
int clntSock;
struct sockaddr_in echoClntAddr;
unsigned int clntLen;
char sendBuff[1025];
char recvBuff[10025];
int n = 0;
pthread_t thr;
sock_desc = socket(AF_INET, SOCK_STREAM, 0);
if(sock_desc < 0 )
dieWithError("Unable to open Socket\n");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET ;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(7024);
if(bind(sock_desc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
dieWithError("bind failed\n");
if(listen(sock_desc,MAX_CONNECTIONS) < 0)
dieWithError("listen failed\n");
file = fopen("testServer.txt", "w");
clntSock = sizeof(struct sockaddr);
int i =0;
fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK);
while(1)
{
connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr,(socklen_t*)&clntSock);
if(connfd > 0){
puts("Connection accepted");
if( pthread_create( &thr, NULL , connection_handler , (void*) &connfd) < 0)
{
perror("could not create thread");
return 1;
}
puts("Handler assigned");
}
}
if (connfd < 0)
{
perror("accept failed");
return 1;
}
return (EXIT_SUCCESS);
}
void dieWithError(char *errormsg){
printf("%s", errormsg);
}
HandleConnection.c
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "extern.h"
void *connection_handler(void *socket_desc)
{
int sock = *(int*)socket_desc;
t_data.t_id = sock;
int read_size;
char *message , client_message[2000];
while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
{
client_message[read_size] = '\0';
t_data.msg = client_message;
printf("%s",client_message);
memset(client_message, 0, 2000);
}
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
return 0;
}
I tried by restarting my PC but same problem persists.Please Help.
(The issue you observe has nothing to do with running on a dual-boot machine.)
The code as shown introduces a race by using the address of the same variable for each incoming connection to pass the socket descriptor to the thread function.
The race occures if
accept()
would return faster a second time. To fast for the thread-handler to store what the address it got passed pointed to via executing:In your specfic case with the listening socket set to non-blocking this most likley is the case, when only one connection came in, the handler got the address of
connfd
passed in and in parallelaccept()
gets called again on the (non-blocking) listening socket, no incoming connection is waiting andaccept()
returns immediately and setsconnfd
to -1. This happens faster then the handler for the previous connection could have calledand with this store the
-1
.To observer this effect (not to fix the issue) modify your code as follows:
The handler then would begin like this:
To fix your issue there are two approaches:
Misuse the thread-function's
void*
argument to pass anint
.The handler then would begin like this:
This is a common, but somewhat "hacky" solution, which relies on a pointer being at least as wide as an
int
.Use a seperate instance of an
int
to store the result ofaccept()
.The handler then would begin like this:
This is a clean and portable approach, with the cost of an additional pair of calls to
malloc()
/free()
.Other corrections in the code shown above:
Define the variable whichs address is passed to
accept()
to besocklen_t
as the 3rd parameter toaccept()
is defined assocklen_t *
. If passing a pointer to something else and this "something" is of different size thansocklen_t
accept()
would run into undefined behaviour.Test the outcome of
accept()
against<0
for error as0
is a valid value for a file/socket descriptor.