我的服务器程序只在第二个请求到达时才回答第一个请求?

My server program only answers the first request when a second one arrives?

我正在尝试通过编写服务器-客户端原型程序来学习套接字编程。

服务器和客户端都需要能够处理来自stdin的命令,所以我使用select函数。

我的问题是 server 程序被阻止并仅在客户端发送另一个请求后才响应客户端请求。

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); 
    }
}

你能给我指明正确的方向吗?我是否遗漏了一些关于 sendrecv 行为的信息?

要使用 select 作为正确的 IO 多路复用工具,您需要正确维护 FD_SET。由于每次 select returns,FD_SET 仅包含准备好操作的 fds,这意味着您必须在调用 [=11= 之前重新装备 FD_SET ] 每一次。

你的代码还有一个问题,你不能只在循环中的 FD_SET 中添加新客户端,你需要保存它然后在开始时重新装备它们。

此外,您不需要检查集合中的每个 FD,因为 select 将 return 准备好 IO 的 fd 的数量。

尝试以下更改:

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);
            }
        }
    }
}

最后但同样重要的是,作为对 select 的改进,在 Linux 上尝试类似 epoll 的东西,它会为您维护状态,这样您就不需要像 select 那样重新武装所有 fds。