Reputation: 127
Is it possible to receive data from one client and send data to some other client at the same time using the select()
function? How do I implement the timeout? How do I send and receive data from more than one client at the same time? I can understand how it can be done using fork()
, but select()
is confusing me.
P.S. Please forgive me if this question is not up to the level of Stack Overflow, but I did not find any good tutorial on select()
and I am new to the socket programming world. Please help me out.
Upvotes: 1
Views: 2397
Reputation: 73041
Is it possible to receive data from one client and send data to some other client at the same time using the select() function?
Yes (note that this is not the same as your program being blocked within a send() call and a recv() call at the same time -- that is not possible with a single thread, but fortunately it's not necessary either. See my comment to John Bollinger's answer, above)
How do I implement the timeout?
The first question is, why do you want to implement a timeout? An efficient server does not need to depend on timeouts.
How do I send and receive data from more than one client at the same time?
Set all of your sockets to non-blocking mode, so that neither send() nor recv() will ever block; rather each will always return immediately. That way there is no way that poor network connectivity to client A will cause your server to block for a long time (inside a recv() or send() call) and to stop responding to client B.
Once you've done that, the only place your program should ever block/wait is inside the select() call. Set up your fd_set arguments so that select() returns whenever any of your sockets is ready-for-read, and also so that it returns whenever any of your sockets that you have data ready to send to is ready-for-write.
Once select() returns, use FD_ISSET() in a loop to find out which socket(s) have data ready for you to recv() and which are ready to accept some data from you (via send()).
Call recv() on each of the ready-for-read sockets to get some bytes of data from them. Be aware that recv() may hand you any number of bytes less than or equal to the number you asked it to give you, and it may even return EWOULDBLOCK (if for some reason it didn't have any bytes to give you after all).
Call send() on each of the ready-for-write sockets to send some more bytes to them. Again, be aware that send() may accept fewer bytes than you passed to it (its return value will tell you how many bytes it actually copied from your buffer into a kernel buffer), and it may also return EWOULDBLOCK (if for some reason it couldn't accept any bytes from you right now).
Then just repeat all of the above in a loop, and that's your single-threaded/multi-client server's event loop. Note that this design requires you to keep send and receive buffers and explicit state variables for each connection to keep track of how many bytes are currently sent/received of the message you are currently sending/receiving, so it's a bit more work than the alternative (blocking) model where the blocking calls allow you use the thread's execution-location to help hold that state; but the upside is that you can have a single thread service many connections at once without the connections interfering with each other.
Upvotes: 1
Reputation: 180048
To literally perform two actions at the same time, you need two threads (possibly, but not necessarily, belonging to different processes) running on a machine that has at least two CPU cores. (In the separate process case you you don't need to explicitly manage threads; you can just rely on the processes' default threads.) That's not what select()
is for. In fact, select()
is mainly for avoiding that.
The select()
function helps [a single thread of] a single process to efficiently service I/O over multiple channels by allowing it to recognize which channels are ready for reading and/or writing at any given time. The program using select()
queues up pending I/O requests, or otherwise knows somehow what I/O is needed, and loops, performing each operation only when select()
informs it that the target I/O device is ready. That allows it both to avoid blocking on I/O to devices that are unready while others are waiting for service, and to avoid wasting CPU by continuously polling devices to determine whether they are ready.
Upvotes: 1