Persistent Connection on a multiprocessed and multithreaded C server - c

Third time I try to ask this question, maybe this time I'll be able to explain my problem better.
I have a multiprocess server with each process doing the accept() (avoiding the Thundering Herd problem with file locking, don't worry). Each process initialize a thread pool (excpet the main one the manage the others). When the accept() succeeds the file descriptor is passed to the thread pool and one of these threads is awakened by a pthread_cond_signal(). After this, the process returns on the file locking waiting to pass through it so it can wait again on the accept(). Meanwhile the thread reads the file descriptor and does its job: reading the HTTP request and serving it in an infinite loop of reading-serving (in order to obtain HTTP persistent-connection). The loop will be broken only if an error occurs or if the timeout expires.
So far so good. But something occurs right after a request is served correctly: in fact, the first request is read and served entirely but when the thread restarts the cycle and enters the read cycle it remains stuck because it reads only few letters like "GE" or "GET", insted of the entire request. If I remove the infinite cycle (for the persisten-connection), each request is served by a different thread and no error occurs!!
This is the reading cycle:
for (;;) {
ssize_t readn, writen;
size_t nleft;
char buff[BUFF_SIZE];
char *ptr = buff;
errno = 0;
nleft = BUFF_SIZE;
while(nleft > 0) {
//I will read as much as I can using the MSG_DONTWAIT flag making the call non-blocking
//that means that or the call will succed or it will be closed by the other side
if ((readn = recv(connsd, ptr, nleft, MSG_DONTWAIT)) < 0) {
//If the non-blocking recv fails, it could set errno with one of the following errorcode
if (errno == EAGAIN || errno == EWOULDBLOCK) {
//This check has been implemented due to an error that happened several times
//The buffer was empty even if a new data was sent.
//This check gives a sort of second chance to the recv.
if (strlen(buff) < 10) {
errno = 0; //It is important to reset the errno!!
continue;
//If other things occured then I will terminate the string and exit the cicle
} else {
break;
}
// If the conenction has been closed by the client
} else if (errno == EINTR) readn = 0;
// If other things occured I will simply shutdown the connection
else {
shutdown_sequence(connsd);
return EXIT_FAILURE;
}
// If I read nothing
} else if (readn == 0) break;
nleft -= readn;
ptr += readn;
}
buff[strlen(buff)-1] = '\0';
//request parsing...
//request serving...
}
Thanks everyone for the patience!
EDIT1: Just tried using Wireshark in order to see what happen. The first request is read and served correctly, but then I receive "Continuation or non-HTTP Traffic" and [TCP Window Full]... I'm trying this server on a Virtual Machine in Ubuntu 14.04
EDIT2: I tried with a simple loop:
while(nleft > 0) {
printf("Entering cylce and reading\n");
fflush(stdout);
if ((readn = recv(connsd, ptr, nleft, 0)) > 0) {
nleft -= readn;
ptr += readn;
printf("reading...\n");
fflush(stdout);
}
if (readn == 0) {
printf("connection closed or nothing more to read\n");
fflush(stdout);
break;
}
if (readn == -1) {
printf("error occurred\n");
fflush(stdout);
break;
}
}
On the terminal I only read:
Entering cylce and reading
reading...
Entering cylce and reading
While Httperf (called with --num-calls=2 --num-conns=1) uses the 50% of the CPU. When I press Ctrl+C to terminate it, the terminal prints:
connection closed or nothing more to read
buff =
GET /1262662405106.jpg HTTP/1.1
User-Agent: httperf/0.9.0
Host: localhost
EDIT3: In response to David:
while(nleft > 0) {
printf("I'm going on the read\n");
fflush(stdout);
if ((readn = recv(connsd, ptr, nleft, 0)) > 0) {
nleft -= readn;
ptr += readn;
if (*(ptr-2) == '\r' && *(ptr-1) == '\n') {
printf("It's an HTTP request\n");
fflush(stdout);
break;
} else continue;
} else if (errno == EINTR || readn == 0) {
break;
}
}
It perfectly recognise the first HTTP request beacuse it prints the message. But for the second one it prints "I'm going on the read" once. When I press Ctrl+C the cycle continues indefinitely printing the same message.
EDIT4:
So... the problem was in the HTTP response... A mistake with the header and a bad allocation of the string. Thank you, Mr. David!

If you're going to do non-blocking I/O and don't want to burn the CPU at 100%, you have to have some place in your code where you wait for data to arrive. You have no such code, so you burn the CPU at 100% spinning tightly while you wait for data to arrive. It sounds like you want blocking I/O. (Start by getting rid of MSG_DONTWAIT.)
Also, don't use strlen to figure out the length of something that's not a string. If you need to know how many bytes were received, keep track of it yourself.
in fact, the first request is read and served entirely but when the thread restarts the cycle and enters the read cycle it remains stuck because it reads only few letters like "GE" or "GET", insted of the entire request.
If you haven't read the entire request, call the read function again until you have an entire request. Use a blocking read.
Basically:
Do a blocking read into our request buffer, after any data already in the buffer.
Did I get an error or is the connection closed? If so, stop.
Do I have a complete request according to the HTTP protocol? If not, go to step 1.
Process the request, send the response.
Go to step 1.

Related

Where would my data be getting lost at within this mutex/pthread_cond_wait structure?

FINAL EDIT: Solution to problem was stated by the answer I have selected. The representative example code is shown in the diff here
EDIT: Full compile-able code at the bottom of the post.
I have this rudimentary multithreaded server that simply accepts a connection and is supposed to pass the file descriptor off to a thread to allow this thread to handle it directly until the client disconnects.
For some reason, even with the following code flow inside of the server, some clients "Fall through the cracks" and get stuck in limbo. (They never get handled by the server so they just hang after accepting the connection)
The following block is my server main running loop:
while(g_serv.b_running)
{
//printf("Awaiting connection.\n");
client_fd = accept(g_serv.serv_listener_fd,
(struct sockaddr*)&cli_addr,
&clilen);
if (0 > client_fd)
{
fprintf(stderr,
"Error accepting connection. [%s]\n",
strerror(errno));
continue;
}
err = sem_trywait(&(g_serv.client_count_sem));
if (0 > err)
{
fprintf(stderr,
"Max connections reached. [%s]\n",
strerror(errno));
notify_client_max_connections(client_fd);
close(client_fd);
client_fd = 0;
continue;
}
printf("A client has connected.\n");
char byte[2] = "0";
err = send(client_fd, byte, 1, 0);
// Set up client FD in global position and wake up a thread to grab it
//
pthread_mutex_lock(&(g_serv.new_connection_fd_lock));
g_serv.new_connection_fd = client_fd;
if (0 != g_serv.new_connection_fd)
{
pthread_cond_signal(&(g_serv.new_connection));
}
pthread_mutex_unlock(&(g_serv.new_connection_fd_lock));
}
This block is the thread handling function:
void* thread_handler(void* args)
{
serv_t* p_serv = (serv_t*)args;
bool thread_client_connected;
int thread_client_fd;
while(p_serv->b_running)
{
pthread_mutex_lock(&(p_serv->new_connection_fd_lock));
while (0 == p_serv->new_connection_fd && p_serv->b_running)
{
pthread_cond_wait(&(p_serv->new_connection),
&(p_serv->new_connection_fd_lock));
}
thread_client_fd = p_serv->new_connection_fd;
p_serv->new_connection_fd = 0;
pthread_mutex_unlock(&(p_serv->new_connection_fd_lock));
// In the case of a pthread cond broadcast for exiting the server.
//
if (0 == thread_client_fd)
{
continue;
}
thread_client_connected = true;
while (thread_client_connected)
{
thread_client_connected = handle_client(thread_client_fd);
}
close(thread_client_fd);
thread_client_fd = 0;
sem_post(&(p_serv->client_count_sem));
}
return NULL;
} /* thread_handler */
Just for data reference here is my serv_t struct:
typedef struct serv_t {
bool b_running;
int max_connections;
int serv_listener_fd;
sem_t client_count_sem;
pthread_mutex_t new_connection_fd_lock;
pthread_cond_t new_connection;
int new_connection_fd;
pthread_t* p_thread_ids;
} serv_t;
Basically, if I run netcat or a client program I have against it with multiple instances via a bash command to "background" the application, some of these instances get stuck. I have it redirecting the output to a file, but what's happening is that particular instance of the client/netcat is just getting stuck after the accept call.
More specifically, if I run my program with two threads, one instance of a program gets stuck and no subsequent copies get stuck, even running 6500 instances against the server.
If I run it with ten threads, as many as 8 or 9 instances get stuck, but the threads still function properly within the server.
EDIT:
Client code I refer to, starting from the server letting the client know that the server is ready to receive data:
char buff[2] = { 0 };
err = recv(client_socket_fd, buff, 1, 0);
if ('0' != buff[0] && 1 != err)
{
fprintf(stderr,
"Server handshake error. [%s]\n",
strerror(errno));
close(client_socket_fd);
return EXIT_FAILURE;
}
if (NULL != p_infix_string)
{
if (MAX_BUFFER_SIZE < strlen(p_infix_string))
{
fprintf(stderr,
"Infix string is over 100 characters long.\n");
return EXIT_FAILURE;
}
errno = 0;
char* p_postfix = infix_to_postfix(p_infix_string);
if (EINVAL == errno || NULL == p_postfix)
{
fprintf(stderr, "Error converting provided string.\n");
}
bool success = send_postfix(p_postfix, client_socket_fd);
free(p_postfix);
if (false == success)
{
fprintf(stderr,
"An error occured while sending the equation to the server.\n");
close(client_socket_fd);
return EXIT_FAILURE;
}
}
The client is getting stuck at the receive call here:
bool send_postfix(char* p_postfix, int client_socket_fd)
{
if (NULL == p_postfix)
{
fprintf(stderr, "No postfix string provided to send to server.\n");
return false;
}
printf("Sending postfix to server\n");
int err = send(client_socket_fd,
p_postfix,
strnlen(p_postfix, MAX_BUFFER_SIZE),
0);
if(strnlen(p_postfix, MAX_BUFFER_SIZE) > err)
{
fprintf(stderr,
"Unable to send message to server. [%s]\n",
strerror(errno));
return false;
}
char response[MAX_BUFFER_SIZE] = { 0 };
printf("Waiting for receive\n");
err = recv(client_socket_fd, &response, MAX_BUFFER_SIZE, 0);
if (0 == err)
{
fprintf(stderr,
"Connection to server lost. [%s]\n",
strerror(errno));
return false;
}
else if (0 > err)
{
fprintf(stderr,
"Unable to receive message on socket. [%s]\n",
strerror(errno));
return false;
}
printf("Server responded with: \n%s\n", response);
return true;
} /* send_postfix */
EDIT: https://github.com/TheStaplergun/Problem-Code
I uploaded the code to this repo and removed the need for the extraneous files I use and filled them with placeholders.
You can recreate this problem using the server with the command ./postfix_server -p 8888 -n 2 and the client issue in another terminal with for i in {1..4}; do ./postfix_client -i 127.0.0.1 -p 8888 -e "3 + $i" &> $i.txt & done
The output of each client will be forcefully flushed because of the setbuf at the top of client. Run it, see if any programs hang, if not run that command again. Just type PS and see if one of them is hanging, and look at the resulting text file. You will see it is stuck at the receive call.
If you sigint the server (CTRL + C), the client that was stuck will close with a Connection reset by peer response from the server, so the server still does have that file descriptor locked up somewhere.
I believe a race condition is happening somehow, because it only happens randomly.
A curious thing is it only happens ONCE PER SERVER INSTANCE.
If I kill that hung instance and proceed to do it again 10000 times it never does another hang until the server is reset.
For some reason, even with the following code flow inside of the
server, some clients "Fall through the cracks" and get stuck in limbo.
(They never get handled by the server so they just hang after
accepting the connection)
There may be other issues, but the first one I see is that main loop does not ensure that a new connection is actually picked up by any handler thread before it tries to hand off the next connection. Even if there are handler threads already blocked on the CV when a new connection is accepted, it is possible for the main server thread to signal the CV, loop back around, accept another connection, reacquire the mutex, and overwrite the new-connection FD before any handler thread picks up the previous one. The chances of that increase if you have more threads than cores.
Note that that will also interfere with your semaphore-based counting of available handlers -- you decrement the semaphore for every semaphore accepted, but you increment it again only for those that are successfully handled.
There are various ways that you could make the main server thread wait for the new connection to be picked up by a handler. One group would involve the server waiting on a CV itself, and relying on a handler to signal it after picking up the connection. Another, perhaps simpler, approach would involve using a semaphore to similar effect. But I would suggest instead not waiting, but instead creating a thread-safe queue for available connections, so that the server doesn't have to wait. That would even allow for queueing more connections than presently available handlers, if that would be useful to you.

TCP socket read/write freezes server

I want to implement protocol using TCP sockets in C which works kind of this way:
Client connects to server and sends filename that it wants to
download
Server reads that value and checks whether it is valid filename (does it exist on the server) + sends ACCEPT or FAILURE status to client
Client reads that status and prepares itself to download + sends READY status to server
Server sends file and closes connection
Server code:
char response[128];
int bytes_read;
while ((bytes_read = read(info.socket, response, 128)) > 0) {}
if (valid_request(files, files_count, response)) {
write(info.socket, MC_ACCEPT, 4);
} else {
write(info.socket, MC_FAILURE, 4);
}
Client code:
int w_status = write(sck, requested_file, strlen(requested_file));
if (w_status < 0) {
fprintf(stderr, "Error writing to socket. Status: %d", w_status);
exit(1);
}
char status[4];
while ((resp = read(sck, status, 4)) > 0) {}
if (strcmp(status, MC_ACCEPT) == 0) {
printf("ACCEPTED!\n");
} else if (strcmp(status, MC_ACCEPT) == 0) {
printf("FAILURE\n");
} else {
printf("DONT KNOW\n");
}
close(sck);
The problem is that server freezes itself on the read() part. It looks like client sends the filename and waits for server response (with status) but server is frozen at read().
Am I somehow blocking the TCP socket? What is wrong with my reasoning?
On the server side:
char response[128];
int bytes_read;
while ((bytes_read = read(info.socket, response, 128)) > 0) {}
You try to read those 128 chars in several read calls. But it will block forever until client closes the socket (it's TCP connected, there's always something to read unless peer closes connection).
And if the data arrives in more than 1 chunk, your code is incorrect, because the first chunk will be overwritten by the next one, and so on. You have to change the offset of your buffer, and do not attempt to read 128 bytes every time, or you'll get stuck.
int bytes_read = 0;
while (bytes_read < 128)
{
int currently_read = read(info.socket, response + currently_read, 128-bytes_read);
bytes_read += currently_read;
}
On the client side, same kind of issues too:
You seem to wait for 4 chars.
You try to read those 4 chars in the first read. But you don't check if 4 chars are actually read (return code discarded).
After that, you read using a loop until you get 0 bytes. But since connection doesn't end, you're stuck there.
What you want is to read exactly 4 bytes before doing something else.
And increase your buffer size & null-terminate your string or strcmp will fail.
char status[5];
status[4] = '\0';
int nb_read = 0;
while (nb_read < 4)
{
int currently_read = read(sck, status + nb_read, 4-nb_read);
nb_read += currently_read;
}
The problem is that you're not processing the request inside the while loop:
while ((bytes_read = read(info.socket, response, 128)) > 0) {}
This keeps looping until read() returns 0, which happens when the client closes the connection, or gets an error and returns -1. After it reads the request from the client, it goes back and calls read() again. Since the client hasn't sent anything else, this blocks.
This is why killing the client gets it unstuck. That closes the connection, so it gets EOF and read() returns 0.
You need to process the input inside the loop:
while ((bytes_read = read(info.socket, response, sizeof response -1)) > 0) {
response[bytes_read] = '\0'; // add string null terminator
if (valid_request(files, files_count, response)) {
write(info.socket, MC_ACCEPT, 4);
} else {
write(info.socket, MC_FAILURE, 4);
}
}

How to recv until theres nothing more to recv without eof?

So i need to recv an html file from the server to the client, the file is bigger than the buffer so i make several sends. Thats why i have this loop when i recv
while (i = recv(s, buf, TAM_BUFFER, 0)) {
if (i == -1) {
perror(argv[0]);
fprintf(stderr, "%s: error reading result\n", argv[0]);
exit(1);
}
while (i < TAM_BUFFER) {
j = recv(s, &buf[i], TAM_BUFFER - i, 0);
if (j == -1) {
perror(argv[0]);
fprintf(stderr, "%s: error reading result\n", argv[0]);
exit(1);
}
i += j;
}
/* Print out the file line by line. */
printf("%s", buf);
}
the send looks something like this:
while (fgets(buf, sizeof(buf), fp)){
if (send(s, buf, TAM_BUFFER, 0) != TAM_BUFFER) errout(hostname);
}
The problem is the loop never ends, becase it doesnt recv the eof and i is never 0, its just remain blocked there.
I cant do the close to send the eof because after he recv the whole file, the client will ask for another file.
I tryed to send a SIGALRM if the loop stays blocked for longer than 5 seconds but it doesnt work as expected, because the loop wont stop, and it will throw an error.
Also how can i do to be able to recv less than TAM_BUFFER?(in the send, change the TAM_BUFFER -> strlen(buf)) I know i need to change the interior loop, but then ill have the same problem, j will not be 0 never, so i dont know how could i end it.(or maybe i dont need the second loop in this case).
EDIT: i cant send the lenght of the file beucause of the protocol im following
TCP is a protocol used to transport a single unstructured octet stream in each direction. Shutdown of the connection (i.e. EOF) is the only way in TCP to signal to the peer that no more data will be sent in this connection. If you need a different way because you need to distinguish between multiple messages inside the same TCP connection then you need to use an application level protocol which can specify such message boundaries. This is usually done by fixed message size, prefixing the message with a length or by special boundary markers.
If you can't embed payload size in your protocol, you have to identify EOF by closing socket or checking for timeout. You can use select function and set timeout for it, see here Using select and recv to obtain a file from a web server through a socket and https://stackoverflow.com/a/30395738/4490542

C sockets, Incomplete file transfer

I'm writing a C program to transfer a file of fixed size, a little over 2Mb, from a server to a client. I'm using TCP sockets on Linux and the code I wrote is the following:
Server (sender)
while (1) {
int nread = read(file, buffer, bufsize);
if (nread == 0) // EOF
break;
if (nread < 0) {
// handle errors
}
char* partial = buffer;
while (nread > 0) {
int nwrite = write(socket, partial, nread);
if (nwrite <= 0) {
// handle errors
}
nread -= nwrite;
partial += nwrite;
}
}
// file sent
shutdown(socket, SHUT_WR);
Client (receiver)
while (filesize > 0) {
nread = read(socket, buffer, bufsize);
if (nread == 0) {
// EOF - if we reach this point filesize is still > 0
// so the transfer was incomplete
break;
}
if (nread < 0) {
// handle errors
}
char* partial = buffer;
while (nread > 0) {
nwrite = write(file, partial, nread);
if (nwrite <= 0) {
// handle errors
}
nread -= nwrite;
partial += nwrite;
filesize -= nwrite;
}
}
if (filesize > 0) {
// incomplete transfer
// handle error
}
close(socket);
When testing the code on my laptop (both client and server "are" on localhost and the communication happen on the loopback interface), sometimes the client exits because read received an EOF, and not because it received all filesize bytes. Since i use a shutdown on the server, this should mean that there is no other data to read.
(Note that the server sent all the bytes and executed the shutdown correctly)
Can you explain me why this is happening?
Where are the missing bytes gone?
-----
EDIT 1 - Clarifications
Some users asked a couple of clarifications so i am posting the answers here:
The program is using TCP blocking sockets
The filesize is a fixed value and is hardcoded in both client and server.
No special socket options as, for example, SO_LINGER are enabled/used.
When the error occur, the server (sender) has already sent all the data and executed the shutdown correctly.
The error, as of today, never happened when testing the application with the client and the server on different machines (transfer over a real network interface and not a loopback interface)
EDIT 2
User Cornstalks pointed me to a really interesting article about the, non always reliable, behaviours of TCP.
The article, which is well worth a read, describe a few tricks useful when sending an unknown amount of data between TCP sockets. The tricks described are the followings:
Take advantage of the SO_LINGER option on the sender. This will help to keep the socket open, upon a call to close(2) or shutdown(2), until all the data has successfully been sent.
On the receiver, beware of pending readable data before the actual receiving loop. It could lead to an immediate reset being sent.
Take advantage of shutdown(2) to signal the receiver the the sender has done sending data.
Let the receiver know the size of the file that will be sent before actually sending the file.
Let the receiver acknowledge the sender that the receiving loop is over. This will help to prevent the sender from closing the socket too soon.
After reading the article, i upgraded my code to implement the tricks number 1 and 5.
This is how i implemented trick number 5:
Server (sender)
// sending loop ...
// file sent
shutdown(socket, SHUT_WR);
// wait acknowledgement from the client
ack = read(socket, buffer, bufsize);
if (ack < 0) {
// handle errors
}
Client (receiver)
// receiving loop..
if (filesize > 0) {
// incomplete transfer
// handle error
}
// send acknowledgement to the server
// this will send a FIN and trigger a read = 0 on the server
shutdown(socket, SHUT_WR);
close(socket);
What about tricks number 2, 3 and 4?
Trick number 2 is not needed because as soon as the server accepts the connection the application proceed to the file transfer. NO extra messages are exchanged.
Trick number 3 is already implemented
Trick number 4 is also already implemented. As mentioned earlier the file size is hardcoded, so there is no need to exchange it.
Did this solve my original problem?
NO my problem was not solved. The error is still happening, and as of today, it only happened when testing the application with both client and server on localhost.
What do you think?
Is there a way to prevent this?
You're:
assuming that read fills the buffer, even though
you're defending magnificently against write() not writing the entire buffer.
You need to do (1), and you don't need to do (2) because you're in blocking mode and POSIX assures that write() doesn't return until all the data is written.
A simple version of both loops:
while ((nread = read(inFD, buffer, 0, sizeof buffer)) > 0)
{
write(outFD, buffer, 0, nread);
}
if (nread == -1)
; // error
A more correct version would check the result of write() for errors of course.

Reading message from socket in C

I try to understand reading from socket in C (Linux), this is only part of code:
while(success == 0) {
while((n = read(sockfd, buffer, BUFFSIZE, 0)) > 0) {
printf("%s",buffer);
bzero(buffer,BUFFSIZE);
}
success = 1;
printf("###");
}
The message is printed, but the three hashes (###) are never print? Why? The program seems to block on read(). Here i do just printing, but what i need to do is to buffer the whole message and then process it.
The program on the other end of the socket is not closing it, nor shutting down its writes (which are your reads), so your end does not know that everything is finished - indeed, logically it isn't finished until the other end says there is nothing more for you to read.
Typically, your application level logic needs to know in advance how much to read, or reads until a certain terminator is received, or the other end gracefully closes or shuts down the socket.
(Non-blocking I/O is something else entirely - it allows you to do other things whilst reading from sockets efficiently in a single thread, but it doesn't solve the problem of determining when you've finished reading from a socket, which is your problem.)
You need to know how large is the message you're receiving, and keep reading until you have the whole message (a read can return only part of your message).
do {
nread = read(s, buf, to_read);
if (nread < 0 && errno == EINTR)
continue;
if (nread < 0) {
perror("read");
exit(1);
}
if (nread == 0) {
printf("socket closed");
... do something appropiate ...
... (reconnecting/exiting the loop with an error/...) ...
}
to_read -= nread;
buf += nread;
} while (to_read > 0);
to_read is the length in bytes you're expecting to read. buf has enough space for it. After every read, update to_read and buf accordingly. And, of course, you should deal with errors correctly.
You have to now when to stop reading from the socket, otherwise the socket will block your program until it receives more data. Have a look at non-blocking sockets if you want to know how to create sockets that don't block your program.
ief2
Try to add the \n. Sometimes the non-ended lines are not printed.
EDIT: Oh wait you mean the program do not end?
I guess your while loop never terminates because the read either succeeds or blocks.
Your while loop will only end when n has the value zero.
When would you expect read to return the value zero? Does the data you are sending to this socket satisfy any condition that would lead to read returning zero?

Resources