I am trying to learn about socket programming by writing a server-client prototype program.
Both the server and the client need to be able to handle commands from stdin
, so I am using the select
function.
My problem is that the server
program gets blocked and answers a client request only after the client sends another request.
server.c
while(1)
{
if (select(maxfd + 1, &tmpfds, NULL, NULL, NULL) == -1)
{
error("Err in select");
}
for (i = 0; i < maxfd; i++)
{
if(FD_ISSET(i,&tmpfds)
{
if (i == listenfd)
{
< add new client to list >
}
else if (i == 0)
{
< parse server commands >
}
else
{
/* This is where I think my problem is */
recv(i, buffer, BUFLEN, 0);
process(buffer);
send(i, buffer, BUFLEN, 0);
}
}
}
client.c
while(1)
{
if (select(fdmax + 1, &tmpfds, NULL, NULL, NULL) == -1)
{
error("Err in select");
}
if (FD_ISSET(0,&tmpfds))
{
fgets(buffer, BUFLEN, stdin);
process_request(buffer);
send(serverfd, buffer, BUFLEN, 0);
}
else if (FD_ISSET(serverfd,&tmpfds))
{
recv(serverfd, buffer, BUFLEN, 0);
process_response(buffer);
}
}
Could you please point me in the right direction ? Am I missing something about how send
and recv
behave?
To use select
as the correct IO multiplexing facility, you need maintain the FD_SET
correctly. Since each time select
returns, the FD_SET
contains only the fds that are ready for the operation, that means you have to rearm the FD_SET
before calling select
each time.
There is another problem in you code, you cannot just add new client in the FD_SET
in the loop, you need to save it and then rearm them in the beginning.
Also, you don't need to check each FD in the set, since select
will return the number of fd that are ready for IO.
Try the following changes:
int clients[MAX_CLIENTS] = {0};
int I;
int maxfd;
int server_sock = <the listening fd>;
FD_SET readfds;
int ret;
while(1) {
// Setup SD_SET each time calling select
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
maxfd = STDIN_FILENO;
FD_SET(server_sock, &readfds);
maxfd = max(maxfd, server_sock);
for (I = 0; I < MAX_CLIENTS; I++) {
if (clients[I] >= 0) {
FD_SET(clients[I], &readfds);
maxfd = max(maxfd, clients[I]);
}
if ((ret = select(maxfd+1,&readfds,NULL,NULL,NULL)) == -1) {
error("Err in select");
}
for(i = 0; i < maxfd && ret; i++, ret--) {
if(FD_ISSET(i, &readfds) {
if (i == listenfd) {
// < add new client to clients array
}
else if (i == STDIN_FILENO) { /* keyboard input */
// < parse server commands >
}
else {
// one of the client is ready
int nread = recv(i,buffer,BUFLEN,0);
if (nread == 0) {
// client is closed, remove I from clients array
continue;
}
process(buffer);
send(i,buffer,BUFLEN,0);
}
}
}
}
Last but not least, as an improvement over select
, try something like epoll
on Linux, which maintains the state for you, so that you don't need to rearm all the fds like select
does.