i have a function like this :
void create_serv_and_init_client(client_t *cl, serv_t *serv)
{
static int i = 0;
pthread_t thread_serv;
if (i == 0) {
*serv = create_serv_socket();
if (pthread_create(&thread_serv, NULL, waiting_connection, \
(void *)serv) < 0) {
perror("could not create thread");
exit(1);
}
pthread_join(thread_serv, NULL);
cl[0] = create_client(0);
printf("OK\n");
i++;
}
}
waiting_connection function :
void *waiting_connection(void *server)
{
serv_t *serv = (serv_t *)server;
serv->newSocket = accept(serv->sockfd, (struct sockaddr*)&serv->newAddr, \
&serv->addr_size);
if (serv->newSocket < 0) {
exit(1);
}
if ((serv->childpid = fork()) == 0) {
close(serv->sockfd);
while (recv(serv->newSocket, serv->buffer, 1024, 0) != 0) {
printf("Client: %s\n", serv->buffer);
send(serv->newSocket, serv->buffer, strlen(serv->buffer), 0);
bzero(serv->buffer, sizeof(serv->buffer));
}
}
}
if i dont pthread_join, i will never receive the sended msg by the client, but however, it will block my program until receiving the message, but i wanna have a unblockant waiting of the client message, so it is possible to do a unblockant waiting, for the reception of the client message ?
because this is a game, so the server is launched when the first client is connected to the game, and if my program always wait others input of others plays, the actual players connected can't play, so this need to be unblockant, asynchronous if you want.
So you want one thread per client connection, and to be able to always accept a new client connection. That's sounds about right.
In that case the management of the messages received from a given client must be done in the corresponding thread, not in the main thread. The main thread only manages the client connections and launch the new threads, it does not have to join the other threads.
So the accept are done in the main thread, not in the separated threads, and it gives the socket with the new client in argument when it launch the new thread for that client, then pthread_detach the new thread.
if ((serv->childpid = fork()) == 0) {
close(serv->sockfd);
all of that does not exist, there is no fork, you use threads.
Of course an other way is to not use threads at all but to fork, anyway the roles are unchanged between the initial process and the child processes.
Your problem is that you didn't assign the right roles to everyone.
Related
I'm having trouble terminating my server in my multithreaded program (one server, multiple clients).
When the variable global_var, which counts the number of currently connected clients, gets set to 0, the server should terminate, but it doesn't.
What I think is happening is since accept() is blocking , the code never reaches the break condition in main loop.
It's breaking correctly out of thread_func but then it blocks inside the while loop, just before the accept() call and after printing "Exiting thread_func".
volatile int finished = 0; // Gets set to 1 by catching SIGINT/SIGSTOP
int global_var = 0; // When it gets to 0, server should terminate
int server_fd;
void * thread_func(void* arg)
{
do_some_pre_stuff();
while(1)
{
if(!global_var)
{
close(server_fd);
finished = 1;
break;
}
if(recv(...) > 0)
{
do_more_stuff()
}
else
{
disconnect_client();
global_var--;
break;
}
}
free_more_ressources();
return NULL;
}
int main()
{
do_initial_stuff();
init_socket();
listen();
while (!finished)
{
if( (fd = accept(server_fd,...)) == -1)
exit(-1);
global_var++;
/* Some intermediate code */
if(!global_var)
break;
// Thread for the newly connected player
if(pthread_create(&thread_id[...], NULL, thread_func, (void*)some_arg)
exit(-1);
}
free_resources();
puts("Exiting thread_func");
}
I tried the advice listed here without success (except the pipe answer, not trying to mess with pipes).
I'm new to socket programming but what I tried so far looked correct but none of the solutions worked (including semaphores, pthread_cancel,etc)
PS: synchronization has been implemented, just omitted here for readability
There's something going on in my code that I didn't intend.
I've got a program with several threads. One thread reads a rfid chip and saves the rfid code string into a global struct variable. There are also some other threads that are opened when a client, which is supposed to get the string, connects to the program on a certain socket. They are on a timed waiting (pthread_cond_timedwait) for the reading thread to broadcast a pthread condition variable (thread->done) that signals that a rfid chip was read and there is a new string to be sent to the clients. Now to the problem: the pthread_cond_timedwait in the sending thread shall only be satisfied when a new string is ready to be sent, but it also triggers when a new client thread is opened. The client threads start with the function below. I hope I explained my problem clearly and anyone can help me with the problem.
code of the sending function:
void* send_data(void* arg)
{
int client_id;
int status_timeout;
struct timespec timeout;
struct Thread_parameter *thread = (struct Thread_parameter*)arg;
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&thread->done, &attr);
// read the client id from struct
pthread_mutex_lock(&thread->mutex);
client_id = thread->t_client_id;
pthread_mutex_unlock(&thread->mutex);
while (1)
{
// wait for string
while (1)
{
pthread_mutex_lock(&thread->mutex);
status_timeout = ETIMEDOUT;
while (1)
{
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec += 1;
status_timeout = pthread_cond_timedwait(&thread->done, &thread->mutex, &timeout);
// Timeout
if (status_timeout == ETIMEDOUT)
{
if (is_connected(client_id))
continue;
else
{
log_output("Lost connection to client '%i'", client_id);
pthread_mutex_unlock(&thread->mutex);
pthread_exit(NULL);
}
}
else if (status_timeout == 0)
{
// condition satisfied
break;
}
}
pthread_mutex_unlock(&thread->mutex);
break;
}
// send the string to the client when the condition is satisfied
while (1)
{
pthread_mutex_lock(&thread->mutex);
if (send(client_id, thread->t_chip_id, CHIP_ID_LENGTH, MSG_NOSIGNAL) >= 0)
{
pthread_mutex_unlock(&thread->mutex);
log_output("Successfully sent data to client '%i'", client_id);
break;
}
else
{
pthread_mutex_unlock(&thread->mutex);
// sending not successful, check if client is still connected
if (is_connected(client_id))
continue;
else
{
close(client_id);
log_output("Lost connection to client '%i'", client_id);
pthread_exit(NULL);
}
}
}
}
}
I am facing some trouble dealing with zombie processes. I wrote a simple server which creates tic tac toe matches between players. I am using select() to multiplex between multiple connected clients. Whenever there are two clients, the server will fork another process which execs a match arbiter program.
The problem is that select() blocks. So therefore, say if there is a match arbiter program running as a child process and it exits, the parent will never wait for the child if there are no incoming connections because select() is blocking.
I have my code here, apologies since it is quite messy.
while(1) {
if (terminate)
terminate_program();
FD_ZERO(&rset);
FD_SET(tcp_listenfd, &rset);
FD_SET(udpfd, &rset);
maxfd = max(tcp_listenfd, udpfd);
/* add child connections to set */
for (i = 0; i < MAXCLIENTS; i++) {
sd = tcp_confd_lst[i];
if (sd > 0)
FD_SET(sd, &rset);
if (sd > maxfd)
maxfd = sd;
}
/* Here select blocks */
if ((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0) {
if (errno == EINTR)
continue;
else
perror("select error");
}
/* Handles incoming TCP connections */
if (FD_ISSET(tcp_listenfd, &rset)) {
len = sizeof(cliaddr);
if ((new_confd = accept(tcp_listenfd, (struct sockaddr *) &cliaddr, &len)) < 0) {
perror("accept");
exit(1);
}
/* Send connection message asking for handle */
writen(new_confd, handle_msg, strlen(handle_msg));
/* adds new_confd to array of connected fd's */
for (i = 0; i < MAXCLIENTS; i++) {
if (tcp_confd_lst[i] == 0) {
tcp_confd_lst[i] = new_confd;
break;
}
}
}
/* Handles incoming UDP connections */
if (FD_ISSET(udpfd, &rset)) {
}
/* Handles receiving client handles */
/* If client disconnects without entering their handle, their values in the arrays will be set to 0 and can be reused. */
for (i = 0; i < MAXCLIENTS; i++) {
sd = tcp_confd_lst[i];
if (FD_ISSET(sd, &rset)) {
if ((valread = read(sd, confd_handle, MAXHANDLESZ)) == 0) {
printf("Someone disconnected: %s\n", usr_handles[i]);
close(sd);
tcp_confd_lst[i] = 0;
usr_in_game[i] = 0;
} else {
confd_handle[valread] = '\0';
printf("%s\n", confd_handle); /* For testing */
fflush(stdout);
strncpy(usr_handles[i], confd_handle, sizeof(usr_handles[i]));
for (j = i - 1; j >= 0; j--) {
if (tcp_confd_lst[j] != 0 && usr_in_game[j] == 0) {
usr_in_game[i] = 1; usr_in_game[j] = 1;
if ((child_pid = fork()) == 0) {
close(tcp_listenfd);
snprintf(fd_args[0], sizeof(fd_args[0]), "%d", tcp_confd_lst[i]);
snprintf(fd_args[1], sizeof(fd_args[1]), "%d", tcp_confd_lst[j]);
execl("nim_match_server", "nim_match_server", usr_handles[i], fd_args[0], usr_handles[j], fd_args[1], (char *) 0);
}
close(tcp_confd_lst[i]); close(tcp_confd_lst[j]);
tcp_confd_lst[i] = 0; tcp_confd_lst[j] = 0;
usr_in_game[i] = 0; usr_in_game[j] = 0;
}
}
}
}
}
}
Is there a method which allows wait to run even when select() is blocking? Preferably without signal handling since they are asynchronous.
EDIT: Actually, I found out that select has a timeval data structure which we can specify the timeout. Would using that be a good idea?
I think your options are:
Save all your child descriptors in a global array and call wait() from a signal handler. If you don't need the exit status of your children in your main loop, I think this is the easiest.
Instead of select, use pselect -- it will return upon receiving a specified (set of) signal(s), in your case, SIGCHLD. Then call wait/WNOHANG on all child PIDs. You will need to block/unblock SIGCHLD at the right moments before/after pselect(), see here: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html
Wait on/cleanup child PIDs from a secondary thread. I think this is the most complicated solution (re. synchronization between threads), but since you asked, it's technically possible.
If you just want to prevent zombie processes, you could set up a SIGCHLD signal handler. If you want to actually wait for the return status, you could write bytes into a pipe (non-blocking, just in case) from the signal handler and then read those bytes in the select loop.
For how to handle SIGCHLD, see http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html -- you want to do something like while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
Perhaps the best approach is sending a single byte from the SIGCHLD signal handler to the main select loop (non-blocking, just in case) and doing the waitpid loop in the select loop when bytes can be read from the pipe.
You could also use a signalfd file descriptor to read the SIGCHLD signal, although that works only on Linux.
I'm trying to write an IRC type chat client which has clients that can connected to a server. I'm trying to get it to work locally atm (Using FIFOS instead of sockets).
I've run into the following issue which I can't seem to solve:
After accepting a new client connection, I want to create a new thread for that client (on the server) that'll handle inputs from that client.
To do this I have the following piece of code (the full code is at the bottom):
while(1) {
.
.
.
if (pthread_create(&thread, NULL, client_handler, &new_client) != 0)
printf("Couldn't create a thread to listen to the client.... Not ok \n");
}
This works fine with 1 connected client.
The moment I try to connect another client it seems the previous thread that executed the method client_handler stopped running.
I know this because the server stops accepting input from that client, but the new thread works just fine (the one that handles the newly connected client).
I was wondering if my methodology was wrong or if I'm not using the pthread_create correctly.
Has anyone got any suggestions?
void server_listen() {
Client new_client;
ClientNode temp;
buffint client_name_length;
char client_name[CLIENT_NAME_SIZE];
char fifo_in[FIFO_NAME_SIZE], fifo_out[FIFO_NAME_SIZE];
buffint client_pid;
char ack[4] = "/ack";
char inuse[6] = "/inuse";
pthread_t thread;
buffint length;
ClientNode it;
buffint message_length;
char message[MESSAGE_LENGTH];
pthread_mutexattr_t attr;
while (1) {
memset(client_name, 0, CLIENT_NAME_SIZE);
client_name_length.data =0;
if (read_helper(irc_server.server_fifo, client_name_length.buff,
sizeof(int)) == -1)
return; /* error */
if (read_helper(irc_server.server_fifo, client_pid.buff, sizeof(int))
== -1)
return; /* error */
if (read_helper(irc_server.server_fifo, client_name, client_name_length.data) == -1)
return; /* error */
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK_NP);
pthread_mutex_init(&new_client.fifo_in_lock, &attr);
printf("Reading from a new client, with name: %s\n", client_name);
new_client.pid = client_pid.data;
strncpy(new_client.name, client_name, client_name_length.data);
new_client.name_length = client_name_length.data;
sprintf(fifo_in, "fifo-%d-in", client_pid.data);
sprintf(fifo_out, "fifo-%d-out", client_pid.data);
new_client.fifo_in = open(fifo_in, O_WRONLY);
if (new_client.fifo_in == -1)
return; /* error */
new_client.fifo_out = open(fifo_out, O_RDONLY);
if (new_client.fifo_out == -1)
return; /* error */
read_lock();
temp = client_exists_by_name(&irc_server.clients, client_name, client_name_length.data);
read_unlock();
if (temp != NULL) {
pthread_mutex_lock(&new_client.fifo_in_lock);
length.data = 6;
if (write_helper(new_client.fifo_in, length.buff, sizeof(int))
== -1) {
//TODO: What do we do if writing to the fifo_out failed?
printf( "Writing to the fifo-out failed for some unknown reason \n");
return;
}
if (write_helper(new_client.fifo_in, inuse, length.data) == -1) {
//TODO: What do we do if writing to the fifo_out failed?
printf( "Writing to the fifo-out failed for some unknown reason \n");
return;
}
pthread_mutex_unlock(&new_client.fifo_in_lock);
continue;
}
write_lock();
insert_node(&irc_server.clients, new_client);
write_unlock();
length.data = 4;
pthread_mutex_lock(&new_client.fifo_in_lock);
if (write_helper(new_client.fifo_in, length.buff, sizeof(int)) == -1) {
//TODO: What do we do if writing to the fifo_out failed?
printf("Writing to the fifo-out failed for some unknown reason \n");
return;
}
if (write_helper(new_client.fifo_in, ack, length.data) == -1) {
//TODO: What do we do if writing to the fifo_out failed?
printf("Writing to the fifo-out failed for some unknown reason \n");
return;
}
pthread_mutex_unlock(&new_client.fifo_in_lock);
foreach(it, irc_server.clients){
pthread_mutex_lock(&it->client.fifo_in_lock);
strncpy(message, new_client.name, new_client.name_length);
strncat(message, " joined the chat", sizeof(" joined the chat"));
message_length.data = sizeof(" joined the chat") + new_client.name_length;
if (write_helper(it->client.fifo_in, message_length.buff, sizeof(int)) == -1) {
//TODO: What do we do if writing to the fifo_out failed?
printf("writing to the fifo_in a public message ERROR1 \n");
return;
}
if (write_helper(it->client.fifo_in, message, message_length.data) == -1) {
//TODO: What do we do if writing to the fifo_out failed?
printf("writing to the fifo_in a public message ERROR2 \n");
return;
}
pthread_mutex_unlock(&it->client.fifo_in_lock);
memset(message, 0, MESSAGE_LENGTH);
message_length.data = 0;
}
if (pthread_create(&thread, NULL, client_handler, &new_client) != 0)
printf("Couldn't create a thread to listen to the client.... Not ok \n");
if (pthread_create(&thread, NULL,client_handler1 ,&new_client ) != 0)
printf("Couldn't create a thread to listen to the client.... Not ok \n");
print_clients();
}
}
It looks like you're sharing a single instance of new_client between all the threads in the server. A call to pthread_create() doesn't magically copy new_client. So every thread you create is using the same new_client. So when your master thread fills in values for a second client, the thread handling the first client tries to use those too.
Allocate a new new_client for each client, fill in the values and pass that into pthread_create(). You'll also need a per-client variable for the first parameter in pthread_create().
Other things - you're seemingly passing raw binary data between your client and server, things like string length integers. That kind of thing is going to cause you a whole pile of woe as soon as you have to start doing clients for different OSes. I strongly recommend you adopt a serialisation technology, preferably ASN.1 (not free but really robust) or Google Protocol Buffers (free but not as rich or robust).
if (pthread_create(&thread, NULL,client_handler ,&new_client ) != 0)
...
if (pthread_create(&thread, NULL,client_handler1 ,&new_client ) != 0)
Why you are using same pthread_t variable every time? You can't use same thread variable. Prefer to use an array of pthread_t like this:
pthread_t thread[2];
if (pthread_create(&thread[0], NULL, client_handler, &new_client ) != 0)
...
if (pthread_create(&thread[1], NULL, client_handler1, &new_client ) != 0)
I'm writing a web server in C (which I suck with) using Pthreads (which I suck with even more) and I'm stuck at this point. The model for the server is boss-worker so the boss thread instantiates all worker threads at the beginning of the program. There is a global queue that stores the socket of the incoming connection(s). The boss thread is the one that adds all items (sockets) to the queue as the connections are accepted. All of the worker threads then wait for an item to be added to a global queue in order for them to take up the processing.
The server works fine as long as I connect to it less times than the number of worker threads that the server has. Because of that, I think that either something is wrong with my mutexes (maybe the signals are getting lost?) or the threads are being disabled after they run once (which would explain why if there are 8 threads, it can only parse the first 8 http requests).
Here is my global queue variable.
int queue[QUEUE_SIZE];
This is the main thread. It creates a queue struct (defined elsewhere) with methods enqueue, dequeue, empty, etc. When the server accepts a connection, it enqueues the socket that the incoming connection is on. The worker threads which were dispatched at the beginning are constantly checking this queue to see if any jobs have been added, and if there are jobs, then they dequeue the socket, connect to that port, and read/parse/write the incoming http request.
int main(int argc, char* argv[])
{
int hSocket, hServerSocket; /* handle to socket */
struct hostent* pHostInfo; /* holds info about a machine */
struct sockaddr_in Address; /* Internet socket address stuct */
int nAddressSize = sizeof(struct sockaddr_in);
int nHostPort;
int numThreads;
int i;
init(&head,&tail);
//**********************************************
//ALL OF THIS JUST SETS UP SERVER (ADDR STRUCT,PORT,HOST INFO, ETC)
if(argc < 3) {
printf("\nserver-usage port-num num-thread\n");
return 0;
}
else {
nHostPort=atoi(argv[1]);
numThreads=atoi(argv[2]);
}
printf("\nStarting server");
printf("\nMaking socket");
/* make a socket */
hServerSocket=socket(AF_INET,SOCK_STREAM,0);
if(hServerSocket == SOCKET_ERROR)
{
printf("\nCould not make a socket\n");
return 0;
}
/* fill address struct */
Address.sin_addr.s_addr = INADDR_ANY;
Address.sin_port = htons(nHostPort);
Address.sin_family = AF_INET;
printf("\nBinding to port %d\n",nHostPort);
/* bind to a port */
if(bind(hServerSocket,(struct sockaddr*)&Address,sizeof(Address)) == SOCKET_ERROR) {
printf("\nCould not connect to host\n");
return 0;
}
/* get port number */
getsockname(hServerSocket, (struct sockaddr *) &Address,(socklen_t *)&nAddressSize);
printf("Opened socket as fd (%d) on port (%d) for stream i/o\n",hServerSocket, ntohs(Address.sin_port));
printf("Server\n\
sin_family = %d\n\
sin_addr.s_addr = %d\n\
sin_port = %d\n"
, Address.sin_family
, Address.sin_addr.s_addr
, ntohs(Address.sin_port)
);
//Up to this point is boring server set up stuff. I need help below this.
//**********************************************
//instantiate all threads
pthread_t tid[numThreads];
for(i = 0; i < numThreads; i++) {
pthread_create(&tid[i],NULL,worker,NULL);
}
printf("\nMaking a listen queue of %d elements",QUEUE_SIZE);
/* establish listen queue */
if(listen(hServerSocket,QUEUE_SIZE) == SOCKET_ERROR) {
printf("\nCould not listen\n");
return 0;
}
while(1) {
pthread_mutex_lock(&mtx);
printf("\nWaiting for a connection");
while(!empty(head,tail)) {
pthread_cond_wait (&cond2, &mtx);
}
/* get the connected socket */
hSocket = accept(hServerSocket,(struct sockaddr*)&Address,(socklen_t *)&nAddressSize);
printf("\nGot a connection");
enqueue(queue,&tail,hSocket);
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond); // wake worker thread
}
}
Here is the worker thread. This should be always running checking for new requests (by seeing if the queue is not empty). At the end of this method, it should be deferring back to the boss thread to wait for the next time it is needed.
void *worker(void *threadarg) {
pthread_mutex_lock(&mtx);
while(empty(head,tail)) {
pthread_cond_wait(&cond, &mtx);
}
int hSocket = dequeue(queue,&head);
unsigned nSendAmount, nRecvAmount;
char line[BUFFER_SIZE];
nRecvAmount = read(hSocket,line,sizeof line);
printf("\nReceived %s from client\n",line);
//***********************************************
//DO ALL HTTP PARSING (Removed for the sake of space; I can add it back if needed)
//***********************************************
nSendAmount = write(hSocket,allText,sizeof(allText));
if(nSendAmount != -1) {
totalBytesSent = totalBytesSent + nSendAmount;
}
printf("\nSending result: \"%s\" back to client\n",allText);
printf("\nClosing the socket");
/* close socket */
if(close(hSocket) == SOCKET_ERROR) {
printf("\nCould not close socket\n");
return 0;
}
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond2);
}
Any help would be greatly appreciated. I can post more of the code if anyone needs it, just let me know. I'm not the best with OS stuff, especially in C, but I know the basics of mutexes, cond. variables, semaphores, etc. Like I said, I'll take all the help I can get. (Also, I'm not sure if I posted the code exactly right since this is my first question. Let me know if I should change the formatting at all to make it more readable.)
Thanks!
Time for a workers' revolution.
The work threads seem to be missing a while(true) loop. After the HTTP exchange and closing the socket, they should be looping back to wait on the queue for more sockets/requests.