所以,我遇到了一个问题,我试图做一个简单的c服务器-客户端,基本上是一个客户端连接到服务器,时不时地服务器ping客户端,不应该这么复杂,但我遇到了一个奇怪的行为从“选择”,“发送”和“接收”。
服务器代码(没有整个连接部分)基本上是这样的:
while (1) {
read_set = set; write_set = set; error_set = set; timeout = ref_timeout;
int result = select(NULL, &read_set, &write_set, &error_set, &timeout); //I'm on windows that's why the first parameter is null
if (result < 0) {
puts("Error select");
return 1;
}
if (result == 0) puts("timed out");
else {
for (int index = 0; index < 2; index++) {
SOCKET i = sockets[index];
if (FD_ISSET(i, &error_set)) {
printf("%zu error\n", i);
FD_CLR(i, &set);
closesocket(i);
}
if (FD_ISSET(i, &write_set)) {
const char* ping = "PING";
int size = send(i, ping, strlen(ping)+1, 0);
printf("%d bytes sent\n", size);
if (size < 0) {
puts("Error send");
closesocket(i);
FD_CLR(i, &set);
}
send(i, NULL, 0, 0);
}
if (FD_ISSET(i, &read_set)) {
if (i == listen_socket){
struct sockaddr addr;
int addrlen;
SOCKET new_socket = accept(listen_socket, &addr, &addrlen);
FD_SET(new_socket, &set);
if (new_socket > biggest) biggest = new_socket;
printf("new conn %zu\n", new_socket);
sockets[1] = new_socket;
}
else {
char msg[100];
memset(msg, 0, sizeof(msg));
if (recv(i, msg, sizeof(msg), 0) < 0) {
closesocket(i);
FD_CLR(i, &set);
}
else {
printf("%zu %s\n", i, msg);
}
}
}
}
}
}
客户端代码(不包括连接部分)是:
char buffer[100];
const char* pong = "PONG";
for (int i = 0; i < 3; i++) {
memset(buffer, 0, sizeof(buffer));
int result = recv(client_socket, buffer, sizeof(buffer), 0);
if (result < 0) {
puts("something went wrong receiving");
closesocket(client_socket);
return 1;
}
if (send(client_socket, pong, strlen(pong)+1, 0) < 0) {
puts("something went wrong sending");
return 1;
}
printf("msg: %s\n", buffer);
Sleep(10000);
}
我期望的行为是,每当客户端从recv阻塞时,服务器上的select返回write_set上的客户端套接字,服务器向客户端写入5个字节,读取5个字节,然后阻塞,直到下一个recv来自客户端。
但事实并非如此,在第一次recv之后,服务器继续写入5个字节,它正确地接收到“PONG”,但并没有停止向客户端写入。
我知道它正在向客户端写入,因为当我杀死客户端进程时,发送立即失败。
Windows是不是很奇怪?我是不是不知道如何使用select?在第一次接收后,客户端如何告诉服务器它没有达到阅读?
1条答案
按热度按时间hs1rzwqc1#
.,每当客户端从recv阻塞时,服务器上的select返回write_set上的客户端套接字,服务器向客户端写入5个字节,读取5个字节,
如果客户端等待
recv
中的数据,则不会向服务器发送任何类型的通知。在一侧的send
和另一侧的recv
之间没有其他紧耦合。recv
、send
和select
都只在socket的发送和接收缓冲区上工作:recv
将从套接字接收缓冲区返回数据。请注意,如果缓冲区中没有那么多数据,它可能不会返回所有请求的数据,因此请检查返回值以了解实际读取了多少数据。如果缓冲区中没有数据,它将阻塞,直到操作系统接收到这些数据并将其放入缓冲区。send
将返回-无论对等体当前是否正在调用recv
。请注意,可能没有足够的空间容纳所有数据,因此您需要检查send
返回的内容,以了解实际上有多少数据被放入发送缓冲区。如果缓冲区中没有空间,send
将阻塞,并等待操作系统通过将数据发送到对等体来腾出一些空间。select
将返回一个套接字可读,如果有任何数据在套接字接收缓冲区。select
将返回一个sockets作为可写