Unix Programming - select(), sending and receiving data

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > December 2005 > select(), sending and receiving data





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author select(), sending and receiving data
Markus Pitha

2005-12-26, 7:48 am

Hello,

I'm trying to write a little server and a corresponding client. The
server runs without any problems. I tried to connect with telnet and the
telnet client was able to receive the "Welcoming messages" of the server
as well as I could see the typed data of the client appearing on the
server stdout, as it was intended.
Unfortunately I have problems with my client. I am able to send data to
the server, but I can not receive any data. After my research I came to
the conclusion that select() would be able to handle this, but obviously
I did do something wrong.
Can anybody tell me why I can not receive any data from the server? (So
recv() doesn't work in the program, but send() works)
Here is the client script:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>


int main(int argc, char *argv[]) {
int sock, bytes;
struct sockaddr_in srv;
char stdinbuffer[256];
fd_set readfds, writefds;
struct timeval tv;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
tv.tv_sec = tv.tv_usec = 0;

if (argc != 3) {
fprintf(stderr, "usage: %s host port\n", argv[0]);
return 1;
}

if ( (sock = socket(AF_INET, SOCK_STREAM, 0) ) == -1 )
perror("socket failed()");

srv.sin_family = AF_INET;
srv.sin_addr.s_addr = inet_addr(argv[1]);
srv.sin_port = htons( (unsigned short int) atol(argv[2]) );

if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) == -1)
perror("connect failed()");
FD_SET(sock, &readfds);
FD_SET(sock, &writefds);

while (1) {
select(sock+1, &readfds, &writefds, NULL, &tv);

if (FD_ISSET(sock, &readfds) ) {
while (bytes = recv(sock, stdinbuffer, sizeof(stdinbuffer), 0 ) ) {
if (bytes == -1)
perror ("recv() in FD_ISSET");
stdinbuffer[bytes] = '\0';
printf("%s", stdinbuffer);
}
}

if (FD_ISSET(sock, &writefds) ) {
printf("> ");
fgets(stdinbuffer, 255, stdin);
if (send(sock, stdinbuffer, strlen(stdinbuffer), 0 ) == -1)
perror("Error sending to socket within FD_ISSET");
if (stdinbuffer[0] == '\n')
break;
}

}
close(sock);

return 0;
}
Pascal Bourguignon

2005-12-26, 7:48 am

Markus Pitha <markus@pithax.net> writes:

> Hello,
>
> I'm trying to write a little server and a corresponding client. The
> server runs without any problems. I tried to connect with telnet and the
> telnet client was able to receive the "Welcoming messages" of the server
> as well as I could see the typed data of the client appearing on the
> server stdout, as it was intended.
> Unfortunately I have problems with my client. I am able to send data to
> the server, but I can not receive any data. After my research I came to
> the conclusion that select() would be able to handle this, but obviously
> I did do something wrong.
> Can anybody tell me why I can not receive any data from the server? (So
> recv() doesn't work in the program, but send() works)
> Here is the client script:


You need to let your editor indent correctly your code.
You need to reset the fd sets everytime.
You need to flush the output.
You need to avoid waiting for received packets (unless you mean it, it
depends on your protocol).


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>

int main(int argc, char *argv[]) {
int sock, bytes;
struct sockaddr_in srv;
char stdinbuffer[256];
fd_set readfds, writefds;
struct timeval tv;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
tv.tv_sec = tv.tv_usec = 0;

if (argc != 3) {
fprintf(stderr, "usage: %s host port\n", argv[0]);
return 1;
}

if ( (sock = socket(AF_INET, SOCK_STREAM, 0) ) == -1 ){
perror("socket failed()");
exit(1);
}

srv.sin_family = AF_INET;
srv.sin_addr.s_addr = inet_addr(argv[1]);
srv.sin_port = htons( (unsigned short int) atol(argv[2]) );

if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) == -1){
perror("connect failed()");
exit(1);
}

while (1) {
FD_ZERO(&readfds); FD_SET(sock, &readfds);
FD_ZERO(&writefds); FD_SET(sock, &writefds);
select(sock+1, &readfds, &writefds, NULL, &tv);

if (FD_ISSET(sock, &readfds) ) {
while((bytes=recv(sock,stdinbuffer,sizeo
f(stdinbuffer)-1,
MSG_DONTWAIT))>0){
stdinbuffer[bytes] = '\0';
printf("%s", stdinbuffer);fflush(stdout);
}
if (bytes == -1){
perror ("recv() in FD_ISSET");
}
}

if (FD_ISSET(sock, &writefds) ) {
printf("> ");fflush(stdout);
fgets(stdinbuffer, 255, stdin);
if (send(sock, stdinbuffer, strlen(stdinbuffer), 0 ) == -1)
perror("Error sending to socket within FD_ISSET");
if (stdinbuffer[0] == '\n')
break;
}

}
close(sock);

