Seems like pthread_create is overriding previous threads - c

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)

Related

Multiple clients on server with multithreading using Socket Programming in C

I want to write some code using multithreading and sockets to create a server and this server can accept four connections (clients)
How can I receive and send data to the client? How do I know from which client I receive my data?
This the code:
if (listen(serverSocket, 20) == 0)
printf("Listening\n");
else
printf("Error\n");
int i = 0;
while (1){
addr_size = sizeof(serverStorage);
//accept
newSocket = accept(serverSocket,
(struct sockaddr*)&serverStorage,
&addr_size);
int type = 0;
recv(newSocket,
&type, sizeof(type), 0);
if (type == 1) {
// Creater readers thread
if (pthread_create(&readerthreads[i++], NULL,
reader, &newSocket)
!= 0)
// Error in creating thread
printf("Failed to create thread\n");
}
else if (type == 2) {
// Create writers thread
if (pthread_create(&writerthreads[i++], NULL,
writer, &newSocket)
!= 0)
// Error in creating thread
printf("Failed to create thread\n");
}
if (i >= 5) {
// Update i
i = 0;
while (i < 5) {
// Suspend execution of
// the calling thread
// until the target
// thread terminates
pthread_join(writerthreads[i++],
NULL);
pthread_join(readerthreads[i++],
NULL);
}
// Update i
i = 0;
}
}
How can I modify it to receive data from a specific client and send also to specific client?
there is the method send(); and recv(); but I don't know How the variable NewSocket tell to which client is sending it

can pthread_join be unblockant?

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.

How to use recv to recieve long text strings

I am trying to create a server and client program that sends a string from client to server where the server executes that string and sends the output back to the client. I am doing this in linux and I am very confused why my program isnt working the least bit. Here is the code.
**Client**
int main()
{
//Code to use unix socket here
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
while(printf("> "), fgets(str, MAX, stdin), !feof(stdin)) {
if (send(s, str, strlen(str), 0) == -1) {
perror("send");
exit(1);
}
}
done=0;
do {
if(t=recv(s, str, MAX, 0)<0)
perror("recv failed at client side!\n");
str[t] = '\0';
if(strcmp(str, "ENDOFTRANS")==0)
{
printf("\nRead ENDOFTRANS. Breaking loop.\n");
done=1;
}
printf("Server > %s", str);
} while(!done);
}
And then the server code is:
**Server**
#define MAX 1000
int main(void)
{
//Unix socket code
//This process is now a daemon.
daemon();
//Listens for client connections, up to 5 clients can queue up at the same time.
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
for(;;) {
int done, n, status;
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done = 0;
do {
switch(fork())
{
case -1: //ERROR
perror("Could not fork.\n");
break;
case 0: //CHILD
//Accept string from client.
//Edit: Why am I getting an error here? says: Invalid argument.
if(n = recv(newsock, str, MAX, 0)) {
perror("Recv error at server side.\n");
exit(1);
}
str[n]='\0';
if (n <= 0) {
if (n < 0)
perror("recv");
done = 1;
}
printf("String=>%s<",str);
//Redirect socket to STDOUT & STDERR.
test = close(WRITE); assert(test==0);
test = dup(newsock); assert(test==WRITE);
test = close(ERROR); assert(test==0);
test = dup(newsock); assert(test==ERROR);
if (!done)
{
if (str==something)
{
//execute command
}
else {
//Fork and execvp the command
}
//Sends End of Transaction character.
ENDTHETRANS();
exit(0);
}
break;
default: //PARENT
//Parent keeps accepting further clients.
wait(&status);
if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done=1;
break;
}
} while (!done);
}
close(s);
}
Im relatively new to programming in general and from my understanding the client code is good except that when it recieves the text back from the server it only recieves the text in small bits (2 rows at a time). I have to keep pressing enter on client promt to get the rest of the input. I have tried so many things that by this point I dont even know what I am doing wrong anymore.
Firstly, in the server code, after it recieves the string from the client I have a printf("String=>%s<",str); that outputs the string. However when the server prints the output as String=>ls -l the < key at the end gets eaten up somehow. It shouldnt be doing that right?
Any help much appreciated. Please bare in mind that I am a beginner and have only used pipes as inter process communcation before. Now I wanna make my first unix socket program.
Thanks in advance.
The usual problem in cases such as this is not realizing that SOCK_STREAM sockets don't preserve message boundaries. So data sent with a send call might be split up and received in multiple recvs, or it might be coalesced and multiple sends end up in a single recv. Most importantly, when a kernel send buffer fills up, a send call might write partial data (sending only some of the requested data) and return a short return value. You need to test for this and resend the rest of the data.
Another problem that often shows up is issues with line endings (particularly when talking between linux and windows). There may be extra carriage return characters (\r) in the either the client or server that confuse the other side. These tend to result in apparently missing or truncated output when printed.
edit
The line
if(t=recv(s, str, MAX, 0)<0)
is equivalent to
if(t = (recv(s, str, MAX, 0)<0))
that is, it sets t to 0 or 1 depending on whether there was an error or not. As with most errors of this type, turning on warnings will give you some indication about it.

