I'm learning to work with sockets in c.
Here is my code:
void work_with_client(int client_sock){
char buff[10] = {0};
while(1){
kv_log_message("\tWork with client\n");
int is_received = recv(client_sock, buff, 10, 0);
if(is_received < 0){
perror("Received failed");
} else {
printf("RESPONSE: %s\n", buff);
}
printf("RESPONSE %d\n", is_received);
sleep(1);
}
}
When I connect through telnet to this server and immediately disconnect, server print this lines:
Work with client
RESPONSE:
RESPONSE 10
Work with client
RESPONSE:
RESPONSE 10
Work with client
RESPONSE:
RESPONSE 10
Work with client
RESPONSE:
RESPONSE 10
Work with client
RESPONSE:
RESPONSE 1
Work with client
RESPONSE:
RESPONSE 0
Work with client
And I don't get why first 4 recv calls get full size of buffer data (I don't write anything to the socket from client perspective).
Any suggestions?
P.S. Run on Mac OS X Yosemite
UPDATE: i test my program on Linux, and recv call always return 0 in my case, so probably the problem in telnet on Mac OS X
When recv() returned 0 this indicates the other side shut down the connection.
From man recv:
RETURN VALUE
[...] The return value will be 0 when the peer has performed an orderly shutdown.
You then might like to exit the while() loop.
Update:
Also the code might invoke undefined behaviour by passing a non 0-terminated "string" to printf().
To fix this read one char less to buff then it's size and add the 0-terminator.
while (1)
{
ssize_t is_received = recv(client_sock, buff, sizeof buff - 1, 0);
if (is_received < 0){
perror("Received failed");
break;
} else if (is_received == 0) {
printf("%s\n", "peer shutdown");
break;
}
buff[is_received] = '\0';
printf("RESPONSE: %s\n", buff);
}
Ok i get it, telnet on Mac OS X (Yosemite) send some data if you just close terminal without proper closing connection through telnet itself (i.e. put escape character and quit command).
Honestly I don't know is it bug or some kind of debug behaviour.
Related
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);
}
}
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
I'm trying to implement a client-server application with multiclients using threads. Just to try, I would like to print the messages from each client, but when I send messages from a client, the server does not print anything.
Server (thread code)
void comunicationHandler(void *socket)
{
int sock = *(int*) socket;
char msg[2000];
while ((strcmp(msg, "!quit")) != 0) {
if (recv(sock, msg, 2000, 0) < 0)
puts("Error recv");
printf("%s", msg);
}
puts("Client Disconnected\n");
}
when I send "!quit", the Server goes in a infinite loop printing the messages
Client
for(;;) {
printf("\nInserisci il msg: ");
scanf("%s", msg);
if (strcmp(msg, "!quit") == 0)
break;
write(sd, msg, 2000);
}
There are multiple problems with your code:
TCP is stream based, there is no guarantee that all the bytes you send will be received in one shot on the other side. You need to modify code to check what is the number of bytes received and is it atleast equal to the size of "!quit" before you go in for the "strcmp" comparison.
Better to null terminate the buffer once you receive the buffer equal to the size of "!quit"
It is not clear as to why you are sending a 2000 bytes buffer from the client when you intend to send only "!quit". Modify and send only appropriate size as needed
Check recv return value against 0 also
Break out of the loop in both server and client once the Job is done.
Server goes in a infinite loop
You want to test recv()'s result against 0 and quit in this case. 0 indicates that the client orderly closed the connection.
I'm trying to write a simple two way socket communication using C. So far, the main while loop for my client.c file looks like this:
while ( gets(str) != NULL ) {
sSize = send(s, str, strlen(str), 0);
if ( len != strlen(str) ) {
exit(1);
}
else {
rSize = recv(s, buffer, 64, 0);
buf[rSize] = '\0';
printf("%s\n", buffer);
}
}
while loop in sever.c looks like this:
while ( 1 ) {
gets(str);
send(p, str, strlen(str), 0);
rSize = recv(p, buffer, 32, 0);
if ( rSize < 0 ) {
exit(1);
}
buf[len] = '\0';
else{
printf("%s\n", buffer);
}
}
The program compiles normally and I can establish connection between both machines, but when I send message either from client or server, I get an anomaly:
Sending message 'hi' from client
client -------------------------- server
hi
If I go to server to send 'you' message, I get:
client -------------------------- server
hi
you
you hi
Not sure exactly how this is, but what I'm trying to achieve is that, whenever message is sent from either client or server, it should display immediately on the other side.
Please note that gets() is a blocking function. Initially both client and server are blocked in gets() waiting for input. When you type 'hi' on client, it sends this to the server which is still blocked on gets.
After sending hi, the client blocks on recv() call, waiting for message from server. On the other side, server hasn't still received the 'hi' message send by the client.
When you type 'you' on the server, it comes out of gets() and sends 'you' to client. After that the server calls recv() and reads the 'hi' sent by the client. Since the client is already waiting in recv(), it reads 'you' sent by the server.
Thus the program is working absolutely the way it has been implemented. Please mention your object, not sure what do you want to achieve.
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.