我编写了一个小的 tcp 套接字服务器程序,使用 select() 来检查客户端套接字是否可写。如果客户端可写,我就会向其写入数据。
客户端是用Python编写的,仅用于测试,它连接到服务器,并且从不从连接中读取。
结果是,服务器 write() 最终阻塞。
服务器(C):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/select.h>
int main(int argc, char **argv){
int serv_sock;
struct sockaddr_in addr;
const char *ip = "0.0.0.0";
int opt = 1;
int port = 7000;
if(argc > 2){
port = atoi(argv[1]);
}
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons((short)port);
inet_pton(AF_INET, ip, &addr.sin_addr);
serv_sock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(serv_sock, (struct sockaddr *)&addr, sizeof(addr));
listen(serv_sock, 1024);
printf("server listen on port: %d\n", port);
int sock = accept(serv_sock, NULL, NULL);
printf("accepted\n");
while(1){
fd_set w_set;
FD_ZERO(&w_set);
int maxfd = sock;
FD_SET(sock, &w_set);
int ret = select(maxfd + 1, NULL, &w_set, NULL, NULL);
if(ret < 0){
if(errno == EINTR){
continue;
}else{
printf("select error! %s\n", strerror(errno));
exit(0);
}
}
char buf[128 * 1024];
if(FD_ISSET(sock, &w_set)){
printf("write begin\n");
int n = write(sock, buf, sizeof(buf));
printf("write %d\n", n);
}
}
return 0;
}
客户端(Python):
import socket
s = socket.socket()
s.connect(('127.0.0.1', 7000))
输出:
$ gcc a.c; ./a.out
server listen on port: 7000
accepted
write begin
write 131072
write begin
write 131072
write begin
write 131072
write begin
write 131072
write begin
write 131072
write begin
我的问题是:即使 select 告诉可写,为什么 write() 操作也会阻塞? select() 的手册说:
如果可以的话,文件描述符被认为准备就绪 执行相应的 I/O 操作(例如,read(2)),无需 阻塞。
Select() 不知道您要尝试写入多少字节,它只知道套接字的传出数据缓冲区未满。
如果您希望 write() 不阻塞,请将套接字设置为非阻塞模式。
接收方的 TCP 缓冲区最终将被发送方发送的数据填满,以便它通告一个零大小的窗口来通知发送方“我没有剩余空间来保存您的数据,现在停止”。