我正在尝试编写一个简单的套接字服务器,为了这个例子,它是一个echo服务器。
这是socket_server.c(注意关于它被卡住的评论)
int fdmax, i, socket_descriptor;
fd_set master, read_fds;
struct timeval tv = {.tv_sec = 0, .tv_usec = 1000};
void socket_server_init(void)
{
FD_ZERO(&master);
FD_ZERO(&read_fds);
}
socket_server_socket socket_server_start(char *socket_path)
{
struct sockaddr_un local;
int len;
int socket_descriptor = socket(AF_UNIX, SOCK_STREAM, 0);
if (socket_descriptor == -1)
return (socket_server_socket) {.status = SOCKET_SERVER_START_SOCKET_FAILURE, .descriptor = -1};
local.sun_family = AF_UNIX;
strcpy(local.sun_path, socket_path);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(socket_descriptor, (struct sockaddr *)&local, len) == -1)
return (socket_server_socket) {.status = SOCKET_SERVER_START_BIND_FAILURE, .descriptor = -1};
if (listen(socket_descriptor, 5) == -1)
return (socket_server_socket) {.status = SOCKET_SERVER_START_LISTEN_FAILURE, .descriptor = -1};
FD_SET(socket_descriptor, &master);
fdmax = socket_descriptor;
return (socket_server_socket) {.status = 0, .descriptor = socket_descriptor};
}
socket_server_socket socket_server_wait_for_connection(socket_server_socket server)
{
read_fds = master;
select(fdmax+1, &read_fds, NULL, NULL, &tv);
for (i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == server.descriptor)
{
// It's getting stuck here.
socket_descriptor = accept(server.descriptor, (struct sockaddr *) NULL, NULL);
if (socket_descriptor > fdmax)
fdmax = socket_descriptor;
FD_SET(socket_descriptor, &master);
return (socket_server_socket) {.status = -1, .descriptor = socket_descriptor};
} else {
return (socket_server_socket) {.status = 0, .descriptor = i};
}
}
}
return (socket_server_socket) {.status = -1, .descriptor = -1};
}
int socket_server_update(socket_server_socket client)
{
char buffer[256];
int n = recv(client.descriptor, buffer, 256, 0);
if (n < 0)
return SOCKET_SERVER_UPDATE_RECV_FAILURE;
if (send(client.descriptor, buffer, n, 0) < 0)
return SOCKET_SERVER_UPDATE_SEND_FAILURE;
close(client.descriptor);
return 0;
}
然后在我的主程序中:
socket_server_init();
socket_server_socket server = socket_server_start(SOCKET_PATH);
while (1) {
printf("wait for conn\n");
socket_server_socket client = socket_server_wait_for_connection(server);
if (client.status == 0)
{
socket_server_update(client);
}
sleep(1);
printf("%d: Log !\n", (int)time(NULL));
}
当我运行程序时,我观察到:
我希望循环继续“Log!”继续输出,但看起来我的节目卡在accept
电话,但只是第二次。
据我了解,如果需要接受或重新获取,select
仅应将描述符添加到read_fds
。所以似乎发生的事情是:
select
将服务器的描述符添加到read_fds
select
再次将服务器描述符添加到read_fds
accept
编辑所以accept
挂起我已经验证了在达到accept
调用时的两次,描述符都是相同的。所以我很困惑。
我做错了什么?我确信不应该再次accept
同一个连接。
答案 0 :(得分:1)
你使用超时,如果超时,select(2)
将返回0,你的程序不关心它的返回值并将其视为一个成功的调用(&#34;我们有一些东西到阅读&#34;),但read_fds
尚未更新。因此,它仍然保持其先前的价值(&#34;我们有一个新客户&#34;),然后,您致电accept
并陷入困境。
解决方案非常简单:查看select(2)
的返回值:
int result = select(fdmax+1, &read_fds, NULL, NULL, &tv);
if (result < 0) {
// Handle error.
} else if (result == 0) {
// Handle timeout.
}
要详细了解unix中的select
和套接字,我强烈建议您阅读Beej's Guide to Network Programming。