return 0;
}


Instead of block reading stdin, you could use select on it too, which
would allow you to display incoming packets while the user types in
his new message. (Some management of the screen would be needed,
ncurses or at least manage raw input to be able to print back the
current user input).


[pjb@thalassa tmp]$ gcc -o a a.c
[pjb@thalassa tmp]$ ./a 127.0.0.1 25
220 thalassa.informatimago.com ESMTP Postfix
recv() in FD_ISSET: Resource temporarily unavailable
> HELO ici
> MAIL FROM: <pjb@informatimago.com>

250 thalassa.informatimago.com
recv() in FD_ISSET: Resource temporarily unavailable
> RCPT TO: <pjb@informatimago.com>

250 Ok
recv() in FD_ISSET: Resource temporarily unavailable
> DATA

250 Ok
recv() in FD_ISSET: Resource temporarily unavailable
> Subject: Test

354 End data with <CR><LF>.<CR><LF>
recv() in FD_ISSET: Resource temporarily unavailable
>

[pjb@thalassa tmp]$


--
__Pascal Bourguignon__ http://www.informatimago.com/

CAUTION: The mass of this product contains the energy equivalent of
85 million tons of TNT per net ounce of weight.
Markus Pitha

2005-12-26, 7:48 am

Hello,

Pascal Bourguignon wrote:
> You need to let your editor indent correctly your code.


Sorry, but when I copy and paste it from the editor in here, it's
probably much more messy as when I align it on the left side.

> You need to reset the fd sets everytime.


Ok

> You need to flush the output.


Ok, I'll keep it in my mind.

> You need to avoid waiting for received packets (unless you mean it, it
> depends on your protocol).


Actually that's what I want to do. It should be more a Chat server
instead of a http server.

> Instead of block reading stdin, you could use select on it too, which
> would allow you to display incoming packets while the user types in
> his new message. (Some management of the screen would be needed,
> ncurses or at least manage raw input to be able to print back the
> current user input).


That's what I want to try next, but I want to make it step by step.

By the way. Your code seems to work, thanks for it. But there is
something I don't understand:
When I connect to the server, I don't get the Welcoming Message, but the
">"-sign of the code appears, which means that I'm able to type and send.
When I write the first sentence and send it, I finally get the welcoming
message after this first sentence, but moreover, the errormessage
"recv() in FD_ISSET: Resource temporarily unavailable" appears.
Probably there is still something wrong in my code, but I think I can
find the mistake by myself.

Best Regards,
Markus
Markus Pitha

2005-12-26, 5:56 pm

Hi again,

I tried the whole day to manage this problem, but nothing worked.
I don't understand, how FD_SET is to use. FD_SET is something I have to
set or not, but when I set it, how should I now, if the server sends
data or not in this moment. It's only possible either to set
FD_SET(sock, &readfds); or FD_SET(sock, &writefds); but never both. I
don't understand the weird behaviour of the program, because anything
must me wrong there. I still have to type at least one character to
receive the message of the server. Then I'm able to type again.
Usually I should immediately receive the message after the connection to
the server.
Nils O. Selåsdal

2005-12-26, 5:56 pm

Markus Pitha wrote:
> Hi again,
>
> I tried the whole day to manage this problem, but nothing worked.
> I don't understand, how FD_SET is to use. FD_SET is something I have to
> set or not, but when I set it, how should I now, if the server sends
> data or not in this moment. It's only possible either to set
> FD_SET(sock, &readfds); or FD_SET(sock, &writefds); but never both. I

Ofcourse you can put it in both sets.

Remember also that select modifies the descriptor sets, be sure
you initialize the sets again on each iteration.

> don't understand the weird behaviour of the program, because anything
> must me wrong there. I still have to type at least one character to
> receive the message of the server. Then I'm able to type again.
> Usually I should immediately receive the message after the connection to
> the server.

Markus Pitha

2005-12-26, 5:56 pm

Hello,

Nils O. Selåsdal wrote:
> Remember also that select modifies the descriptor sets, be sure
> you initialize the sets again on each iteration.


