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?
Related
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");
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
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.
I don't understand why function read always return -1. I want to read from socket until '\n' appear!
char* msg = (char*)malloc(sizeof(char)*120);
nleft = sizeof(msg);
while(nleft>0){
n = read(fdTcp, msg, nleft);
if(n == -1){
printf("error reading UPC\n");
exit(1); //error
}
else if (n == 0){
printf("end of reading EOF\n");
break; //closed by peer
}
nleft-=n;
msg += n;
}
nread = n-nleft;
msg[nread] = '\0';
printf("mensagem do CS: %s\n", msg);
Thanks in advance!
char* msg = (char*)malloc(sizeof(char)*120);
nleft = sizeof(msg);
Since msg is a char*, nleft will be the number of bytes in a char*. I don't think that's what you want.
As for answering your real question:
but I don't now how many bytes I will read, I want to ready until I reach '\n' how can I do it?
You have two choices. The terrible option is to read one byte at a time until you read a newline. The better option is to read as much as you can and check for a newline. If you read past the newline, great, that's just less work you'll have to do on your next pass. In pseudo-code:
If there is not at least one newline in the buffer, skip to step 5.
Extract the bytes up to the first newline from the buffer and process them.
Move any bytes past the newline to the beginning of the buffer and adjust the size of the buffer to include just those bytes.
Go to step 1.
Do a blocking read and append the data to the buffer.
Go to step 1.
I assume your socket successfuly open socket connection.
Then file discriptor is 1 that means that ready ro read data. You tried to read socket but you took an error with -1.
Generally this error appear when the socket was closed. If you took -1, socket was closed. If took 0 that means timeout for read socket data. On the other hand if you took more than 0 this means, read how much bytes.
The server that Im working on (which is a Unix C multi-threaded non-block socket server) need to receive a file from a client and broadcast it to all the other clients connected to the server.
Everything is working at the exception that Im having a hard time to determine when a file is done transferring... since Im using non-block socket Im having the issue that sometimes during the file transfer recv return -1 (which I was assuming was the end of the file) then the next pass more bytes comes in.
I try to hack the whole thing putting "END" at the end of the stream. However, sometimes when multiple files are sent in a row the "END" is part of the same recv buffer as the beginning of the next file. Or even worst, sometimes I end up with a buffer that finish with EN and the next pass the D comes in.
What would be the best approach to avoid the situations mentioned above, I don't really want that each time I receive some bytes from the socket loop the whole accumulated buffer to check if "END" is part of it then cut appropriately... Im sure there's a better solution to this right?
Thanks in advance!
If recv() returns -1 it is an error and you need to inspect errno. Most probably it was EAGAIN or EWOULDBLOCK, which just means there is no data currently in the socket receive buffer. So you need to re-select().
When recv() returns zero the peer has disconnected the socket and the transfer is complete.
Signaling the end of a file with some byte sequence is not reliable, the file could contain that sequence. First send the file length - 4 bytes or 8 if you allow huge file transfer, use network byte order.
if ((n = read(..., filelen)) > 0) {
filelen -= n;
}
The most simpe case EJP is referring to, the case where you take the closing of the socket by the other end as end-of-file, could look like the following:
{
ssize_t sizeRead = 0;
while (sizeRead = recv(...)) {
if (0 > sizeRead) { /* recv() failed */
if ((EGAGAIN == errno) ¦¦ (EWOULDBLOCK == errno)) { /* retry the recv() on those two kinds of error */
usleep(1) /* optional */
continue;
}
else
break;
}
... /* process the data read ... */
}
if (0 > sizeRead) {
/* There had been an error during recv() */
}
}