How to handle blocking read() call when reading from a socket? - c

I'm writing a simple IRC client program in C for self-teaching purposes, and am having trouble understanding the behavior of the read() function when called reading from a socket file descriptor.
The following code snippet works and prints the same output as running
$ echo "NICK gertrudes\r\nUSER a 0 * d\r\n" | nc chat.freenode.net 6667
in the terminal, which is the same as my program prints so far:
while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) {
printf("\nloop\n");
buffer[n] = '\0';
if (fputs(buffer, stdout) == EOF)
error("fputs");
}
if (n < 0)
error("reading from socket");
printf("out of the loop\n");
What I fail to understand is why the program never gets to the final printf call, and rather sits there as if waiting for more from the server. Does that mean that the last reply was longer than 0 and the IRC server just won't send anything new until I send another command?
If so (at the risk of going off-topic here), and read() is blocking, where would I write the logic of sending commands to the server while the program is waiting for that call to return?

What I fail to understand is why the program never gets to the final printf call, and rather sits there as if waiting for more from the server.
It is waitng for more from the server. read() will return zero when the peer disconnects, and not before.

Despite your program not being complete, there are several things that you are wrongly assuming. Let's comment these in your code.
while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) {
It's good to read sizeof(buffer)-1 if you plan to complete it with a \0 byte, but think that you can receive a \0 from the socket, if you want to be general, don't assume you are always reading text. Many security exploits come from errors like this. The programmer assumes (erroneously) that the data is ascii text, and someone exploits a buffer overrun (this is not the case) or something illegal, feeding a lot of null characters to make it fail.
printf("\nloop\n");
buffer[n] = '\0';
if (fputs(buffer, stdout) == EOF)
This is a VERY common mistake... you are used to see that when you put a \n at the end of a buffer, stdio prints everything until the last buffer as soon as it sees it. Well, for this to happen, stdio checks if the descriptor is associated to a terminal (by means of an ioctl(2) call, or a call to isatty(3)). This is no longer true with sockets, so probably your buffer has been copied to stdio buffer, and stdio is waiting for the buffer to fill or you to explicitly flush the buffer with fflush(3) before calling write(2) to send all the data over it.
error("fputs");
Do a fflush(stdout); at this point, so you are sure all your data is sent to the peer, before continuing, or don't use stdio at all (use simple write(2) calls, until you are proficient enough to prepare a thread that select(2)s on the socket to feed more data as soon as it is ready to accept more data)
}
if (n < 0)
error("reading from socket");
printf("out of the loop\n");

Related

Write to file descriptor and immediately read from it

