我的服务器程序只在第二个请求到达时才回答第一个请求?
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);
}
}
你能给我指明正确的方向吗?我是否遗漏了一些关于 send
和 recv
行为的信息?
要使用 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。
我正在尝试通过编写服务器-客户端原型程序来学习套接字编程。
服务器和客户端都需要能够处理来自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);
}
}
你能给我指明正确的方向吗?我是否遗漏了一些关于 send
和 recv
行为的信息?
要使用 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。