C pthread doesn't start - c

I'm writing a simple quote server in C and running on Linux. It should pick a random quote from a text file and then start two threads:
the first is in charge of accepting incoming connections and respond with the selected quote,
the second should check once an hour if a day has passed. If it detects that a new day has started it should randomly pick another quote.
My problem is that while the connection thread works fine, the other doesn't even start.
To confirm this I've tried to add a debug print right at the start of the function executed by the thread and another inside it's while loop and none gets printed (those are removed from the code shown here).
I've also added some code to check the pthread_create() return value copied from it's man page but I'm unsure if it's actually working since it doesn't detect any error.
I've tied to first start the "timer thread" first and not start the connection thread at all but still it doesn't get executed. Here follows the relevant code, you can find the full source on GitHub:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <stdbool.h>
#include <pthread.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
pthread_mutex_t quoteLock=PTHREAD_MUTEX_INITIALIZER;
pthread_t checkForNewDayThread, connectionHandlerThread;
void * timer_thread_code(){ //The thread will act as a timer checking every hour if a day has passed
while (true) {
sleep(3600);
if (a_day_has_passed()) {
pthread_mutex_lock(&quoteLock);
QOTD = read_random_quote_from_file(pathToQOTDfile);
pthread_mutex_unlock(&quoteLock);
}
}
}
void * connection_thread_code(int port){ //Code for the thread to handle connections
struct sockaddr_in address;
int server_fd, new_socket, opt = 1, addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// Forcefully attaching socket to the port 1717
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))==-1)
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( port );
// Forcefully attaching socket to the port 1717
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 100) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
printf("Listening on port %i\n", port);
while(1) { //connection handler loop
if ((new_socket = accept(server_fd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&quoteLock);
send(new_socket, QOTD, strlen(QOTD), 0);
pthread_mutex_unlock(&quoteLock);
close(new_socket);
}
}
int main(int argc, char const *argv[])
{
int thread1, thread2, join;
thread1=pthread_create(&connectionHandlerThread, NULL, connection_thread_code(port), NULL);
if(thread1!=0) handle_error_en(thread1, "pthread_create");
thread2=pthread_create(&checkForNewDayThread, NULL, timer_thread_code(), NULL);
if(thread2!=0) handle_error_en(thread2, "pthread_create");
join=pthread_join(connectionHandlerThread, NULL);
if(join!=0) handle_error_en(join, "pthread_join");
return 0;
}
Note: I put only one pthread_join() because both threads should run forever, so to prevent returning from main it should suffice to join only one of the two.
Thanks in advance to anyone who helps me

When you call pthread_create like in your code :
thread1=pthread_create(&connectionHandlerThread, NULL, connection_thread_code(port), NULL);
You actually first call connection_thread_code(port) on the main thread, and pass the return value of that function call as parameter to pthread_create.
That's not what you want to do. You want to pass the function pointer as parameter to pthread_create, as well as the arguments to pass to it :
int port = 1234; /* <-- make sure this is in scope for the duration of the thread ! */
thread1 = pthread_create(&connectionHandlerThread, NULL, connection_thread_code, &port);
/* ... */
pthread_join(connectionHandlerThread, NULL);
For this to work, your thread function should have this signature :
void* connection_thread_code(void* port_ptr);
And get the port like so :
int port = *((int*) port_ptr);
Read up more on passing an int parameter to a thread function here.
Similarly for the second thread (pass the function pointer instead of calling the function, and change the signature).

Related

How can I write from some data from server to all my clients that are connected to my server?

I have a small problem and I don't find any solutions. I have this code.
Server code
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#define PORT 2908
extern int errno;
typedef struct thData{
int idThread;
int cl;
}thData;
static void *treat(void *);
void raspunde(void *);
int main ()
{
struct sockaddr_in server;
struct sockaddr_in from;
int nr;
int sd;
int pid;
pthread_t th[100];
int i=0;
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
return errno;
}
int on=1;
setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
bzero (&server, sizeof (server));
bzero (&from, sizeof (from));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (PORT);
/
if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
{
return errno;
}
if (listen (sd, 2) == -1)
{
perror ("[server]Error at listen().\n");
return errno;
}
while (1)
{
int client;
thData * td;
int length = sizeof (from);
fflush (stdout);
if ( (client = accept (sd, (struct sockaddr *) &from, &length)) < 0)
{
perror ("[server]Error at accept().\n");
continue;
}
td=(struct thData*)malloc(sizeof(struct thData));
td->idThread=i++;
td->cl=client;
pthread_create(&th[i], NULL, &treat, td);
}//while
};
static void *treat(void * arg)
{
struct thData tdL;
tdL= *((struct thData*)arg);
printf ("[thread]- %d - Waiting the message...\n", tdL.idThread);
fflush (stdout);
pthread_detach(pthread_self());
raspunde((struct thData*)arg);
close ((intptr_t)arg);
return(NULL);
};
void raspunde(void *arg)
{
int nr, i=0;
struct thData tdL;
tdL= *((struct thData*)arg);
if (read (tdL.cl, &nr,sizeof(int)) <= 0)
{
printf("[Thread %d]\n",tdL.idThread);
perror ("Error at read() from client.\n");
}
printf ("[Thread %d]The message is...%d\n",tdL.idThread, nr);
nr++;
printf("[Thread %d]Sending the message back...%d\n",tdL.idThread, nr);
printf("%d\n",tdL.cl );
if (write (tdL.cl, &nr, sizeof(int)) <= 0)
{
printf("[Thread %d] ",tdL.idThread);
perror ("[Thread]Error at write() from client.\n");
}
else
printf ("[Thread %d]The message was sent.\n",tdL.idThread);
}
The above code is a server that creates for every client a thread. The client sends a number to the server and the server will answer back to the client with the number incremented by 1.
The question is how can I write for example the nr from the response function or a string message to all my clients that are connected on my server?
Thank you in advance!
As pointed out in the comments, you'll have to keep a list of active threads (protected by a mutex!) to know who is connected to your server.
When a thread is created it would be added to the list and when execution finishes it would remove itself. So you sould make the following changes:
Create a global list of threads and a pthread_mutex_t to protect it (this would happen in main)
Create a function to insert a thread to the list and a function to remove threads. Don't forget to take the mutex before modifying the list and release it after.
Add the insert in the treat function as the first instruction and the add the remove function before close (to prevent sending to a closed connection)
Define a broadcast function that takes a message, loops through the global list and sends the message to each client. Again don't forget to take the mutex while looping through the list and release it when done. You might also want to take into consideration skipping the sender of the broadcast.
The simplest way to implement this would be to use a singly linked list where you always insert at the head (to keep insertion fast) and remove items by their id. The function headers should look like this:
// your linked list
list_t* threadList;
pthread_mutex_t mutex;
void insert_list(thData* th);
// This could also return void, since to know the id you must already have the data that will be returned
thData* remove_list(int id);
// msg can have any type
void broadcast(int msg, int broadcasterId);

