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.
Related
I have a client server client connection where the server reads the message sent by the client every 1 second but I do not want the server to keep on waiting for a message for too long. I tried using the select() function but the server continues waiting for some message to read. Could anyone tell me what I am doing wrong please?
fd_set master;
fd_set read_fds;
FD_ZERO(&master);
FD_ZERO(&read_fds);
FD_SET(sock, &master);
while (1) {
bzero(message, 256);
sleep(1);
read_fds = master;
if(select(sock+2, &read_fds, NULL, NULL, NULL) < 0)
error("ERROR reading");
//if there is any data to read from the socket
else if(FD_ISSET(sock, &read_fds)){
n = read(sock, buffer, 256);
c = buffer[0];
printf("1st char is %c",c);
}//close else if statement
else printf("Nothing was read");
}//close while loop
A few comments that are too long to fit in the comments...
The first parameter to select really only needs to be sock+1.
By passing NULL for the timeout, select will block indefinitely (so you might as well have just called read).
When select does tell you that sock is ready for reading, there may only be one byte present, even if the other end wrote more then that. You will have to loop, reading the socket until you get the number of bytes you want. You will have to decide if you want the timeout only while waiting for the first byte, or if you want to timeout in the middle (I.e. is the select inside or outside the loop).
Normally, when using select, you only want to block in select, and not in read, so the socket should be put in non-blocking mode, and you should be prepared for the read to fail with EWOULDBLOCK.
If you do time out, do you want to close the connection? If you got half way through a message, you can't just throw away the half and keep going because you would now be expecting the next byte to be another start of message when it will now be a middle.
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 having issues with my client side implementation of client server chat program where multiple clients connect. The issue is that i'm coming across is that how exactly should i be sending (chat message to another client) and receiving (chat message from another client) at the same time? What's happening is that i'm always sending data and never reading. Do i need to fork and have one read and the other send?
here is the relevant code
client side
while(1) {
fd_set rfds, wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(serverSocket, &rfds);
FD_SET(serverSocket, &wfds);
if(select(serverSocket+1, &rfds, &wfds, NULL, NULL) < 0) {
perror("select");
exit(-1);
}
if (FD_ISSET(serverSocket, &rfds)) {
// we got data, read it
}
if (FD_ISSET(serverSocket, &wfds)) {
printf(">");
// read keyboard
sendLen = 0;
while ((cmd[sendLen] = getchar()) != '\n')
sendLen++;
cmd[sendLen] = '\0';
// send the data
}
}
You should put the file descriptor 0 (standard input) in the select as well, then read chars and buffer them, and when the socket is available for writing, copy the entire buffer on it. In this way you just block reading on the standard input all the time.
add
FD_SET(0, &rfds);
so select will return when user types something as well.
you must also use fcntl to set stdin as non-blocking.
Then everytime select tells you there's data on stdin do something like that:
while(read(0,buffer+filled,1)>0) {}
Make sure to put another condition to exit the loop if the buffer is full.
then when you can write on the socket do a send, of the size of the amount of bytes you have in your buffer, check if all of it has been written, or move the leftovers bytes at the beginning of the buffer.
That while(getchar()) is blocking you preventing you from receving any messages.
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() */
}
}
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?