Actually I tried a lot of ways with setting and clearing the sets but I
always go in circles:
When I only set FD_SET(sock, &readfds); (and not set FD_SET(sock,
&writefds);) I receive the welcoming message of the server, but then I
can never write. When I switch on both, I can write, but I don't receive
the welcoming message at the beginning of the connection. It doesn't
appear until I pressed ENTER the first time. (after I typed something).
I think my main problem is that I dont know, how to control the SETs
with the flags and return values.
Alex Fraser

2005-12-26, 5:56 pm

"Markus Pitha" <markus@pithax.net> wrote in message
news:269d6$43b02c65$54705512$12599@news.chello.at...
> I tried the whole day to manage this problem, but nothing worked.
> I don't understand, how FD_SET is to use. FD_SET is something I have to
> set or not, but when I set it, how should I now, if the server sends
> data or not in this moment. It's only possible either to set
> FD_SET(sock, &readfds); or FD_SET(sock, &writefds); but never both. I
> don't understand the weird behaviour of the program, because anything
> must me wrong there. I still have to type at least one character to
> receive the message of the server. Then I'm able to type again.
> Usually I should immediately receive the message after the connection to
> the server.


The first select() call probably tells you that the socket is writeable but
not readable - the "welcome message" hasn't arrived yet. So you call
fgets(), which doesn't return until you press the return key. Then you call
send(), and go back round to select() which will now tell you the socket is
readable (and probably writeable too).

BTW, you don't set the socket to non-blocking mode. You should (use
fcntl()), although you'll usually not notice any problem unless you try to
send a significant amount of data faster than the receiver is receiving it.

Alex


Markus Pitha

2005-12-26, 5:56 pm

Hello,

Alex Fraser wrote:
> The first select() call probably tells you that the socket is writeable but
> not readable - the "welcome message" hasn't arrived yet. So you call
> fgets(), which doesn't return until you press the return key. Then you call
> send(), and go back round to select() which will now tell you the socket is
> readable (and probably writeable too).


But how does select() know which one should be readable or writeable?
That's what I want to find out all the time, because the programmer is
the only one who can tell the program when it is to read or to write
when I understood that right. fgets() will only be executed when "if
(FD_ISSET(sock, &readfds))" is true, but it is only true, when I set it
with FD_SET(sock, &readfds);, so I go in circles again. I can't set both
at one time, or I can never know when I have to set anything. (How
should I know if the server is sending).


> BTW, you don't set the socket to non-blocking mode. You should (use
> fcntl()), although you'll usually not notice any problem unless you try to
> send a significant amount of data faster than the receiver is receiving it.


I thought I would set to blocking or non blocking mode with the SETs? So
does that mean that I can't solve my problem without using fcntl()?
Pascal Bourguignon

2005-12-26, 8:49 pm

Markus Pitha <markus@pithax.net> writes:

> Hello,
>
> Pascal Bourguignon wrote:
>
> Sorry, but when I copy and paste it from the editor in here, it's
> probably much more messy as when I align it on the left side.


I don't understand. You mean you don't read and write news from the
same editor you write programs? Try emacs!


>
> Ok
>
>
> Ok, I'll keep it in my mind.
>
>
> Actually that's what I want to do. It should be more a Chat server
> instead of a http server.


A chat server doesn't wait for received packets. I mean, block wait.


>
> That's what I want to try next, but I want to make it step by step.
>
> By the way. Your code seems to work, thanks for it. But there is
> something I don't understand:
> When I connect to the server, I don't get the Welcoming Message, but the
> ">"-sign of the code appears, which means that I'm able to type and send.


Yes, this is time dependant. Either the read or the write is ready
first. (Either a packets arrives nor not before you reach the select call).

If your protocol calls for a first welcome message from the server,
then you must implement a read first (and the protocol probably
specifies some timeout for it to arrive).



> When I write the first sentence and send it, I finally get the welcoming
> message after this first sentence, but moreover, the errormessage
> "recv() in FD_ISSET: Resource temporarily unavailable" appears.


This will always happen, for non-blocking recv, when there is no more
data. The return code and errno sometimes signal mere conditions, not
always errors. Actually, error is too strong a term since it depends
on the caller if a condition should be called an "error".


> Probably there is still something wrong in my code, but I think I can
> find the mistake by myself.


There's nothing wrong in the code. Now you need to do some analysis
and specification work. What protocol do you want to implement, what
user interface, etc.

--
__Pascal Bourguignon__ http://www.informatimago.com/

