Server-Client Threading Accept error/leak - c

I keep getting a "Accept: too many files" error after running my server.c code. Trying to get it to create new threads for clients and limit connections with semaphores.
This is my code:
#define MAX_CLIENTS 30
sem_t s;
void *handle(void *pnewsock);
int sockfd, new_fd, numbytes; // listen on sock_fd, new connection on new_fd
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
socklen_t sin_size;
pthread_t thread;
int main(void){
//initialise locks
sem_init(&s, 0, 0);
/* generate the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(true);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
/* bind the socket to the end point */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
== -1) {
perror("bind");
exit(true);
}
/* start listnening */
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(true);
}
printf("server starts listening ...\n");
/* Main loop */
while (1) {
sin_size = sizeof(struct sockaddr_in);
if (pthread_create(&thread, NULL, handle, &new_fd) != 0) {
fprintf(stderr, "Failed to create thread\n");
}
}
return 0;
}
void *handle(void *pnewsock){
int value;
sem_getvalue(&s,&value);
while (value >= MAX_CLIENTS){
printf("too many connections");
sem_wait(&s);
}
if (value < MAX_CLIENTS){
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \
&sin_size)) == -1) {
perror("accept");
exit(1);
}
printf("server: got connection from %s\n", \
inet_ntoa(their_addr.sin_addr));
char buffer[MAXDATASIZE];
char res[MAXDATASIZE];
memset(buffer, '\0', MAXDATASIZE);
memset(res, '\0', sizeof(res));
if ((numbytes=recv(new_fd, buffer, sizeof(buffer), 0)) == -1) {
perror("recv");
exit(true);
}
else if(numbytes == 0) {
printf("client left");
sem_post(&s);
close(new_fd);
exit(false);
}
else {
buffer[numbytes] = '\0'; // add null terminator
printf("Request: %s\n",buffer);
//search function
}
}
close(new_fd);
exit(false);
return NULL;
}
Can anyone give me some insight to this file leak? Thanks

The main() function is creating pthreads using an infinite loop. Your error is very likely related to this since the loop will keep on creating newer threads.
/* Main loop */
while (1) {
sin_size = sizeof(struct sockaddr_in);
if (pthread_create(&thread, NULL, handle, &new_fd) != 0) {
fprintf(stderr, "Failed to create thread\n");
}
}
What you probably meant was to create one thread for each client. And if that is the case, then the handle() function should call pthread_create() whenever it gets a new client from the accept() call. The server (the socket for which we call listen and accept) does not need multiple threads -- one thread -- which can be the main thread -- is all it needs.

Related

listen(): invalid argument