Simple echo program using sockets in C echoing incorrect message after the first run

I am trying to learn the basic of network communication using sockets in C. My client program takes in a message from the user, echoes it server side and back, and prints out the received message. When I fire both of them up for the first time, they both work exactly as expected. However, if I quit the client side and then fire it up again while keeping the server program running, my echoed messages become off by one.
I assumed it was because the last message is getting caught in the pipe or something, and after poking around, I saw that someone suggested to use shutdown() to flush out the pipe, but that doesn't seem to be working. I also tried to zero out the buffers wherever I thought they may be lingering, but that didn't seem to help, either.
server.c
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
listening();
//Once first socket has been connected, begin echoing process
int i = 0;
while (1)
{
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
i++;
}
close(serverSocket);
return 0;
}
client.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <resolv.h>
#include <netdb.h>
#define PORT 12403
#define LOCALHOST "127.0.0.1"
#define BUFFER_MAX 1024
int socketStatus = 0;
void sigpipeHandler()
{
perror("Connection to server terminated\n");
socketStatus = 0;
}
int main()
{
int mySocket;
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
int count = 0;
//Create socket
if ((mySocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Get IP address of required host machine
char* hostName = "<host name removed>";
int portNumber = PORT;
char* ipAddr = NULL;
struct hostent* host = NULL;
host = gethostbyname(hostName);
ipAddr = inet_ntoa(*((struct in_addr*) host->h_addr_list[0]));
//Initialize server information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(portNumber);
if (inet_aton(ipAddr, (struct in_addr *)&socketInfo.sin_addr.s_addr) == 0)
{
perror("Error assigning IP address");
exit(errno);
}
//Set up sigpipe handler
signal(SIGPIPE, sigpipeHandler);
//Connect to server
if (connect(mySocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error connecting");
exit(errno);
}
//Indicate that socket is OK
socketStatus = 1;
while(1)
{
if(!socketStatus) {shutdown(mySocket, SHUT_WR); break;}
printf("Please enter a command.\n");
char command[BUFFER_MAX];
bzero(command, BUFFER_MAX);
fgets(command, sizeof(command), stdin);
send(mySocket, command, BUFFER_MAX, 0);
//Get echoed message
bzero(buffer, BUFFER_MAX);
recv(mySocket, buffer, sizeof(buffer), 0);
printf("Echo [%d]:%s\n", ++count, buffer);
}
//Close socket
close(mySocket);
return 0;
}
I did some cleanup on your server code and this seems to work.
For my testing, the client code is unchanged. But, as others have suggested, you should check the error codes from send and recv. Also, note that if you ctrl-c the server, the client will hang in the fgets, so it won't detect the server abort until you hit return after the prompt. Not a big deal, but I thought I'd mention it.
I also added a fork so you can have multiple clients talking to the same server instance simultaneously.
I tested this with two clients [in two xterm windows] talking with the single server instance.
I moved your echo code into a new function docomm. A small difference from your code is that any error from either recv or send breaks out of the loop and closes the connection. All connections from new clients are guaranteed to start with a recv call.
In your code, you would not always break out of the loop, but close the connection and call listening again. This would happen for either send or recv. If it happened on the wrong one, this might be the source of the problem you were having because you could do a send before a recv to a new client initially.
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
int forkflg = 1;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
void
docomm(void)
{
char buffer[BUFFER_MAX];
//Once first socket has been connected, begin echoing process
int i = 0;
while (1) {
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
break;
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
break;
i++;
}
printf("close\n");
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
while (1) {
listening();
if (! forkflg) {
docomm();
continue;
}
pid_t pid = fork();
if (pid == 0) {
docomm();
exit(0);
}
while (waitpid(0,NULL,WNOHANG) > 0);
}
close(serverSocket);
return 0;
}
UPDATE:
Just from a glance: 1) Can I ask why you created a fork flag if you never change the value of it? Should it be changed somewhere?
I used forkflg so you can set it to zero (e.g. int forkflg = 0;) to run sequentially. Or, you could add some code and parse argv looking for an option (e.g. -f) to set/clear it [for testing/debug purposes]. For production code, you'd want forkflg to be set and could remove the flag and just do the fork case always [adjusting the code to match].
Just tracing through the program mentally, it seems like the forking section will never be executed. Correct me where I'm wrong: after initially setting the socket to listen, the while loop will enter, and listening() will be called. Execution will halt in listening() until a connection is accepted.
Yes, that's true.
Control will return to main, where docomm() gets called. Control stays in docomm() until the connection breaks, at which point it returns to main and continue gets called, skipping the fork stuff and starting the process over again. So does the fork stuff ever get executed?
What you're describing is the behavior if forkflg is zero.
The fork is called if forkflg is set. Note that, in that case, docomm is called in the child and not the parent (because fork returned 0). So, the parent will not be blocked while the child does the echoing.
Thus, the parent returns immediately and is free to do the waitpid loop to reap any old children and restart the main/outer loop.
The waitpid loop only happens when a new connection comes in, so several children may have already terminated and will stay in zombie state until the waitpid loop gets executed [which will reap any/multiple pending children].
A cleaner way to reap the children might be to set up a signal handler for SIGCHLD and have it do the waitpid loop. This would reap all spent children immediately, without having to wait for a new connection to roll in.
Or, with the signal handler, add the waitpid loop to listening [inside the current loop] because if a SIGCHLD signal comes in, accept will return immediately with errno set to EINTR

In if-statement poll() block, function triggered more than once per request

I tried to write simple server in C that uses poll() for testing there's an incoming request in listening socket and create a thread to serve request. In if-statement block of testing fds[0].revents & POLLIN one thread created per request, but printf executed more than once per request.
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct sockaddr_in serverAddr, clientAddr;
struct pollfd fd[1];
socklen_t clientlen;
pthread_attr_t pattr;
int serverfd, optval = 1;
void *accreq(void *);
void make_request_thread();
void sig_handler(int);
int main(int argc, char *argv[])
{
signal(SIGINT, sig_handler);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(80);
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr.s_addr);
serverfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
bind(serverfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
listen(serverfd, 5);
clientlen = sizeof(clientAddr);
fd[0].fd = serverfd;
fd[0].events = POLLIN;
pthread_attr_init(&pattr);
pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
while(1)
{
if(poll(fd, 1, -1) > 0)
{
if(fd[0].revents & POLLIN)
{
printf("Hello!\n"); /* Why this function executed more than once per incoming request*/
make_request_thread();
}
}
}//end while loop
return 0;
}
void *accreq(void *arg)
{
int saccfd = accept(serverfd, (struct sockaddr *)&clientAddr, &clientlen), port, rc, wc;
char buffer[2048], addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
port = ntohs(clientAddr.sin_port);
printf("[Accept request from %s:%i]\n", addr, port);
rc = read(saccfd, buffer, sizeof(buffer));
printf("%s\n", buffer);
wc = write(saccfd, buffer, strlen(buffer));
close(saccfd);
pthread_exit(0);
}
void make_request_thread()
{
pthread_t thread_acc;
pthread_create(&thread_acc, &pattr, accreq, NULL);
}
void sig_handler(int signo)
{
printf("\nCatch signal interrupt\nExiting...\n");
pthread_attr_destroy(&pattr);
close(serverfd);
exit(0);
}
You have a race condition. The race is between your main thread which will call poll() again on the accepting socket, and the spawned thread that will call accept() on that socket. If the spawned thread that calls accept() wins, then the main thread will block (if there is only one incoming connection). If the main thread wins, the poll() call will return immediately, since the socket still has a pending connection waiting to be accepted.
In your code, you don't need to use poll() at all. You can simply block on a call to accept() and and give the spawned thread the newly created socket to process. If you really want poll(), the easiest fix is to call accept() in the main thread after poll() wakes up, and give the newly created socket to the spawned thread.

Simple TCP-Server

This is a basic TCP-Server implementation for teaching purposes. Are there any error or improvements to do. Any suggest is welcome!
I only have a doubt:
signal(SIGCHLD, SIG_IGN);
Is that call used to prevent zoombie-child processes?
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BACKLOG 5
#define MAXSIZE 1024 //max-bytes for read-buffer
void main(){
int sock_ds, ret, length;
int acc_ds; //Accept socket descriptor
struct sockaddr_in addr; //this addres
struct sockaddr rem_addr; //remote address (generic)
char buff[MAXSIZE];
sock_ds = socket(AF_INET, SOCK_STREAM, 0); // => TCP
bzero((char *)&addr, sizeof(addr)); //reset struct
addr.sin_family = AF_INET;
addr.sin_port = htons(25000);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sock_ds, &addr, sizeof(addr));
if(ret == -1){
perror("Binding error");
exit(1);
}
ret = listen(sock_ds, BACKLOG); // backlog queue
if(ret == -1){
perror("Listen error");
exit(1);
}
length = sizeof(rem_addr);
signal(SIGCHLD, SIG_IGN); //zombie children management
/*Busy-waiting (server) and concurrency */
while(1){
/*Repeat until success*/
while(acc_ds = accept(sock_ds, &rem_addr, &length) == -1){
if(fork() == 0){ //child-process
close(sock_ds); //unused from child
do{
read(acc_ds, buff, MAXSIZE);
printf("Message from remote host:&s\n", buff);
}while(strcmp(buff, "quit") == 0);
/*Transimission completed: server response */
write(acc_ds, "Reading Done", 10);
close(acc_ds); //socket closed
exit(0); //exiting from child
}
else{
close(acc_ds); //unused from parent
}
}
}
}
Return type of main is not int. It should be. Either return EXIT_SUCCESS or EXIT_FAILURE.
Result of socket() call is not checked. It should be, or bind will fail but perror() will tell "Invalid argument" instead of the actual error.
A return value of read() is not checked possibly triggering undefined behavior when printing.
There is no &s format specified, it should be %s.
%s expects a null-terminated string. This is not guaranteed by the code (see point #3). strcmp() may crap out as well.
As for the SIGCHLD, #cnicutar has kindly answered that already, nothing to add there.
Hope it helps. Good Luck!
Yes, that's exactly what ignoring SIGCHLD is for. From TLPI:
There is a further possibility for dealing with dead child processes.
Explicitly setting the disposition of SIGCHLD to SIG_IGN causes any
child process that subsequently terminates to be immediately removed
from the system instead of being converted into a zombie.
It is standard across Unix implementations.
I need to learn how this works, too. So, I googled "simple tcp-server", found this little program, and fixed up the code to be happier with gcc -Wall and the comments. here is what I put together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#define BACKLOG 5
#define MAXSIZE 1024 //max-bytes for read-buffer
#define PORT 25000
/****************************************************************/
int main() {
int sock_ds, ret; unsigned int length;
int acc_ds; //Accept socket descriptor
struct sockaddr_in addr; //this address
struct sockaddr rem_addr; //remote address (generic)
char buff[MAXSIZE+1];
if (!(sock_ds = socket(AF_INET, SOCK_STREAM, 0))) perror("socket call failed"); // => TCP
bzero( (char *)&addr, sizeof(addr)); //reset struct
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sock_ds, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1) {
perror("Binding error");
exit(EXIT_FAILURE);
}
ret = listen(sock_ds, BACKLOG); // backlog queue
if (ret == (-1)) {
perror("Listen error");
exit(EXIT_FAILURE);
}
length = sizeof (rem_addr);
// sigaction(SIGCHLD, SA_NOCLDWAIT); //zombie children management
/*Busy-waiting (server) and concurrency */
while (1) {
fprintf(stderr, "[Waiting for client %d]\n", getpid());
/*Repeat until success*/
while ((acc_ds = accept(sock_ds, &rem_addr, &length)) == -1) {
fprintf(stderr, "[Accepted from client %d]\n", getpid());
if (fork() == 0) { //child-process
close(sock_ds); //unused from child
fprintf(stderr, "[Reading from client %d]\n", getpid());
while (read(acc_ds, buff, MAXSIZE)) {
buff[MAXSIZE]='\0';
printf("Message from remote host:%s\n", buff);
fflush(stdout);
if (strncmp (buff, "quit", 5) == 0) break;
}
/*Transmission completed: server response */
if (write(acc_ds, "Reading Done", 10)) fprintf(stderr, "failed write\n");
close(acc_ds); //socket closed
exit(EXIT_SUCCESS); //exiting from child
}
else{
close(acc_ds); //unused from parent
}
}
}
return EXIT_SUCCESS;
}
two problems. the first is that gnu linux gcc refuses to accept
sigaction(SIGCHLD, SA_NOCLDWAIT); //zombie children management
tcp-server2.c: In function ‘main’:
tcp-server2.c:53:3: warning: passing argument 2 of ‘sigaction’ makes pointer from integer without a cast [enabled by default]
In file included from tcp-server2.c:7:0:
/usr/include/signal.h:267:12: note: expected ‘const struct sigaction * restrict’ but argument is of type ‘int’
tcp-server2.c:53:3: error: too few arguments to function ‘sigaction’
In file included from tcp-server2.c:7:0:
/usr/include/signal.h:267:12: note: declared here
the second is that when I telnet to port 25000 and type a few things, nothing ever is echoed as received. so the server does not seem to work. it never gets to accepted from client.
now, I could pick up a programming example from somewhere else, but I thought I should just report this here, so we get a simplest tcp server posted correct here.

accept returns existing connection, causing seg fault

I am creating a server daemon in c that accepts numerous simultaneous connections, and the clients will be sending data to the server. I currently have each client connection being spawned into a new thread. I am seeing that accept() will sometimes (not always) return the ID of existing connection which (obviously) causes a wide variety of issues, including segmentation faults.
I even turned off the socket option SO_REUSEADDR to make sure that wasn't the case. Whenever a single client makes numerous consecutive calls, everything is fine (conid in my code below increments - 5,6,7,8,9, etc...). But whenever more than one client ties to simultaneously connect, sometimes conid gets duplicated (an example from one run: 5,6,7,7,8,9,10,10,10,11,12,12, ...).
I'm wondering how accept() can return an existing connection?? It would make sense if I was calling accept() within more than one thread, but as you can see below it only exists in the main process thread. On the other hand, I never experienced this issue with select(), so maybe it is an issue with threading??? At this point, I've tried just about everything I can think of, but it's apparent to me I'm just missing something
Edit: edited code to show that mystruct wasn't being free'd in the while loop, and (hopefully) provide more insight.
Edit #2: per request, I have posted the full source of my example.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdarg.h>
#include <time.h>
#include <errno.h>
#include <netdb.h>
//this is my test structure
struct mystruct_ {
int id; //only id for testing
};
typedef struct mystruct_ mystruct;
//error logging function
void merr(const char *msg, ...) {
//get the time
time_t t;
time(&t);
//grab this function's arguments
va_list args;
char buf[BUFSIZ];
va_start(args,msg);
//build the message
vsprintf(buf,msg,args);
//output the message
printf(" ERROR :: %s\n",buf);
//that's it!
va_end(args);
}
//this function handles the threads
void *ThreadedFunction(void *arg) {
//get the passed structure
mystruct *test = (mystruct *)arg;
//print conid -- this is where I am seeing the duplicates
printf("my connection id is %d\n",test->id);
// do some stuff, like: pull vars out of mystruct
int nbytes;
char buf[256];
while(1) {
if((nbytes=recv(test->id, buf, sizeof buf, 0)) <= 0) {
//handle break in connection
close(test->id);
} else {
//for this example, just print out data from client to make my point
buf[nbytes] = 0;
printf("%s",buf);
}
}
}
//main just sets up the connections and creates threads
int main(int argc, char *argv[])
{
char *port = "1234";
//get ready for connection
struct sockaddr_storage addr;
socklen_t addrsize = sizeof addr;
struct addrinfo hints, *res, *ai, *p;
int sockfd, conid, rv;
int yes = 1;
//
//load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
if((rv = getaddrinfo(NULL, port, &hints, &ai))!= 0) {
merr("failed to bind port '%s': %s\n",port,gai_strerror(rv));
exit(1);
}
//
//bind the port
for(p=ai; p!=NULL; p=p->ai_next) {
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockfd<0) continue;
//setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //commented for testing
if(bind(sockfd,p->ai_addr,p->ai_addrlen)<0) { close(sockfd); continue; }
break;
}
//if we don't have p, it means server didn't get bound
if(p==NULL) { merr("failed to bind port '%s' (reason unknown)",port); exit(2); }
freeaddrinfo(ai); //all done with this
//
// listen to the (now bounded) socket:
if(listen(sockfd,10)==-1) { merr("listen; errmsg: \"%s\"",strerror(errno)); exit(3); }
// bind(), listen(), etc... blah blah blah
mystruct test[1024]; //just for testing
printf("Ready and Listening...\n");
while(1) {
conid = accept(sockfd, (struct sockaddr *)&addr, &addrsize);//get a connection
test[conid].id = conid;
pthread_t p;
pthread_create(&p,NULL,ThreadedFunction,&test[conid]); //create new thread
}
}
This is broken:
while(1) {
conid = accept(sockfd, (struct sockaddr *)&addr, &addrsize);//get a connection
test[conid].id = conid;
pthread_t p;
pthread_create(&p,NULL,ThreadedFunction,&test[conid]); //create new thread
}
pthread_t p; declares an opaque handle on the stack which pthread_create will fill in. That handle's lifetime must last until you call pthread_join or pthread_detach.
In this case, the storage for that pthread_t is probably being reused, messing up the passing of the argument to the thread function. At least, that is my guess.
Try calling pthread_detach after pthread_create.
accept returns a file descriptor that my be reused. As your ThreadedFunction never terminates when done with a file descriptor you will get a race condition. So after the close statement put return;

Resources