"Our users will know fear and cower before our software! Ship it!
Ship it and let them flee like the dogs they are!"
Pascal Bourguignon

2005-12-26, 8:49 pm

Markus Pitha <markus@pithax.net> writes:

> Hello,
>
> Nils O. Selåsdal wrote:
>
> Actually I tried a lot of ways with setting and clearing the sets but I
> always go in circles:
> When I only set FD_SET(sock, &readfds); (and not set FD_SET(sock,
> &writefds);) I receive the welcoming message of the server, but then I
> can never write. When I switch on both, I can write, but I don't receive
> the welcoming message at the beginning of the connection. It doesn't
> appear until I pressed ENTER the first time. (after I typed something).
> I think my main problem is that I dont know, how to control the SETs
> with the flags and return values.


Read again the man page of select(2) !

You must realize that fgets blocks. Instead of calling fgets when
there is no input from the user, you might want to put stdin on
readfds too.

loop
readfds={socks,fileno(stdin)}
writefds={socks,fileno(stdout)}
select
when stdin in readfds
fgets -> buffer.tosend
when stdout in writefds and buffer.toprint
printf buffer.toprint
when socks in readfds
recv -> buffer.toprint
when socks in writefds and buffer.tosend
send buffer.tosend

You should beware that stdin and stdout are buffered, and their
underlying file descriptors can be out of sync; use fflush and read
setbuf(3).

--
__Pascal Bourguignon__ http://www.informatimago.com/

"Our users will know fear and cower before our software! Ship it!
Ship it and let them flee like the dogs they are!"
Pascal Bourguignon

2005-12-26, 8:49 pm

Markus Pitha <markus@pithax.net> writes:

> Hello,
>
> Alex Fraser wrote:
>
> But how does select() know which one should be readable or writeable?
> That's what I want to find out all the time, because the programmer is
> the only one who can tell the program when it is to read or to write
> when I understood that right. fgets() will only be executed when "if
> (FD_ISSET(sock, &readfds))" is true, but it is only true, when I set it
> with FD_SET(sock, &readfds);, so I go in circles again. I can't set both
> at one time, or I can never know when I have to set anything. (How
> should I know if the server is sending).


This has to do with asynchronous I/O. When you send packets, they are
not received by the recipient even before the send call returns! The
data is put in buffers and the network stack process them at leasure,
asynchronously from your program. Even at 1Gb/s, a 1KB packet takes
1024.0*8/1e9 = 8.192 µs to be transmited, not counting any overhead,
time enough to execute more than 10,000 instructions on current
processors!

What select tells you is whether a packet has arrived and is ready to
be read, and whether there is space in the output buffers where you can
put more data to be sent.
It tells you that the next read or write won't block, so you'll be
able to call them and still have the processor to do the rest of your
processing loop.


>
> I thought I would set to blocking or non blocking mode with the SETs? So
> does that mean that I can't solve my problem without using fcntl()?


You can do it either call by call with the flags on recv and send, or
globally for all calls on the socket (including normal read and write)
with fcntl.

--
__Pascal Bourguignon__ http://www.informatimago.com/
Until real software engineering is developed, the next best practice
is to develop with a dynamic system that has extreme late binding in
all aspects. The first system to really do this in an important way
is Lisp. -- Alan Kay
Alex Fraser

2005-12-26, 8:49 pm

"Pascal Bourguignon" <spam@mouse-potato.com> wrote in message
news:87fyof5q4k.fsf@thalassa.informatimago.com...
> Markus Pitha <markus@pithax.net> writes:

[snip]
> What select tells you is whether a packet has arrived and is ready to
> be read, and whether there is space in the output buffers where you can
> put more data to be sent.
> It tells you that the next read or write won't block, so you'll be
> able to call them and still have the processor to do the rest of your
> processing loop.


select() cannot, and does not, tell you that the next read or write won't
block.

This is obviously true for a write (with write() or send()): how many bytes
can you write without blocking? That number can change, up (eg when data is
acknowledged by the remote peer) or down (eg the OS decides it needs the
memory for something else), between select() and write()/send(). Even if the
number doesn't change, what happens if you try to write more?

This is part of why, in general, it is a mistake to use blocking I/O calls
with select(). It's not much extra effort to set the descriptor to
non-blocking and handle EAGAIN/EWOULDBLOCK, so IMO there is no excuse.

Alex


David Schwartz

2005-12-27, 2:50 am


