/
server_epoll.c
170 lines (155 loc) · 4.2 KB
/
server_epoll.c
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
///////////////////////////////////////////////////////
// 基于epoll实现TCP服务器 -回显服务器
// 有大量的客户端链接,活跃的客户端比较少
///////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
// 异常处理 , 将错误判断封装成为宏 -----> 错误处理代码和正常的逻辑代码相互耦合
// c++异常不推荐使用 --- 1.运行时影响性能-执行流乱跳 2.影响代码的执行逻辑
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
typedef struct epoll_event epoll_event;
void ProcessListenSock(int epoll_fd, int listen_sock)
{
//1. 调用accept
sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(listen_sock, (sockaddr*)&peer, &len);
if(new_sock < 0)
{
perror("accept");
return;
}
//2. 把new_sock加入到epoll中
epoll_event event;
event.events = EPOLLIN;
event.data.fd = new_sock; //带上这里的文件描述符
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_sock, &event);
if(ret < 0)
{
perror("epoll_ctk ADD");
return;
}
printf("[Client %d] connected\n", new_sock);
return;
}
void ProcessNewSock(int epoll_fd, int new_sock)
{
//1.读写数据
char buf[1024] = {0};
ssize_t read_size = read(new_sock, buf, sizeof(buf) - 1);
if(read_size < 0)
{
perror("read");
return;
}
if(read_size == 0)
{
//2.一旦读到返回值为0,对端关闭了文件描述符
//本端也应该关闭文件描述符,还要把本端的文件描述符从epoll中清理掉
close(new_sock);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, new_sock, NULL);
printf("[Cilent %d] disconnected\n", new_sock);
return;
}
buf[read_size] = '\0';
printf("[Client %d] say: %s\n", new_sock, buf);
write(new_sock, buf, strlen(buf));
return;
}
int ServerInit(const char* ip, short port)
{
//1. 创建socket
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock < 0)
{
perror("socket");
return -11;
}
//2. 绑定端口号
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
int ret = bind(listen_sock, (sockaddr*)&addr, sizeof(addr));
if(ret < 0)
{
perror("bind");
return 0-1;
}
//3. 进行监听
ret = listen(listen_sock, SOMAXCONN); //5-10 SOMAXCONN = 128
if(ret < 0)
{
perror("listen");
return -1;
}
return listen_sock;
}
int main(int argc, char* argv[]) {
if(argc != 3)
{
printf("Usage : ./server_epoll [ip] [port]\n");
return 1;
}
//创建并初始化socket
int listen_sock = ServerInit(argv[1], atoi(argv[2]));
if(listen_sock < 0)
{
printf("ServerInit failed\n");
return 1;
}
//创建并初始化epoll对象
// 把listen_sock放到epoll对象中
int epoll_fd = epoll_create(10);
if(epoll_fd < 0)
{
perror("epoll_create");
return 1;
}
epoll_event event;
event.events = EPOLLIN;
event.data.fd = listen_sock; // key:listen_sock value : listen_sock epoll_wait根据返回的文件描述符的类别分别处理
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event);
if(ret < 0)
{
perror("epoll_ctl");
return -1;
}
//服务器初始化完成
printf("ServerInit Ok\n");
//服务器循环
while(1)
{
epoll_event output_event[100];
int nfds = epoll_wait(epoll_fd, output_event, 100, -1); //-1表示永远阻塞等待,0非阻塞
if(nfds < 0)
{
perror("epoll_wait");
continue;
}
//epoll_wait 返回之后,都有那些文件描述符就绪,就写到了 output_event里面
int i = 0;
for(;i < nfds; ++i)
{
//根据就绪的文件描述符的类别分情况进行讨论
if(listen_sock == output_event[i].data.fd)
{
//1>listen_sock就绪 调用accept
ProcessListenSock(epoll_fd, listen_sock);
}
else
{
//new_sock就绪 调用一次 read
ProcessNewSock(epoll_fd, output_event[i].data.fd);
}//endif
}//end for
}//endwhile
return 0;
}