非阻塞套接字模型

首先说明这不是近期写的东西,主要想通过它来看看站点显示是否正常。时间应该回退到2016年的七月,由于某些原因,要啃一下grpc的源码,回想起来还是蛮痛苦的(可怜现在已经忘的差不多了……)。

那时候的我还是对网络编程很感兴趣的。

最近发现套接字在使用中阻塞型使用的非常少,在非阻塞模型中,错误码十分关键,一般都是在操作返回之后,根据错误码和返回值判断相应的操作结果,之后做分别处理。

这里简单的写了一个服务端的模型,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#define PORT 10010
#define BUFF 256

int main(int argc, char const *argv[])
{
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_fd == -1)
{
printf("create server socket error[%s]...\n", strerror(errno));
return -1;
}

struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));

server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);

if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(sockaddr)) == -1)
{
printf("bind on local address error[%s]\n", strerror(errno));
close(server_fd);
return -1;
}

if(listen(server_fd, 1) == -1)
{
printf("listen on local address error[%s]\n", strerror(errno));
close(server_fd);
return -1;
}
puts("listening on local address...");

struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr), client_fd;

if((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addr_len)) == -1)
{
printf("accept connection error[%s]\n", strerror(errno));
close(server_fd);
return -1;
}
puts("accept one connection...");

int flags = fcntl(client_fd, F_GETFL, 0);
if(fcntl(client_fd, F_SETFL, flags | O_NONBLOCK) < 0)
{
puts("set socket fd to nonblock failed...");
close(server_fd);
return -1;
}

puts("set client_fd to nonblock...");

int recv_len = 0;
char recv_buf[BUFF];
bzero(recv_buf, BUFF);

do
{
do
{
sleep(1);
recv_len = recv(client_fd, recv_buf, BUFF, 0);
printf("recv len = %d\n", recv_len);
if(errno == EINTR)
puts("EINTR");
if(errno == EAGAIN)
puts("EAGAIN");
}while(recv_len < 0 && (errno == EINTR || errno == EAGAIN));

printf("%s\n", recv_buf);
bzero(recv_buf, BUFF);

}while(recv_len != 0);

puts("server done...");
close(client_fd);
close(server_fd);
return 0;
}

使用telnet到端口10010,可以观测到在没有数据到来时,错误码为 EAGAIN
退出telnet客户端,这时观察到返回值变为0,程序退出,也就是说,recv返回值为0表示的是链接的断开,而非没有收取到数据。