"Markus Pitha" <markus@pithax.net> wrote in message
news:269d6$43b02c65$54705512$12599@news.chello.at...

> I tried the whole day to manage this problem, but nothing worked.
> I don't understand, how FD_SET is to use. FD_SET is something I have to
> set or not, but when I set it, how should I now, if the server sends
> data or not in this moment. It's only possible either to set
> FD_SET(sock, &readfds); or FD_SET(sock, &writefds); but never both.


If a previous send blocked and the other end might send more data, it's
perfectly acceptable to set the same socket in both the read and write sets.

DS


David Schwartz

2005-12-27, 2:50 am


"Markus Pitha" <markus@pithax.net> wrote in message
news:5f25c$43b03910$54705512$30295@news.chello.at...

> But how does select() know which one should be readable or writeable?


You set a socket in either the read set, the write set, or both. Then
you call 'select'. When 'select' returns, you check if the socket is set in
the read set or the write set. If it's set in the read set, that means that
there's probably some data waiting for you to 'read' it and that calling
'read' would be a good idea. If it's set in the write set, that means that
there's probably some space in the write buffer and that calling 'write'
(with blocking off) will probably send some bytes.

You should set a socket in the read set any time the other end might
send data nad you want to know if you should try calling 'recv' or 'read'.
You should set a socket in the write set any time you want to know if there
might be space in the send buffer (usually because a previous call to
'write' or 'send' returned a 'would block indication).

> That's what I want to find out all the time, because the programmer is
> the only one who can tell the program when it is to read or to write


Well, you know when you need to write data. Many applications try to
read data all the time, although you can be selective and only check for
incoming data when the other side is supposed to be sending it. It is
generally a mistake to put a socket in the write set when you aren't trying
to send any data, because odds are there will be space in the write buffer
and 'select' will return immediately.

> when I understood that right. fgets() will only be executed when "if
> (FD_ISSET(sock, &readfds))" is true, but it is only true, when I set it
> with FD_SET(sock, &readfds);, so I go in circles again. I can't set both
> at one time, or I can never know when I have to set anything. (How
> should I know if the server is sending).


If the server might send, keep the socket in the read set. If you need
to send data, or a previous send blocked, then put the socket in the write
set. If both these things are true, put the socket in both sets.

> I thought I would set to blocking or non blocking mode with the SETs?


No. The 'select' operation acts the same if the socket is blocking or
non-blocking and has no effect on the status of the socket.

> So
> does that mean that I can't solve my problem without using fcntl()?


It depends what you're trying to do. You can rig things so that the
program works right most of the time with blocking sockets, but you will be
relying on behavior that is not guaranteed, which is not a good habit to get
into.

DS


Alex Fraser

2005-12-27, 2:50 am

"Pascal Bourguignon" <spam@mouse-potato.com> wrote in message
news:87k6dr5qgo.fsf@thalassa.informatimago.com...
[snip]
> loop
> readfds={socks,fileno(stdin)}
> writefds={socks,fileno(stdout)}
> select
> when stdin in readfds
> fgets -> buffer.tosend
> when stdout in writefds and buffer.toprint
> printf buffer.toprint
> when socks in readfds
> recv -> buffer.toprint
> when socks in writefds and buffer.tosend
> send buffer.tosend


A couple of simple rules: prior to select(), descriptors should be in
readfds if and only if the associated buffer has any space (is not full),
and descriptors should be writefds if and only if the associated buffer has
any data (is not empty). (Just to be clear, by "associated buffer" I mean
the buffer you would pass to read()/recv() or write()/send() with the
descriptor in question.)

Violating these rules, as you do above, may create a "busy loop". And in
this particular case, it is practically guaranteed to: stdout and the socket
will typically always be writeable, so select() will always return quickly.

Alex


Maxim Yegorushkin

2005-12-27, 7:49 am


Pascal Bourguignon wrote:

[]

> if (FD_ISSET(sock, &readfds) ) {
> while((bytes=recv(sock,stdinbuffer,sizeo
f(stdinbuffer)-1,
> MSG_DONTWAIT))>0){
> stdinbuffer[bytes] = '\0';
> printf("%s", stdinbuffer);fflush(stdout);
> }
> if (bytes == -1){
> perror ("recv() in FD_ISSET");
> }
> }


Should not it be:

if (bytes == -1 && EWOULDBLOCK != errno)

Markus Pitha

2005-12-27, 6:06 pm

Hello,

thank you all. I'll try to use this information to solve the issue now.

Regards,
Markus
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com