C linux socket - tcp - loop-epoll
# C linux socket - tcp - select
# 封装原始socket库
- myconnect.h
#ifndef __MYCONNECT_H_
#define __MYCONNECT_H_
#include<sys/socket.h>
int Accept(int __fd, struct sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len);
int Bind(int __fd, const struct sockaddr *__addr, socklen_t __len);
int Connect(int __fd, const struct sockaddr *__addr, socklen_t __len);
int Listen(int __fd, int __n);
int Socket(int __domain, int __type, int __protocol);
ssize_t Read(int __fd, void *__buf, size_t __nbytes);
ssize_t Write(int __fd, const void *__buf, size_t __n);
int Close(int __fd);
ssize_t Readn(int __fd, void *__buf, size_t n);
ssize_t Writen(int __fd, const void *__buf, size_t n);
ssize_t Readline(int __fd, void *__buf, size_t maxlen);
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- myconnect.c
#include"myconnect.h"
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/socket.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int Accept(int __fd, struct sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len)
{
int n;
again:
if ((n = accept(__fd, __addr, __addr_len)) < 0)
if((errno==ECONNABORTED)||(errno==EINTR))
goto again;
else
handle_error("accept");
return n;
}
int Bind(int __fd, const struct sockaddr *__addr, socklen_t __len)
{
int n;
if ((n = bind(__fd, __addr, __len)) < 0)
handle_error("bind");
return n;
}
int Connect(int __fd, const struct sockaddr *__addr, socklen_t __len)
{
int n;
;
if ((n = connect(__fd, __addr, __len)) < 0)
handle_error("connect");
return n;
}
int Listen(int __fd, int __n)
{
int n;
if ((n = listen(__fd, __n)) < 0)
handle_error("listen");
return n;
}
int Socket(int __domain, int __type, int __protocol)
{
int n;
if ((n = socket(__domain, __type, __protocol)) < 0)
handle_error("socket");
return n;
}
/**
* read返回值:
* 1.> 0 实际读到的字节数
* 2.= 0 数据读完(读到文件末尾、管道写端关闭、socket对端关闭)
* 3.-1 异常
* 1.errno==EINTR 被信号中断 重启/退出
* 2.errno==EAGAIN(EWOULDBLOCK) 非阻塞方式读,并且没有数据
* 3.其他情况 错误。---perror; exit;
*/
ssize_t Read(int __fd, void *__buf, size_t __nbytes)
{
ssize_t n;
again:
if ((n = read(__fd, __buf, __nbytes)) == -1)
if (errno == EINTR)
goto again;
else
return -1;
return n;
}
ssize_t Write(int __fd, const void *__buf, size_t __n)
{
ssize_t n;
again:
if((n=write(__fd,__buf,__n))==-1)
if(errno==EINTR)
goto again;
else
return -1;
return n;
}
int Close(int __fd)
{
int n;
if((n=close(__fd))==-1)
handle_error("close");
return n;
}
/**
* 读一定数量字符串,成功返回0,失败返回非零。
* size_t n:应该读取的字节数
*/
ssize_t Readn(int __fd, void *__buf, size_t n)
{
size_t nleft; //unsigned int 剩余未读取的字节数
ssize_t nread; //int 实际读到的字节数
char *ptr;
ptr = __buf;
nleft = n; //n 未读取字节数
while (nleft > 0)
{
if ((nread = read(__fd, ptr, nleft)) < 0)
{
if (errno == EINTR)
nread = 0;
else
return -1;
}
else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return n - nleft;
}
/*写一定数量字符串,成功返回0,失败返回-1或大于0的数*/
ssize_t Writen(int __fd, const void *__buf, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr = __buf;
nleft = n;
while (nleft > 0)
{
if((nwritten = write(__fd, ptr, nleft))<0)
{
if (errno == EINTR)
nwritten = 0;
else
return -1;
}
else if (nwritten == 0)
break;
nleft -= nwritten;
ptr += nwritten;
}
return nleft;
}
/*供Readline()使用*/
static ssize_t my_read(int __fd,char*ptr)
{
static int read_cnt;
static char *read_ptr;
static char read_buff[100];
if (read_cnt <= 0)
{
again:
if ((read_cnt = read(__fd, read_buff, sizeof(read_buff))) < 0)
{
if(errno==EINTR)
goto again;
return -1;
}
else if(read_cnt==0)
return 0;
read_ptr = read_buff;
}
read_cnt--;
*ptr = *read_ptr++;
return 1;
}
/*调用my_read(),实现一次读一行,即读到\n结束,末尾\n\0。*/
ssize_t Readline(int __fd, void *__buf, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = __buf;
for (n = 1; n < maxlen; n++)
{
if ((rc = my_read(__fd, &c)) == 1)
{
*ptr++ = c;
if(c=='\n')
break;
}
else if(rc==0)
{
*ptr = 0;
return n - 1;
}else
return -1;
}
*ptr = 0;
return n;
}
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# 调用-client
- client.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<ctype.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<string.h>
#include"myconnect.h"
#define SERV_PORT 6868
#define SERV_IP "192.168211.129"
int main()
{
int cfd;
struct sockaddr_in serv_addr;
char buf[BUFSIZ];
cfd = Socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);
Connect(cfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
while(1)
{
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
Write(cfd, buf, strlen(buf));
int n=Read(cfd, buf, sizeof(buf));
if (n > 0)
printf("%s", buf);
else
break;
}
close(cfd);
return 0;
}
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
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
# 调用-server
- server.c
#include<stdio.h>
#include<sys/select.h>
#include<sys/epoll.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<ctype.h>
//#include"myconnect.h"
/*未调用myconnect封装函数,错误处理不健全*/
#define SERV_PORT 6868
#define MAX_EVENTS 1024 /**监听上限数*/
#define BUFLEN 4096
/* 描述就绪文件描述符相关信息 */
struct myevent_s
{
int fd; //要监听的文件描述符
int events; //对应的监听事件
void *arg; //泛型参数
void (*call_back)(int fd, int events, void *arg); //回调函数
int status; //是否在监听:1->在红黑树上,0->不在
char buf[BUFLEN]; //
int len; //
long last_active; //记录每次加入红黑树的时间
};
int g_efd, listenfd; //全局变量,保存epoll_create返回的文件描述符
struct myevent_s g_events[MAX_EVENTS + 1]; //自定义结构体数组,+1-->listen fd
/*回调函数*/
void recvdata(int fd, int events, void *arg);
void senddata(int fd, int events, void *arg);
/*当有文件描述符就绪,epoll返回,调用该函数与客户端建立连接*/
void acception(int lfd, int events, void *arg);
/*将结构体myevent_s变量初始化*/
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg);
/*向红黑树中添加文件描述符*/
void eventadd(int efd, int events, struct myevent_s *ev);
/*删除节点*/
void eventdel(int efd, struct myevent_s *ev);
/*初始化监听socket*/
void initlistensocket(int g_efd, short port);
/*初始化监听socket*/
void initlistensocket(int g_efd, short port)
{
int lfd = socket(AF_INET, SOCK_STREAM, 0);
listenfd = lfd;
fcntl(lfd, F_SETFL, O_NONBLOCK); //将socket设为非阻塞
/*void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg);*/
eventset(&g_events[MAX_EVENTS], lfd, acception, &g_events[MAX_EVENTS]);
/*void eventadd(int efd, int events, struct myevent_s *ev);*/
eventadd(g_efd, EPOLLIN, &g_events[MAX_EVENTS]);
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(lfd, 10);
return;
}
/*当有文件描述符就绪,epoll返回,调用该函数与客户端建立连接*/
void acception(int lfd, int events, void *arg)
{
struct sockaddr_in clie_addr;
socklen_t clie_addr_len = sizeof(clie_addr);
//char clie_ip[INET_ADDRSTRLEN];
int cfd, i;
if ((cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len)) == -1)
{
if (errno != EAGAIN && errno != EINTR)
{
/*暂时不做处理*/
}
printf("%s:accept, %s\n", __func__, strerror(errno));
return;
}
do
{
for (i = 0; i < MAX_EVENTS;i++) //从全局数组中找到一个空闲元素
if(g_events[i].status==0)
break;
if(i==MAX_EVENTS)
{
printf("%s:max connect limit [%d]\n", __func__, MAX_EVENTS);
break; //跳出do while(0)
}
int flag = 0;
if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) //设置cfd为非阻塞
{
printf("%s: fcntl nonblocking failed,%s\n", __func__, strerror(errno));
break;
}
/*给cfd设置一个myevent_s结构体,回调函数设置为recvdata*/
eventset(&g_events[i], cfd, recvdata, &g_events[i]);
eventadd(g_efd, EPOLLIN, &g_events[i]); //将cfd加入到红黑树中,监听读事件
} while (0);
printf("new connect [%s:%d][time:%ld],pos[%d]\n",
inet_ntoa(clie_addr.sin_addr), ntohs(clie_addr.sin_port), g_events[i].last_active, i);
return;
}
/*收到消息的回调函数*/
void recvdata(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s *)arg;
int len;
len = recv(fd, ev->buf, sizeof(ev->buf), 0);
eventdel(g_efd, ev); //从节点摘下
if(len>0)
{
ev->len = len;
ev->buf[len] = '\0';
printf("recv[%d]:%s", fd, ev->buf);
eventset(ev, fd, senddata, ev); //设置fd回调函数为senddata
eventadd(g_efd, EPOLLOUT, ev); //将fd加入红黑树监听写事件
}
else if(len==0)
{
close(ev->fd);
/* ev-g_events地址相减得到偏移元素位置 */
printf("[fd=%d] pos[%ld],closed\n", fd, ev - g_events);
}
else
{
close(ev->fd);
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
}
return;
}
/*发送消息的回调函数*/
void senddata(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s *)arg;
char buf[BUFLEN];
for (int i = 0; i <= ev->len; i++)
buf[i] = toupper(ev->buf[i]);
int len = send(fd, buf, ev->len, 0);
eventdel(g_efd, ev);
if(len>0)
{
eventset(ev, fd, recvdata, ev); //设置fd回调函数为senddata
eventadd(g_efd, EPOLLIN, ev); //将fd加入红黑树监听写事件
printf("send[%d]:%s", len, buf);
}
// else if(len==0)
// {
// close(ev->fd);
// /* ev-g_events地址相减得到偏移元素位置 */
// printf("[fd=%d] pos[%ld],closed\n", fd, ev - g_events);
// }
else
{
close(ev->fd);
printf("send[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
}
return;
}
/*将结构体myevent_s变量初始化*/
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg)
{
/*code*/
ev->fd = fd;
ev->call_back = call_back;
ev->events = 0;
ev->arg = arg;
ev->status = 0;
if(ev->len<=0)
{
memset(ev->buf, 0, sizeof(ev->buf));
ev->len = 0;
}
ev->last_active = time(NULL); //调用eventset函数的时间
return;
}
/*向红黑树中添加文件描述符*/
void eventadd(int efd, int events, struct myevent_s *ev)
{
struct epoll_event epv = {0, {0}};
int op = 0;
epv.data.ptr = ev;
epv.events = ev->events = events; //EPOLLIN EPOLLOUT
if(ev->status==1) //已经在树中,修改
{
op = EPOLL_CTL_MOD;
}
else //未在树中,添加
{
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if (epoll_ctl(efd, op, ev->fd, &epv) < 0)
printf("event add failed [fd=%d],events[%0x]\n", ev->fd, events);
else
printf("event add OK [fd=%d], op=%d, events[%0x]\n", ev->fd, op, events);
return;
}
/*删除节点*/
void eventdel(int efd, struct myevent_s *ev)
{
struct epoll_event epv = {0, {0}};
if (ev->status != 1)
return;
epv.data.ptr = ev;
ev->status = 0;
epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);
}
int main(int argc, char *argv[])
{
unsigned short port = SERV_PORT;
if (argc == 2)
port = atoi(argv[1]); //使用用户指定端口,否则使用默认端口
g_efd = epoll_create(MAX_EVENTS + 1); //创建红黑树,返回全局g_efd
if(g_efd<=0)
printf("create efd in %s err %s\n", __func__, strerror(errno));
initlistensocket(g_efd, port); //初始化监听socket
struct epoll_event events[MAX_EVENTS + 1]; //保存已经满足就绪事件的文件描述符数组
printf("server running:port [%d]\n", port);
int checkpos = 0, i;
while(1)
{
/**超时验证,每次测试100个连接,不测试listenfd
* 当客户端60s没有和服务器通信,则关闭客户端连接*/
long now = time(NULL);
for (i = 0; i < 100; i++, checkpos++) //一次检测100个
{
if (checkpos == MAX_EVENTS)
checkpos = 0;
if(g_events[checkpos].status!=1)
continue;
long duraction = now - g_events[checkpos].last_active; //客户端不活跃时间
if(duraction>=60)
{
close(g_events[checkpos].fd); //关闭连接
printf("[fd=%d] timeout\n", g_events[checkpos].fd);
eventdel(g_efd, &g_events[checkpos]); //从红黑树移除
}
}
/**监听红黑树g_efd,将满足的时间的文件描述符加至events数组中,
* 1s没有事件满足,返回0*/
int nfd = epoll_wait(g_efd, events, MAX_EVENTS + 1, 1000);
if (nfd < 0)
{
printf("epoll_wait error,exit\n");
break;
}
for (i = 0; i < nfd; i++)
{
/*使用结构体myevent_s类型指针,接收联合体data的void *ptr成员*/
struct myevent_s *ev = (struct myevent_s *)events[i].data.ptr;
if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN))
{
ev->call_back(ev->fd,events[i].events,ev->arg); //读事件就绪
}
if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT))
{
ev->call_back(ev->fd,events[i].events,ev->arg); //写事件就绪
}
}
}
/*退出前释放所有资源*/
close(listenfd);
close(g_efd);
return 0;
}
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
server2.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define MAX_EVENTS 1024 /*监听上限*/
#define BUFLEN 4096 /*缓存区大小*/
#define SERV_PORT 6868 /*端口号*/
void recvdata(int fd,int events,void *arg);
void senddata(int fd,int events,void *arg);
/*描述就绪文件描述符的相关信息*/
struct myevent_s
{
int fd; //要监听的文件描述符
int events; //对应的监听事件,EPOLLIN和EPLLOUT
void *arg; //指向自己结构体指针
void (*call_back)(int fd,int events,void *arg); //回调函数
int status; //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
char buf[BUFLEN];
int len;
long last_active; //记录每次加入红黑树 g_efd 的时间值
};
int g_efd; //全局变量,作为红黑树根
struct myevent_s g_events[MAX_EVENTS+1]; //自定义结构体类型数组. +1-->listen fd
/*
* 封装一个自定义事件,包括fd,这个fd的回调函数,还有一个额外的参数项
* 注意:在封装这个事件的时候,为这个事件指明了回调函数,一般来说,一个fd只对一个特定的事件
* 感兴趣,当这个事件发生的时候,就调用这个回调函数
*/
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int fd,int events,void *arg), void *arg)
{
ev->fd = fd;
ev->call_back = call_back;
ev->events = 0;
ev->arg = arg;
ev->status = 0;
if(ev->len <= 0)
{
memset(ev->buf, 0, sizeof(ev->buf));
ev->len = 0;
}
ev->last_active = time(NULL); //调用eventset函数的时间
return;
}
/* 向 epoll监听的红黑树 添加一个文件描述符 */
void eventadd(int efd, int events, struct myevent_s *ev)
{
struct epoll_event epv={0, {0}};
int op = 0;
epv.data.ptr = ev; // ptr指向一个结构体(之前的epoll模型红黑树上挂载的是文件描述符cfd和lfd,现在是ptr指针)
epv.events = ev->events = events; //EPOLLIN 或 EPOLLOUT
if(ev->status == 0) //status 说明文件描述符是否在红黑树上 0不在,1 在
{
op = EPOLL_CTL_ADD; //将其加入红黑树 g_efd, 并将status置1
ev->status = 1;
}
if(epoll_ctl(efd, op, ev->fd, &epv) < 0) // 添加一个节点
printf("event add failed [fd=%d],events[%d]\n", ev->fd, events);
else
printf("event add OK [fd=%d],events[%0X]\n", ev->fd, events);
return;
}
/* 从epoll 监听的 红黑树中删除一个文件描述符*/
void eventdel(int efd,struct myevent_s *ev)
{
struct epoll_event epv = {0, {0}};
if(ev->status != 1) //如果fd没有添加到监听树上,就不用删除,直接返回
return;
epv.data.ptr = NULL;
ev->status = 0;
epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);
return;
}
/* 当有文件描述符就绪, epoll返回, 调用该函数与客户端建立链接 */
void acceptconn(int lfd,int events,void *arg)
{
struct sockaddr_in cin;
socklen_t len = sizeof(cin);
int cfd, i;
if((cfd = accept(lfd, (struct sockaddr *)&cin, &len)) == -1)
{
if(errno != EAGAIN && errno != EINTR)
{
sleep(1);
}
printf("%s:accept,%s\n",__func__, strerror(errno));
return;
}
do
{
for(i = 0; i < MAX_EVENTS; i++) //从全局数组g_events中找一个空闲元素,类似于select中找值为-1的元素
{
if(g_events[i].status ==0)
break;
}
if(i == MAX_EVENTS) // 超出连接数上限
{
printf("%s: max connect limit[%d]\n", __func__, MAX_EVENTS);
break;
}
int flag = 0;
if((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) //将cfd也设置为非阻塞
{
printf("%s: fcntl nonblocking failed, %s\n", __func__, strerror(errno));
break;
}
eventset(&g_events[i], cfd, recvdata, &g_events[i]); //找到合适的节点之后,将其添加到监听树中,并监听读事件
eventadd(g_efd, EPOLLIN, &g_events[i]);
}while(0);
printf("new connect[%s:%d],[time:%ld],pos[%d]",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), g_events[i].last_active, i);
return;
}
/*读取客户端发过来的数据的函数*/
void recvdata(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s *)arg;
int len;
len = recv(fd, ev->buf, sizeof(ev->buf), 0); //读取客户端发过来的数据
eventdel(g_efd, ev); //将该节点从红黑树上摘除
if (len > 0)
{
ev->len = len;
ev->buf[len] = '\0'; //手动添加字符串结束标记
printf("C[%d]:%s\n", fd, ev->buf);
eventset(ev, fd, senddata, ev); //设置该fd对应的回调函数为senddata
eventadd(g_efd, EPOLLOUT, ev); //将fd加入红黑树g_efd中,监听其写事件
}
else if (len == 0)
{
close(ev->fd);
/* ev-g_events 地址相减得到偏移元素位置 */
printf("[fd=%d] pos[%ld], closed\n", fd, ev-g_events);
}
else
{
close(ev->fd);
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
}
return;
}
/*发送给客户端数据*/
void senddata(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s *)arg;
int len;
len = send(fd, ev->buf, ev->len, 0); //直接将数据回射给客户端
eventdel(g_efd, ev); //从红黑树g_efd中移除
if (len > 0)
{
printf("send[fd=%d], [%d]%s\n", fd, len, ev->buf);
eventset(ev, fd, recvdata, ev); //将该fd的回调函数改为recvdata
eventadd(g_efd, EPOLLIN, ev); //重新添加到红黑树上,设为监听读事件
}
else
{
close(ev->fd); //关闭链接
printf("send[fd=%d] error %s\n", fd, strerror(errno));
}
return ;
}
/*创建 socket, 初始化lfd */
void initlistensocket(int efd, short port)
{
struct sockaddr_in sin;
int lfd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(lfd, F_SETFL, O_NONBLOCK); //将socket设为非阻塞
memset(&sin, 0, sizeof(sin)); //bzero(&sin, sizeof(sin))
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
bind(lfd, (struct sockaddr *)&sin, sizeof(sin));
listen(lfd, 20);
/* void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg); */
eventset(&g_events[MAX_EVENTS], lfd, acceptconn, &g_events[MAX_EVENTS]);
/* void eventadd(int efd, int events, struct myevent_s *ev) */
eventadd(efd, EPOLLIN, &g_events[MAX_EVENTS]); //将lfd添加到监听树上,监听读事件
return;
}
int main()
{
int port=SERV_PORT;
g_efd = epoll_create(MAX_EVENTS + 1); //创建红黑树,返回给全局 g_efd
if(g_efd <= 0)
printf("create efd in %s err %s\n", __func__, strerror(errno));
initlistensocket(g_efd, port); //初始化监听socket
struct epoll_event events[MAX_EVENTS + 1]; //定义这个结构体数组,用来接收epoll_wait传出的满足监听事件的fd结构体
printf("server running:port[%d]\n", port);
int checkpos = 0;
int i;
while(1)
{
/* long now = time(NULL);
for(i=0; i < 100; i++, checkpos++)
{
if(checkpos == MAX_EVENTS);
checkpos = 0;
if(g_events[checkpos].status != 1)
continue;
long duration = now -g_events[checkpos].last_active;
if(duration >= 60)
{
close(g_events[checkpos].fd);
printf("[fd=%d] timeout\n", g_events[checkpos].fd);
eventdel(g_efd, &g_events[checkpos]);
}
} */
//调用eppoll_wait等待接入的客户端事件,epoll_wait传出的是满足监听条件的那些fd的struct epoll_event类型
int nfd = epoll_wait(g_efd, events, MAX_EVENTS+1, 1000);
if (nfd < 0)
{
printf("epoll_wait error, exit\n");
exit(-1);
}
for(i = 0; i < nfd; i++)
{
//evtAdd()函数中,添加到监听树中监听事件的时候将myevents_t结构体类型给了ptr指针
//这里epoll_wait返回的时候,同样会返回对应fd的myevents_t类型的指针
struct myevent_s *ev = (struct myevent_s *)events[i].data.ptr;
//如果监听的是读事件,并返回的是读事件
if((events[i].events & EPOLLIN) &&(ev->events & EPOLLIN))
{
ev->call_back(ev->fd, events[i].events, ev->arg);
}
//如果监听的是写事件,并返回的是写事件
if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT))
{
ev->call_back(ev->fd, events[i].events, ev->arg);
}
}
}
return 0;
}
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# makefile
- makefile
all: server client
server: server.c myconnect.o
gcc server.c myconnect.o -o server
client: client.c myconnect.o
gcc client.c myconnect.o -o client
myconnect.o:myconnect.c
gcc -c myconnect.c
clean:
rm *.o server client
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 编译
#编译
make
#清除编译文件
#make clean
1
2
3
4
2
3
4
先执行server,再执行client,效果是client输入小写,server转大写后发送回client。
编辑 (opens new window)
上次更新: 2023/03/31, 22:34:04
- 01
- Linux系统移植(五)--- 制作、烧录镜像并启动Linux02-05
- 03
- Linux系统移植(三)--- Linux kernel移植02-05