Close socket after select()

I'm coding an IRC client and I would like implement a "/server" command to switch the connection of my client to an other server.
Before initialize the new connection I want to close the sockect's fd but the close() call fail. Anybody could say me why ?
Here is my code :
/* Main execution loop */
FD_ZERO(&irc->rdfs);
FD_SET(STDIN_FILENO, &irc->rdfs);
FD_SET(irc->socket_fd, &irc->rdfs);
if ((select(irc->socket_fd + 1, &irc->rdfs, NULL, NULL, NULL)) == -1)
{
if ((close(irc->socket_fd)) == -1)
exit(usage(CLOSE_ERROR));
exit(usage(SELECT_ERROR));
}
if (FD_ISSET(STDIN_FILENO, &irc->rdfs))
{
fgets(irc->buffer, SIZE - 1, stdin);
{
p = strstr(irc->buffer, RET);
if (p != NULL)
*p = 0;
else
irc->buffer[SIZE - 1] = 0;
}
write_on_server(irc, irc->buffer); /* The function where I call switch_server() in */
}
else if (FD_ISSET(irc->socket_fd, &irc->rdfs))
{
if ((read_on_server(irc)) == 0)
exit(usage(SERVER_DISCONNECT));
puts(irc->buffer);
}
And here is where I'm trying to close my socket's fd :
void switch_server(t_irc *irc)
{
if ((close(irc->socket_fd)) == -1) /* This is the close which fail */
exit(EXIT_FAILURE);
}
void write_on_server(t_irc *irc, const char * buffer)
{
if (!(strncmp("/server", buffer, strlen("/server"))))
switch_server(irc);
else
if ((send(irc->socket_fd, buffer, strlen(buffer), 0)) < 0)
{
if ((close(irc->socket_fd)) == -1)
exit(usage(CLOSE_ERROR));
exit(usage(CLIENT_SEND_ERROR));
}
}
Thanks a lot.
If you want to know why a syscall like close() failed, use perror() to print an error message to stderr, or strerror(errno) to convert the error code to a string and output it some other way.
Almost certainly the socket FD is invalid. You need to call perror() on that, and on the select() failure.

select() not responding?

The objective of my program is to use select to manage multiple sockets. However, I thought of trying it with one socket first. Now, the problem that I am facing is that initially client sends data to server, and server receives it and displays it, but then when client again sends some data, the server code remains still at select command.
here are some snippets that will give you an idea of how I am initializing the socket.
if((master_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
exit(1);
}
if((bind(master_socket, (struct sockaddr *)&req, sizeof(req))) < 0)
{
exit(1);
}
listen(master_socket, 5);
FD_SET(master_socket,&listening);
/* wait for connection, then receive and print text */
len = sizeof(struct sockaddr);
while(1)
{
FD_ZERO(&listening); //Flush out everything in socket
FD_SET(master_socket,&listening); // Add master
if(f_client>0) // Add client if any
{
FD_SET(f_client,&listening);
}
printf("Checking for new connection \n");
//Timeout is null, so waiting indefinitely
rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
if (FD_ISSET(master_socket, &listening))
{
printf("Master side invoked\n");
if((f_client = accept(master_socket, (struct sockaddr *)&req, &len)) < 0)
{
exit(1);
}
}
else if (FD_ISSET(f_client,&listening))
{
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
}
}
Basically in above program, it connects to the server, maintains a file descriptor for client f_client and add it. And in every round, it clears the socket, add master socket, and client socket if any, and then checks. Problem here is, first time it works, but second time when client sends some data. it gets hang to rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
I am not to understand what is wrong here. Can anyone help?
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
This code is broken. The fputs function can only be used with a C-style string. You just have arbitrary data with no particular structure. Since you ignore valread, you also have no idea how many bytes you read. (Think about it, how could fputs possibly know how many bytes to output? That information is only in valread, and you don't pass it that information.)
You've already received the data, this broken code just threw it away. If you log valread, you'll see that you actually already read it in your last call to read before the call to select that hung.
instead of fputs, you could use something like this:
for (int i = 0; i < valread; ++i)
putchar(buf[i]);

Resources