Today I have encountered some weird looking code that at first glance it's not apparent to me what it does.
send(file_desc,"Input \'y\' to continue.\t",0x18,0);
read(file_desc,buffer,100);
iVar1 = strcmp("y",(char *)buffer);
if (iVar1 == 0) {
// some more code
}
It seems that a text string is being written into the file descriptor. Immediately then after that it reads from that file descriptor into a buffer. And it compares if the text written into the buffer is a "y".
My understanding (please correct me if I am wrong), is that it writes some data which is a text string into the file descriptor, and then the file descriptor acts as a temporary storage location for anything you write to it. And after that it reads that data from the file descriptor into the buffer. It actually is the same file descriptor. It seems as a primitive way of using a file descriptor to copy data from the text string into the buffer. Why not just use a strcpy() instead?
What would be the use case of writing to a file descriptor and then immediately read from it? It seems like a convoluted way to copy data using file descriptors. Or maybe I don't understand this code well enough, what this sequence of a send() and a read() does?
And assuming that this code is instead using the file descriptor to copy the text string "Input \'y\' to continue.\t" into the buffer, why are they comparing it with the string "y"? It should probably be false every single time.
I am assuming that any data written into a file descriptor stays in that file descriptor until it is read from. So here it seems that send() is being used to write the string into, and read() is used to read it back out.
In man send it says:
The only difference between send() and write(2) is the presence of flags. With a zero
flags argument, send() is equivalent to write(2).
why would they use send() instead of write()? This code is just so mind boggling.
Edit: here's the full function where this code is originally from:
void send_read(int file_desc)
{
int are_equal;
undefined2 buffer [8];
char local_28 [32];
/* 0x6e == 110 == 'n' */
buffer[0] = 0x6e;
send(file_desc,"Input \'y\' to continue.\t",0x18,0);
read(file_desc,buffer,100);
are_equal = strcmp("y",(char *)buffer);
if (are_equal == 0) {
FUN_00400a86(file_desc,local_28);
}
else {
close(file_desc);
}
return;
}
The send() and recv() functions are for use with sockets (send: send a message on a socket — recv: receive a message from a connected socket). See also the POSIX description of Sockets in general.
Socket file descriptors are bi-directional — you can read and write on them. You can't read what you wrote, unlike with pipe file descriptors. With pipes, the process writing to the write end of a pipe can read what it wrote from the read end of the pipe — if another process didn't read it first. When a process writes on a socket, that information goes to the peer process and cannot be read by the writer.
send(2) is a system call that can only be used with sockets. A socket is a descriptor that allows you to use it to send data or receive from a remote point (a remote socket) that can be on a different computer or in the same as you are. But it works like a phone line, what you send is received by your parnter and what he/she sends is received by you. read(2) system call can be used by sockets, while send(2) cannot be used by files, so your sample code is mixing calls related to files with calls related to sockets (that's not uncommon, as read(2) and write(2) can both be used with sockets)
The code you post above is erroneous, as it blindly compares the received buffer with strcmp function, assuming that it received a null terminated string. This can be the case, but it also cannot.
Even if the sender (in the other side of the connection) agreed on sending a full message, nul terminated string. The receiver must first get the amount of data received (this is the return value of the read(2) call, which can be:
-! indicating some error on reception. The connection can be reset by the other side, or the other side can have rebooted while you send the data.
0 indicating no more data or end of data (the other side closed the connection) This can happen if the other side has a timeout and you take too much to respond. It closes the connection without sending anything. You just receive nothing.
n some data, less than the buffer size, but including the full packet sent by the peer (and the agreed nul byte it sent with it). This is the only case in which you can safely strcmp the data.
n some data, less than the buffer size, and less than the data transmitted. This can happen due to some data fragmentation of the data in several packets. Then you have to do another read until you have all the data send by your peer. Packet fragmentation is something natural in TCP, for example.
n some data, less than the buffer size, and more than the data transmitted. The sender did another transmit, after the one you receive, and both packets got into the kernel buffer. You have to investigate this case, as you have one full packet, and must save the rest of the received data in the buffer, for later processing, or you'll lose data you have received.
n some data, the full buffer filled, and no space to store the full transmitted data remained. You have filled the buffer and no \0 char came... the packet is larger than the buffer, you run out of buffer space and have to decide what to do (allocate other buffer to receive the rest, discard the data, or whatever you decide to do) This will not happen to you because you expect a packet of 1 or 2 characters, and you have a buffer of 100, but who knows...
At least, and as a minimum safe net, you can do this:
send(file_desc,"Input \'y\' to continue.\t",0x18,0);
int n = read(file_desc,buffer,sizeof buffer - 1); /* one cell reserved for '\0' */
switch (n) {
case -1: /* error */
do_error();
break;
case 0: /* disconnect */
do_disconnect();
break;
default: /* some data */
buffer[n] = '\0'; /* append the null */
break;
}
if (n > 0) {
iVar1 = strcmp("y",(char *)buffer);
if (iVar1 == 0) {
// some more code
}
}
Note:
As you didn't post a complete and verifiable example, I couldn't post a complete and verifiable response.
My apologies for that.

No code runs after a read or a write in socket programming in c [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am making a server client program in c.
The server starts first waits for the client then the client "connects".
after the client connects it waits for input from the user while the server is running read() to get that input from the client.
Here is the main issue.
after a new line character has been discovered by the while loop it then sends this data in the buffer from the client to the server which is waiting with a read. The server reads but no code below the read can be run unless it has something to do with the buffer.
so if the code is like this
read(socket_fd, buff, sizeof(buff));
printf("data from the client: %s", buff); // this line will be run in the terminal
printf("TESTING TESTING TESTING"); // this line will never be read
same thing on the clients side.
after it performs the write() on the client side any code that is under the write will not be ran. basically making this essentially a deadlock where both programs (I think) are just waiting on input from the other.
I don't know why this is. perhaps it is waiting for more data with the read, but that wouldn't explain why it runs code that prints the buffer but not anything else.
here is the snippet that sends and recieves data from the client side. The server is set up with TCP
while(1){
//wait for data from user
bzero(buffer, 256);
printf("Enter the string : ");
n = 0;
while ((buffer[n++] = getchar()) != '\n')
;
write(sockfd, buffer, sizeof(buffer));
printf("WE HERE");
read(sockfd, buffer, sizeof(buffer));
printf("READING THE DATA");
printf("From Server : %s", buffer);
if ((strncmp(buffer, "exit", 4)) == 0) {
printf("Client Exit...\n");
break;
}
}
and here is the server code which reads the data from the client and provides a response.
while(1) {
bzero(buffer, 256);
//read the message from the client
read(newsockfd, buffer, sizeof(buffer));
printf("From the client: %s", buffer);
printf("WORKING HERE BEFORE LOWER CASE [SERVER]");
printf("the buffer again: %s", buffer);
lower_string(buffer);
printf("WORKING AFTER THE LOWER CASE [SERVER]");
write(sockfd, buffer, sizeof(buffer));
printf("WRITING TO THE CLIENT");
if (strncmp("exit", buffer, 4) == 0) {
printf("Server Exit...\n");
break;
}
bzero(buffer, 256);
}
Your code contains a number of problems:
You aren't putting newline characters at the end of your printf() statements, which means that the printed text won't be visible until the stdout buffer gets full enough to force it to be flushed to the console. That is probably confusing you about the behavior of your program, since printf() statements are being executed but you aren't seeing their output in a timely manner. You should do e.g. printf("WE HERE\n"); rather than printf("WE HERE");
You aren't capturing the return values from send() and recv() into a variable so that you can examine what values they returned and act on them appropriately. If you don't look at the return values, you don't know how many bytes were actually sent or received (it may be less than the number of bytes you asked to be sent or received!) and you don't know if there was an error or an EOF condition that occurred.
You should be aware that recv() will block until at least one byte of data is available to place into your passed-in buffer, and similarly, write() can block until at least one byte of your passed-in buffer can be consumed by the networking stack. This can indeed lead to a deadlock in certain circumstances (e.g. if the remote peer never sends any data because it is blocked inside a recv() waiting for you to send some data to it first). This problem can be handled via various more advanced techniques (e.g. timeouts, or non-blocking or asynchronous I/O) but I won't go into those here.
Zeroing out your entire 256-byte array and then receiving up to 256 bytes means that in the case where you received 256 bytes of data at once, your array will not be NUL-terminated, and you will invoke undefined behavior if you try to use it as a C-string (e.g. by passing it to printf("%s", buffer);. You'd be better off receiving sizeof(buf)-1 bytes instead (and if you capture the return value of recv() as suggested in point #2, you can then just set buffer[numBytesReceived] = '\0'; afterwards, which is a more efficient way to make sure the string is NUL-terminated than unnecessarily clearing out all 256 bytes)
Note that you cannot assume that you will recv() the entire string within a single recv() call. It's unlikely to happen in this toy program (unless your network conditions are very bad), but in general it's possible for the sender to send() e.g. "123456789" and the receiver's first recv() call to get "1234" and then the second recv() call gets "567" and then the third gets "89", or any other combination of subsets of the string. The receiver is guaranteed to receive all of the bytes in order, but not guaranteed to receive them all at once. Production-level code would need to be smart enough to handle that correctly.

One socket descriptor always blocked on write. Select not working?

Hello I have a server program and a client program. The server program is working fine, as in I can telnet to the server and I can read and write in any order (like a chat room) without any issue. However I am now working on my client program and when I use 'select' and check if the socket descriptor is set to read or write, it always goes to write and then is blocked. As in messages do not get through until the client sends some data.
How can I fix this on my client end so I can read and write in any order?
while (quit != 1)
{
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_SET(client_fd, &read_fds);
FD_SET(client_fd, &write_fds);
if (select(client_fd+1, &read_fds, &write_fds, NULL, NULL) == -1)
{
perror("Error on Select");
exit(2);
}
if (FD_ISSET(client_fd, &read_fds))
{
char newBuffer[100] = {'\0'};
int bytesRead = read(client_fd, &newBuffer, sizeof(newBuffer));
printf("%s",newBuffer);
}
if(FD_ISSET(client_fd, &write_fds))
{
quit = transmit(handle, buffer, client_fd);
}
}
Here is code to transmit function
int transmit(char* handle, char* buffer, int client_fd)
{
int n;
printf("%s", handle);
fgets(buffer, 500, stdin);
if (!strchr(buffer, '\n'))
{
while (fgetc(stdin) != '\n');
}
if (strcmp (buffer, "\\quit\n") == 0)
{
close(client_fd);
return 1;
}
n = write(client_fd, buffer, strlen(buffer));
if (n < 0)
{
error("ERROR writing to socket");
}
memset(buffer, 0, 501);
}
I think you are misinterpreting the use of the writefds parameer of select(): only set the bit when you want to write data to the socket. In other words, if there is no data, do not set the bit.
Setting the bit will check if there is room for writing, and if yes, the bit will remain on. Assuming you are not pumping megabytes of data, there will always be room, so right now you will always call transmit() which waits for input from the command line with fgets(), thus blocking the rest of the program. You have to monitor both the client socket and stdin to keep the program running.
So, check for READ action on stdin (use STDIN_FILENO to get the file descriptor for that), READ on client_fd always and just write() your data to the client_fd if the amount of data is small (if you need to write larger data chunks consider non-blocking sockets).
BTW, you forget to return a proper value at the end of transmit().
Sockets are almost always writable, except when the socket send buffer is full, which indicates that you are sending faster than the receiver is receiving.
So your transmit() function will be entered every time around the loop, so it will read some data from stdin, which blocks until you type something, so nothing happens.
You should only select on writability when a prior send() has returned EWOULDBLOCK/EAGAIN. Otherwise you should just send, when you have something to send.
I would throw this code away and use two or three threads in blocking mode.
select is used to check whether a socket has become ready to read or write. If it is blocking for read then that indicates no data to read. If it is blocking in write, then that indicates the TCP buffer is likely full and the remote end has to read some data so that the socket will allow more data to be written. Since the select blocks until one of the socket descriptions is ready, you also need to use timeout in select to avoid waiting for a long time.
In your specific case, if your remote/receiving end keep reading data from the socket then the select will not block for the write on the other end. Otherwise the tcp buffer will become full on the sender side and select will block. Answers posted also indicate the importance of handling EAGAIN or EWOULDBLOCK.
Sample flow:
while(bytesleft > 0)
then
nbytes = write data
if(nbytes > 0)
bytesleft -= nbytes;
else
if write returns with EAGAIN or EWOULDBLOCK
call poll or select to wait for the socket to be come ready
endif
endif
if poll or select times out
then handle the timeout error(e.g. the remote end did not send the
data within expected time interval)
endif
end while
The code also should include handle error conditions and read/write returning with (For example, write/read returning with 0). Also note read/recv returning 0 indicates the remote end closed the socket.

Sending data to and from using sockets in c

I am working on an program for school and having some issues with sockets. I have pasted the write and read commands from my program below since I think these are the problem. The program should take the plaintext file and encrypt it using the key provided.
MY PROBLEM: When I execute the program using "client [plaintext] [key] [port]" the program returns "Reading data from client -- 140 bytes" and then just hangs. I can hit ctrl-c and the program prints the correct output for ptext and ktext and that 37 bytes were sent back to the client (which is the correct number of bytes). I feel like the encrypted text should print as well but it does not.
TWO QUESTIONS:
1) Why does the program hang?
2) Why does it seem like data is written from the server to the client but the client does not read any of the data?
Thank you in advance for any help you can offer.
CLIENT
n = write(sockfd,ptext,strlen(ptext));
bzero(crypt_text, BUF_MAX);
bzero(buffer, BUF_MAX);
while((n = read(sockfd,buffer,BUF_MAX))>0){
printf("Reading data from Server -- %d bytes\n",n);
strcat(crypt_text, buffer);
bzero(buffer,BUF_MAX);
}
if (n < 0){
error("ERROR reading from socket");
}
printf("%s", crypt_text);
SERVER
while((n = read(newsockfd,buffer,512))>0){
printf("Reading data from client -- %d bytes\n",n);
strcat(full_text, buffer);
bzero(buffer,BUF_MAX);
}
if (n < 0){
error("ERROR reading from socket");
}
bzero (ptext,BUF_MAX);
bzero (ktext, BUF_MAX);
strcpy(ptext, strtok(full_text,"["));
strcpy(ktext, strtok(NULL, "["));
printf("ptext length ==%s %d\n\n",ptext,strlen(ptext)); //Prints the correct plain text
printf("ktext length ==%s %d\n\n",ktext,strlen(ktext)); //prints the correct key
crypt_text = encrypt(ptext, ktext);
n = write(newsockfd,crypt_text,strlen(crypt_text));
printf("WRITE TO CILENT ==== %d",n); //This returns the correct number of bytes that should be sent back to client
if (n < 0){
error("ERROR writing to socket");
}
As is, your client and server will always hang waiting for each other. This is because read() blocks by default until new data is available to fetch from the file (in this case, a socket).
Look carefully at the code:
The client writes once into the socket before entering the read loop
The server only reads from the socket (well, further down there is a write(), but it will never reach it). The first time the loop runs on the server, it will read the data that the client initially wrote into the socket.
The server processes the data it just read and concatenates it to full_text. Then it goes back to the loop condition, where it calls read() again. read() blocks because there is nothing else to read from the socket at this point.
The client enters a similar loop where it attempts to read from the socket, expecting messages from the server.
At this point, both the server and the client are blocked waiting for messages from each other, which will never happen.
Tu put it another way: you only wrote to the socket once, and somehow you expect to read it multiple times.
You have to rethink your design. Go back to the problem description, work your way through a simple protocol, dry run it on paper, and then implement it - that's how it's done in the real world :)
There are other bugs in your code. For example you write this:
strcat(full_text, buffer);
But buffer is not NUL terminated. n bytes have been read, the rest of the buffer is indeterminate. You should set a '\0' byte at offset n and only try reading BUF_MAX-1 bytes to keep a byte available for all cases and do this:
buffer[n] = '\0';
strcat(full_text, buffer);
Furthermore, you do not test if there is enough room available in full_text for the n+1 bytes strcat will copy at the end.
On another front, packets can be sliced and diced into chunked of different sizes when received by the server. Buffering is required to ensure a reliable client / server communication. To enable this buffering, you need to devise a protocol to determine when a packet has been fully received: a simple protocol is to transmit lines terminated by '\n'.

C server weird behavior

So, I have this piece of code, which will just read the message from the client and reply with a "yup"
while(strcmp(buffer, "QUIT") != 0){
bzero(buffer, 255); //cleans the read buffer
/*this reads the aux (request) from the client*/
recv(newsockfd, buffer, 255, 0);
if(strlen(buffer))
printf("Thread %d: %s\n", thread_no, buffer);
fflush(stdout);
write(newsockfd, "yup\n", 4);
}
The problem is that at the very first reading everything goes ok, but all other readings are messed up, if I send the message "guitar", for example, it gets the 'g', loops and then it gets the "uitar", sending aother "yup".
I have no clue what's happening.
Long story short: TCP isn't a message orientated protocol, it's a stream orientated protocol. Messages might be fragmented or merged together, and your application has to deal with that (the only guarantee is that you'll receive the data in the same order you sent it in).
recv() reads as many data as are available currently. You should read until you hit EOF, an error or a newline. Only if you have that newline, you have a complete line which you then compare with "QUIT", and which youi acknowledge with "Yup."
Three other bugs in the code snippit above.
1) Not checking the return value from recv(). The socket could have been closed gracefully (return value == 0), suffered an error (return value == -1), etc... More importantly, there's not to suggest that you will receive all 4 bytes of the "QUIT" message that was sent by a remote send call. You may only receive "Q".
2) If recv() were to receive 255 characters (none of which being a null char), then the subsequent strlen() will read into invalid memory and possibly crash.
3) Not checking the return value of the write call.

Resources