Trying to create a server-client application, and I'm having quite a bit of trouble setting up the connection on the server-side. After setting up the socket, and bind()ing the socket, my listen()-call fails with the error message
listen: Invalid argument
which I get from perror()-ing the case where listen() returns -1.
The synopsis of the program is the following: I use getaddrinfo() to generate a linked list of struct addrinfo's, loop through that until I find one that I can successfully create a socket with, then bind() and finally listen().
The listen() call goes as follows:
if ((status = listen(socket_fd, BACKLOG_SIZE)) == -1) {
perror("listen");
close(socket_fd);
freeaddrinfo(res);
exit(EXIT_FAILURE);
}
To be sure, I've printed the values of socket_fd and BACKLOG_SIZE, turning out to be 3 and 5, respectively. Have been debugging for hours now, and I simply cannot find out where the problem lies. Haven't found anyone with the same issue on stackOverflow, either...
Thank you in advance for any help!
Full program:
int main(int argc, char* argv[]) {
int port_no = server_usage(argc, argv);
ready_connection(port_no);
/* Synopsis:
getaddrinfo()
socket()
bind()
listen()
accept()
*/
int socket_fd = setup_socket(NULL, port_no);
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
int new_fd = 0;
// Allow reuse of sockets
int activate=1;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &activate, sizeof(int));
if ((status = bind(socket_fd, res->ai_addr, res->ai_addrlen)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
if ((status = connect(socket_fd, res->ai_addr, res->ai_addrlen)) == -1) {
perror("connect");
close(socket_fd);
freeaddrinfo(res); // free the linked-list
exit(EXIT_FAILURE);
}
if ((status = listen(socket_fd, BACKLOG_SIZE)) == -1) {
perror("listen");
close(socket_fd);
freeaddrinfo(res); // free the linked-list
exit(EXIT_FAILURE);
}
if ((new_fd == accept(socket_fd, (struct sockaddr *)&their_addr, &addr_size)) == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
char buffer[BUFSIZE];
recv(new_fd, buffer, BUFSIZE, 0);
close(socket_fd);
close(new_fd);
freeaddrinfo(res); // free the linked-list
return 0;
}
setup_socket()-function:
int setup_socket(char* hostname, int port_no) {
// hints is mask struct, p is loop variable
struct addrinfo hints, *p;
memset(&hints, 0, sizeof hints); // make sure the struct is empty
// TODO IPv6-support?
hints.ai_family = AF_INET; // only IPv4 supported
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
char port_str[6]; // max port size is 5 digits + 0-byte
memset(port_str, 0, 6);
sprintf(port_str, "%d", port_no);
if ((status = getaddrinfo(hostname, port_str, &hints, &res)) == -1) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(EXIT_FAILURE);
}
int socket_fd = 0;
for (p = res; p != NULL; p = p->ai_next) {
if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
}
if (socket_fd == 0) {
errno = ENOTSOCK;
perror("no socket");
exit(EXIT_FAILURE);
}
return socket_fd;
}
You cannot connect(), then listen() on the same socket. Lose the connect().

Prob with binary files using sockets

This is not the total code.
This is working fine for normal files like text files, but not working for tar.gz and binary files transfer please help me.
And how to send the chunks of memory using sockets.
server.c
void main()
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char buf[16384];
char remotefile[MAXDATASIZE];
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
printf("call binding\n");
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) == -1)
{
perror("bind");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
while(1)
{ // main accept() loop
sin_size = sizeof their_addr;
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
perror("accept");
exit(1);
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
if (!fork())
{ // this is the child process
if ((byt=recv(new_fd, remotefile, MAXDATASIZE-1, 0)) == -1)
{
perror("server recv");
exit(1);
}
int serverfile_fd;
size_t result;
printf("\nremotefile in val1 is %s\n",remotefile);
if((serverfile_fd = open(remotefile,O_RDONLY)) < 0)
{
printf("error at remotefile\n");
exit(1);
}
else
{
read(serverfile_fd, &buf[0], sizeof(buf));
}
//printf("file is\n%s", buf);
/* 3. sending buf in val 0*/
if (send(new_fd, buf, 16384, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
client.c
int remote_to_local(const char *remotehost,const char *remotefile,const char *localfile)
{
int sockfd, numbytes,i = 0,j = 0;
char buf[16384];
struct hostent *he;
struct sockaddr_in s_addr; // connector's address information
printf("\n");
printf("Remotehost is %s\n", remotehost);
if ((he=gethostbyname(remotehost)) == NULL)
{ // get the host info
perror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
s_addr.sin_family = AF_INET; // host byte order
s_addr.sin_port = htons(PORT); // short, network byte order
s_addr.sin_addr = *((struct in_addr *)he->h_addr);
//inet_aton(he->h_addr, &s_addr.sin_addr);
memset(s_addr.sin_zero, '\0', sizeof s_addr.sin_zero);
if (connect(sockfd, (struct sockaddr *)&s_addr, sizeof s_addr) == -1)
{
perror("connect");
exit(1);
}
//send(sockfd, remotefile, MAXDATASIZE-1, 0);
val[0] = 1;
printf("Val 0 is %d\n", val[0]);
printf("Val 1 is %d\n", val[1]);
/*1 sending val in r to l*/
if (send(sockfd, val, MAXDATASIZE-1, 0) == -1)
perror("send");
printf("remotefile is %s\n",remotefile);
/* 2 sending remotefile in r to l*/
if (send(sockfd, remotefile, MAXDATASIZE-1, 0) == -1)
perror("send");
/* 3. recieve buf in r to l */
if ((numbytes=recv(sockfd, buf, 16384, 0)) == -1)
{
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
//printf("Received: \n%s",buf);
int clientfile_fd;
printf("Local file is %s\n",localfile);
if((clientfile_fd = open(localfile,O_CREAT|O_WRONLY,0777)) < 0)
{
printf("error at remotefile\n");
exit(1);
}
else
{
//read(clientfile_fd, &buf[0], sizeof(buf));
int result = strlen(buf);
//printf("Result size is %d\n",result);
open(localfile,O_TRUNC);
write(clientfile_fd, &buf[0], result);
}
close(sockfd);
return 0;
}
Go through ALL your code and fix/change ALL the places where you:
don't correctly handle the results returned by system calls like
recv(). If a positive value is returned, that value is the ONLY safe
way of finding out how much data has been read into the buffer.
Get rid of all the strlen(), printf("%s...) etc. that are either
useless, (the binary data may contain nulls and so the action will
complete early), or dangerous, (binary data contains no nulls at all
and so the calls are UB).
Following logic for receiving a file is already a lot better than what you have. But there are a lot more problems with your code than just this :
FILE *received_file;
received_file = fopen(FILENAME, "w");
...
//copy logic, copies data received from the socket into the file as is.
while (((len = recv(client_socket, buffer, BUFSIZ, 0)) > 0))
{
fwrite(buffer, sizeof(char), len, received_file);
}
fclose(received_file);
close(client_socket);
The receive is continuously called until your receive 0 or a negative number, if you receive 0 that means you need to close the socket because the transfer is finished and the peer has closed its end of the connection too.
The file handle should be created right after accept.
Bottom line is that your code needs a total revision because it is too lengthy for what it is supposed to do, and it is based on totally wrong assumptions. Read first about network programming before attempting anything like this. Socket programming is an advanced topic, without proper understanding you will fail.

Why is pthread_join not returning?

I think pthread_join should always return a value and then allow the main thread to process code after that. In my past experience, this will work. But now I am stuck with it. Somehow it just doesn't return and block the main thread. Or may it is the main thread that executes the task. I don't know why. In the code below, I cannot reach "Thread created2" until I terminate the client. Any idea?
int main(int argc, char *argv[]) {
int sockfd, port; /* listen on sock_fd, new connection on new_fd */
struct sockaddr_in my_addr; /* my address information */
struct sockaddr_in their_addr; /* connector's address information */
socklen_t sin_size;
if(signal(SIGINT, sigintEvent) == SIG_ERR)
printf("can't catch SIGINT!");
/* generate the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if (argc > 1) {
port = atoi(argv[1]);
} else {
port = MYPORT;
}
/* generate the end point */
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(port); /* short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
/* bzero(&(my_addr.sin_zero), 8); ZJL*/ /* zero the rest of the struct */
/* bind the socket to the end point */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
== -1) {
perror("bind");
exit(1);
}
/* start listnening */
if (listen(sockfd, MAXCONNECTIONS) == -1) {
perror("listen");
exit(1);
}
createPool(MAXCONNECTIONS);
/* create a node pointer as head of the list */
head = (node*)malloc(sizeof(node));
openFile();
printf("server starts listnening ...\n");
int new_fd;
sin_size = sizeof(struct sockaddr_in);
while((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size))) {
printf("Accepted!\n");
printf("server: got connection!\n");
//tNode* tThread = (tNode*)threadDequeue();
pthread_t pt;
printf("Got tThread.\n");
if((pthread_create(&pt, NULL, runService,(void*)&new_fd)) != 0) {
printf("error creating thread.");
abort();
}
printf("Thread created.\n");
if( pthread_join(pt, NULL) != 0 ) {
printf("error joining thread");
abort();
}
printf("Thread created2.\n");
}
exit(1);
}
From the documentation we can read the following information about pthread_join
The pthread_join() function waits for the thread specified by
thread
to terminate. If that thread has already terminated, then
pthread_join() returns immediately. The thread specified by thread
must be joinable.
This indicates that in your case parent thread is waiting for the completion of its child thread pt. The child thread pt which is executing the runService is still not returned/completed. Hence your parent thread would keep on waiting for completion( not returning from pthread_join method).
You should try to review the code of runService to understand this situation.

Limiting Clients From Connecting

Trying to limit the amount of client connections in my client-server c application. This is what I have, however it doesn't work. (Doesn't even recognise when max_connections is reached. How can I fix this?
int main(int argc, char *argv[])
{
//fill db
if (obtainDb() == false) {
printf("Database obtain error.\n");
exit(true);
}
//initialise variables
total_connections = 0;
/* generate the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(true);
}
/* generate the end point */
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
/* bind the socket to the end point */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
== -1) {
perror("bind");
exit(true);
}
/* start listnening */
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(true);
}
printf("server starts listnening ...\n");
while(true){
sin_size = sizeof(struct sockaddr_in);
if (total_connections == max_connections) {
printf("Max Number of clients connected!\n");
while(total_connections == max_connections);
}
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \
&sin_size)) == -1) {
perror("accept");
continue;
}
total_connections++;
printf("server: got connection from %s\n", \
inet_ntoa(their_addr.sin_addr));
userInput();
while(waitpid(-1,NULL,WNOHANG)>0);
}
return false;
}
Thanks
Edit: UserInput():
void userInput(void) {
if (!fork()) { // this is the child process
while(true){
char buffer[MAXDATASIZE];
char res[MAXDATASIZE];
memset(buffer, '\0', MAXDATASIZE);
memset(res, '\0', sizeof(res));
if ((numbytes=recv(new_fd, buffer, sizeof(buffer), 0)) == -1) {
perror("recv");
exit(true);
}
if (numbytes == 0) {
printf("client left");
close(new_fd);
total_connections--;
exit(false);
}
buffer[numbytes] = '\0'; // add null terminator
printf("Request: %s\n",buffer);
search(buffer,res);
}
close(new_fd); // parent doesn't need this
exit(false);
}
close(new_fd);
}
When you fork all variables are copied to the new process. That means that the children have their own copy of total_connections.
Instead of using a variable, you should use wait to find out whether any children have exited.
Forking creates a new instance of your proces, which also means that each variable is copied to the new process. Your initial total_connections will actually never get increased beyond 1.
C fork dealing with global variable
A relatively simple option would be to use threads instead of processes for handling multiple clients simultaneously.

How to ensure that server distinguishes two different TCP clients running from two different shells on the same computer?

If I run the client program from two different shells, how should the server be able to determine to whom he is talking to currently?
Platform: Linux, GCC
Client code: This client takes the host name as an argument.
#define SERVERPORT 3490
#define MAXDATASIZE 1000
int main (int argc, char *argv[])
{
int clientSocketFD;
int numOfBytesReceived;
char receivedData[MAXDATASIZE];
struct hostent *objHostent;
struct sockaddr_in serverAddress;
if (argc != 2)
{
fprintf (stderr,"usage: key in client hostname.\n");
exit (1);
}
if ((objHostent = gethostbyname (argv[1])) == NULL)
{
herror ("error: incorrect host name specified.");
exit (1);
}
if ((clientSocketFD = socket (PF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("error: socket system call failed.");
exit (1);
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons (SERVERPORT);
serverAddress.sin_addr = *((struct in_addr *)objHostent->h_addr);
memset (serverAddress.sin_zero, '\0', sizeof serverAddress.sin_zero);
if (connect (clientSocketFD, (struct sockaddr *)&serverAddress, sizeof serverAddress) == -1)
{
perror ("connect() error");
exit (1);
}
while (1)
{
if ((numOfBytesReceived = recv (clientSocketFD, receivedData, MAXDATASIZE-1, 0)) == -1)
{
perror ("\nrecv() error");
}
receivedData [numOfBytesReceived] = '\0';
if (send (clientSocketFD, "Get Lost", 15, 0) == -1)
{
perror ("send() error");
}
}
close (clientSocketFD);
return 0;
}
Server code:
#define SERVERPORT 3490
#define BACKLOG 10
struct threadArguments
{
int threadId;
int listeningSocketDescriptor;
char *message;
};
struct threadArguments threadDataArray [5];
void* genericThreadFunction (void* threadData)
{
struct threadArguments *temp;
temp = (struct threadArguments *) threadData;
printf ("Hello World! It's me, thread #%ld!\n", (long) temp->threadId);
while (1)
{
if (send (temp->listeningSocketDescriptor, temp->message, 14, 0) == -1)
perror ("\nsend() error\n");
char receivedData [14];
int numOfBytesReceived;
if ((numOfBytesReceived = recv (temp->listeningSocketDescriptor, receivedData, 14-1, 0)) == -1)
{
perror ("\nrecv() error\n");
}
else
{
receivedData [numOfBytesReceived] = '\0';
printf ("\nReceived: %s\n", receivedData);
}
}
}
int main (void)
{
int serverSocketFD;
int newServersocketFD;
struct sockaddr_in serverAddress;
struct sockaddr_in clientAddress;
socklen_t sin_size;
if ((serverSocketFD = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("socket");
exit (1);
}
int yes=1;
if (setsockopt (serverSocketFD, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror ("setsockopt");
exit (1);
}
serverAddress.sin_family = AF_INET; // host byte order
serverAddress.sin_port = htons (SERVERPORT); // short, network byte order
serverAddress.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset (serverAddress.sin_zero, '\0', sizeof serverAddress.sin_zero);
if (bind (serverSocketFD, (struct sockaddr *)&serverAddress, sizeof serverAddress) == -1)
{
perror ("bind");
exit (1);
}
if (listen (serverSocketFD, BACKLOG) == -1)
{
perror ("listen");
exit (1);
}
pthread_t threads [5];
int returnValueOfPthreadCreate;
long threadId = -1;
while (1)
{
sin_size = sizeof clientAddress;
if ((newServersocketFD = accept (serverSocketFD, (struct sockaddr *)&clientAddress, &sin_size)) == -1)
{
perror ("accept");
continue;
}
printf ("accept(): got connection from %s\n", inet_ntoa (clientAddress.sin_addr));
threadId++;
threadDataArray [threadId].threadId = threadId;
threadDataArray [threadId].listeningSocketDescriptor = newServersocketFD;
threadDataArray [threadId].message = "Excuse Me, please!";
returnValueOfPthreadCreate = pthread_create (&threads [threadId], NULL, genericThreadFunction, (void*) &threadDataArray[threadId]);
if (returnValueOfPthreadCreate)
{
printf ("ERROR; return code from pthread_create() is %d\n", returnValueOfPthreadCreate);
}
}
pthread_exit (NULL);
return 0;
}
EDIT 1.
Peername returns 0:
if ((newServersocketFD = accept (serverSocketFD, (struct sockaddr *)&clientAddress, &sin_size)) == -1)
{
perror ("accept");
continue;
}
printf ("accept(): got connection from %s\n", inet_ntoa (clientAddress.sin_addr));
printf ("\npeername: %d", getpeername (newServersocketFD, (struct sockaddr *)&clientAddress, &sin_size));
The client's ip-address and (source-)port, fetched as described below, do identify a client(-connection) uniquely.
If you are using address family IF_INET to create the listen()ing socket you can pull the client's ip-address and (the client's connection specific) (source-)port from the variable of type struct sockaddr_in which's reference was passed to accept() by using it's members sin_addr and sin_porteach time accept() returns successfully.
Depending on the platform you are on, you need to change the byte order of such members (see man-page for ntoh() on how to do this). For directly converting the member sin_addr to a character array you are already using a method (inet_ntoa()) taking care of the byte order.
Using getpeername() on the file descriptor which was return by the successful accept() (and, in the context of the server process, is also uniquely identifying the client's connection) shall return the same values for ip-address and port of the peer as the call to accept() did.
You'll have a separate accepted socket (newServersocketFD in your code) per client.
If you use getpeername() on each, you'll find that they use different source ports.

Resources