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);
}
}
Related
I'm creating a program in c which listens to GET and PUT requests and routes them to an httpserver. The problem is that after about N requests (N = number of servers), the httpserver stops responding because it doesn't receive the data from the client after the first request.
I forward the data by accepting client connections using accept(), read from the client socket and put the data from the client in a buffer using recv until it returns 0. I send this buffer to the server using send():
int connect(int from, int to) {
char buf[BUFFSIZE];
int n = recv(from, buf, BUFFSIZE-1, 0);
if (n == 0) {
return 0;
}
buf[n] = '\0';
n = send(to, buf, n, 0);
if (n == 0) {
return 0;
}
return n;
}
int acceptfd = accept(listenfd, &client_addr, &client_addrlen)
connect(acceptfd, serverfd)
I was wondering if I wasn't properly detecting the end of the client's input, but I print the output of send() returns exactly the number of bytes I expect, but server doesn't receive it.
The server works perfectly every time, when I make requests to it independently. The server receives connections similarly (I can't change the server), it accepts client connections using accept() and it reads from that socket using recv. To clarify, I do not have the server code, but I have my own that works similarly to the one that I am using for this project that I also tested with (the problem persists) :
char buff[BUFF_SIZE + 1];
int client_sockd = accept(server_sockd, &client_addr, &client_addrlen);
int bytes = recv(server_sockd, buff, BUFF_SIZE, 0);
Is there something I could be misunderstanding about send() or maybe buffers? I have tried using the O_NONBLOCK flag and I use select() to determine which socket to read from so I don't know if blocking is the issue.
How to keep the connection listening from the connected client? In the below code the thread receives the data and replies to the client and disconnects. I want to put the receive and sent process in loop. How can I do that ?
void *thread_handle_connection(void *arg) {
char buffer[MAX_MSG_SIZE]; // Receive buffer
int bytes_read;
do {
// If there aren't any connections, sleep and recheck every second
while(!num_connections && !term_requested) {
sleep(1);
}
// Lock out connections queue and grab the first one
pthread_mutex_lock(&queue_mutex);
int connectionfd = remove_connection_from_queue();
pthread_mutex_unlock(&queue_mutex);
if(-1 == connectionfd) {
continue;
}
// pthread_barrier_wait(&barrier); // Barrier for threads - for testing only
// Read up to 1024 bytes from the client
bytes_read = recv(connectionfd, buffer, MAX_MSG_SIZE - 1, 0);
// If the data was read successfully
if(bytes_read > 0) {
// Add a terminating NULL character and print the message received
buffer[bytes_read] = '\0';
// Calculate response
int multiplicand = atoi(buffer);
char *response;
asprintf(&response, "%d", multiplicand * MULTIPLIER);
// Echo the data back to the client; exit loop if we're unable to send
if(-1 == send(connectionfd, response, strlen(response), 0)) {
warn("Unable to send data to client");
break;
}
free(response);
}
// Close connection
close(connectionfd);
} while(bytes_read > 0 && !term_requested);
return NULL;
}
First, recv function doesn't guarantee that you read everything that has been written by sender. You may get part of the data (for example sender may send 10KByte but receiver may receive only 1.5K on first read).
Second, send function doesn't guarantee that it send everything you ask. If not everything has been sent you need to send rest of the answer.
Third, TCP is stream oriented. This means that you need to separate one message from another. For text-based protocol typically "new line" is used for this purpose.
Putting all together. If you want persistent connection with request you need:
define request and response separators
maintain read buffer
read all data to the buffer and scan it for request separator
send response with response separator
If you want level up in networking programming you may want to know something about non-blocking operations and poll/select functions.
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.
so I have two processes, one client-process one server-process. The user can issue a command to the client, when a user enters a command the client will send the command length to the server, and after that it will send the actual command.
The server then sends back first the length of the response and then a response.
I can do 5-30 commands or so with no problem at all, but at some point it fails to read enough bytes, despite the correct response size being received.
The server sends the response in the following way:
str[0] = '\0';
unsigned long int totalSize = 0;
while ((fgets(outBuf, MAXOUTPUT, myFile)) != NULL)
{
strcat(str, outBuf);
}
uint32_t *un = 0;
totalSize = strlen(str);
*un = htonl(totalSize);
result= send(clientFD, un, sizeof(uint32_t), 0);
if(result < 1)
{
printf("Failed sending message size to client");
exit(-1);
}
while(token != NULL)
{
size_t length = strlen(token);
token[length] = '\n';
write(clientFD, token, length + 1);
token = strtok(NULL, "\n");
}
The client has received the message length correctly(verified with prints) and reads the response this way:
result = read(socketFD, recvBuf, bufferlen); //bufferlen is response size
if(result < bufferlen)
{
perror("read()");
exit(-1);
}
I have verified that the client receives the correct message length every time, including the last one where it fails to read.
So my question is: What are likely reasons that my read sometimes fail to retrieve the full response? It happens after doing about 5-30 commands or so usually, and the perror returned is Error 0 (aka no error to be found).
As an additional note, the commands tested are
ls -la, ls -l, ls.
I have not found a pattern in which commands cause the crash, but I have combined them a lot.
Also: Both the client and server are 32 bit and being run on the same machine locally.
read() (especially on sockets) returns as soon as some data is available, it may always return less bytes than you asked for. In this case, you will need to repeat the read until you have read enough data:
size_t bytes_read = 0;
while (bytes_read < bufferlen) {
result = read(socketFD, recvBuf + bytes_read, bufferlen - bytes_read);
if (result < 0) {
perror("read()");
exit(-1);
}
bytes_read += result;
}
The TCP socket works on a byte stream concept. The server is adding bytes to the byte stream, and the client is consuming them. The socket need not send all of the bytes at once; it will eventually send them and they will be read in order at the other end. Messages are not guaranteed to be kept whole. You encounter a problem when you can read the bufferlen field but the whole corresponding message has not arrived yet.
Your client needs to continue reading from the socket until bufferlen bytes have been read.
Also be aware that the act of reading the bufferlen field may also need to be completed with multiple reads.
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.