6
6
``` c
7
7
FILE *cin, *cout;
8
8
cin = fdopen(sockfd, " r" );
9
- setbuf (cin, (char * )0);
9
+ setbuf (cin, (char * )0);
10
10
cout = fdopen(sockfd, "w");
11
- setbuf(cout, (char * )0);
11
+ setbuf(cout, (char * )0);
12
12
```
13
13
* 可以通过fgets, fread, fscanf等函数对文件流cin进行读操作(从socket中读)。
14
14
* 可以通过fputs, fwrite, fprintf等函数对文件流cout进行写操作(写入socket)。
15
15
* 需要注意的是,最好在写操作之后加一句fflush(cout),使写入的数据尽快发送。
16
16
* 在断开socket连接前,需要先执行fclose(cin); fclose(cout); 再执行close(sockfd)
17
17
18
-
19
18
## 关于send返回值
20
19
进程间通过socket通信,一个进程执行`listen`后,即使没有执行`accept`,对方进程`connect`也会成功,
21
20
并且`send`也会成功。该进程之后执行`accept`和`read`也都正常。
@@ -26,3 +25,254 @@ setbuf(cout, (char *)0);
26
25
27
26
## 如何获得信息
28
27
`man tcp`
28
+
29
+ # epoll
30
+ epoll是在Linux2.6内核中引入I/O多路复用技术。
31
+
32
+ ```c
33
+ int epoll_create(int size);
34
+ int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
35
+ int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
36
+ ```
37
+
38
+ ### select的问题
39
+ 1 . 最大并发数限制,由FD_SETSIZE设置,默认值是1024/2048
40
+ 2 . 效率: select每次调用都会线性扫描 ** 全部** 的FD集合
41
+ 3 . 内核/用户空间 内存拷贝问题
42
+
43
+ ` poll ` 解决了第一个
44
+
45
+ ### ET和LT模式
46
+ 1 . LT(level triggered)是默认方式, 同时支持block和no-block socket. 内核通知一个文件描述符fd就绪了,然后可以对这个就绪的fd进行IO操作。如果不作操作, 下次调用epoll时内核还是会继续通知, 传统的select/poll都是这种模型的代表。
47
+ 2 . ET(edge-triggered)只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll通知。如果一直不对这个fd作操作,再次调用epoll内核不会发送的通知。
48
+
49
+ ET和LT的区别在于LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。而ET则只在事件发生之时通知,如果要采用ET模式,需要一直read/write直到出错为止。
50
+
51
+ ### [ 程序实例] ( https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c )
52
+ ``` c
53
+ #include < stdio.h>
54
+ #include < stdlib.h>
55
+ #include < string.h>
56
+ #include < sys/types.h>
57
+ #include < sys/socket.h>
58
+ #include < netdb.h>
59
+ #include < unistd.h>
60
+ #include < fcntl.h>
61
+ #include < sys/epoll.h>
62
+ #include < errno.h>
63
+
64
+ #define MAXEVENTS 128
65
+
66
+ static int make_socket_non_blocking (int sfd)
67
+ {
68
+ int flags, s;
69
+ flags = fcntl(sfd, F_GETFL, 0);
70
+ if(flags == -1)
71
+ {
72
+ perror("fcntl getfl");
73
+ return -1;
74
+ }
75
+ flags |= O_NONBLOCK;
76
+ s = fcntl(sfd, F_SETFL, flags);
77
+ if(s == -1)
78
+ {
79
+ perror("fcntl settl");
80
+ return -1;
81
+ }
82
+ return 0;
83
+ }
84
+
85
+ static int create_and_bind(char * port)
86
+ {
87
+ struct addrinfo hints;
88
+ struct addrinfo * result, * rp;
89
+ int s, sfd;
90
+ memset(&hints, 0, sizeof(struct addrinfo));
91
+ hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices* /
92
+ hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket* /
93
+ hints.ai_flags = AI_PASSIVE; /* All interfaces* /
94
+ s = getaddrinfo(NULL, port, &hints, &result);
95
+ if(s != 0)
96
+ {
97
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
98
+ return -1;
99
+ }
100
+ for(rp = result; rp != NULL; rp = rp->ai_next)
101
+ {
102
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
103
+ if(sfd == -1)
104
+ continue;
105
+ s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
106
+ if(s == 0)
107
+ {
108
+ /* managed to bind successfully!* /
109
+ break;
110
+ }
111
+ close(sfd);
112
+ }
113
+ if(rp == NULL)
114
+ {
115
+ fprintf(stderr, "Could not bind\n");
116
+ return -1;
117
+ }
118
+ freeaddrinfo(result);
119
+ return sfd;
120
+ }
121
+
122
+ int
123
+ main(int argc, char * argv[ ] )
124
+ {
125
+ int sfd, s;
126
+ int epfd;
127
+ struct epoll_event event;
128
+ struct epoll_event * events;
129
+ if(argc != 2)
130
+ {
131
+ fprintf(stderr, "Usage: %s [ port] \n", argv[ 0] );
132
+ exit(EXIT_FAILURE);
133
+ }
134
+ sfd = create_and_bind(argv[ 1] );
135
+ if(sfd == -1)
136
+ abort();
137
+ s = make_socket_non_blocking(sfd);
138
+ if(s == -1)
139
+ abort();
140
+ s = listen(sfd, SOMAXCONN);
141
+ if(s == -1)
142
+ {
143
+ perror("listen");
144
+ abort();
145
+ }
146
+ epfd = epoll_create1(0);
147
+ if(epfd == -1)
148
+ {
149
+ perror("epoll_create");
150
+ abort();
151
+ }
152
+ event.data.fd = sfd;
153
+ event.events = EPOLLIN | EPOLLET;
154
+ s = epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &event);
155
+ if(s == -1)
156
+ {
157
+ perror("epoll_ctl");
158
+ abort();
159
+ }
160
+ events = calloc(MAXEVENTS, sizeof event);
161
+ while(1)
162
+ {
163
+ int n, i;
164
+ n = epoll_wait(epfd, events, MAXEVENTS, -1);
165
+ for(i = 0; i < n; i++)
166
+ {
167
+ if((events[ i] .events & EPOLLERR) ||
168
+ (events[ i] .events & EPOLLHUP) ||
169
+ (!(events[ i] .events & EPOLLIN)))
170
+ {
171
+ /* An error has occured on this fd,
172
+ or the socket is not ready for reading* /
173
+ fprintf(stderr, "epoll error\n");
174
+ close(events[ i] .data.fd);
175
+ continue;
176
+ }
177
+ else if(sfd == events[ i] .data.fd)
178
+ {
179
+ /* have a notification on the listening socket,
180
+ means one or more incoming connections.* /
181
+ while(1)
182
+ {
183
+ struct sockaddr in_addr;
184
+ socklen_t in_len;
185
+ int infd;
186
+ char hbuf[ NI_MAXHOST] , sbuf[ NI_MAXSERV] ;
187
+ in_len = sizeof in_addr;
188
+ infd = accept(sfd, &in_addr, &in_len);
189
+ if(infd == -1)
190
+ {
191
+ if((errno == EAGAIN) ||
192
+ (errno == EWOULDBLOCK))
193
+ {
194
+ /* have processed all incoming connections.* /
195
+ break;
196
+ }
197
+ else
198
+ {
199
+ perror("accept");
200
+ break;
201
+ }
202
+ }
203
+ s = getnameinfo(&in_addr, in_len,
204
+ hbuf, sizeof hbuf,
205
+ sbuf, sizeof sbuf,
206
+ NI_NUMERICHOST | NI_NUMERICSERV);
207
+ if(s == 0)
208
+ {
209
+ fprintf(stdout, "Accepted connection on descriptor %d "
210
+ "(host=%s, port=%s)\n", infd, hbuf, sbuf);
211
+ }
212
+ /* Make the incoming socket non-blocking,
213
+ add it to the list of fds to monitor.* /
214
+ s = make_socket_non_blocking(infd);
215
+ if(s == -1)
216
+ abort();
217
+ event.data.fd = infd;
218
+ event.events = EPOLLIN | EPOLLET;
219
+ s = epoll_ctl(epfd, EPOLL_CTL_ADD, infd, &event);
220
+ if(s == -1)
221
+ {
222
+ perror("epoll_ctl");
223
+ abort();
224
+ }
225
+ } /* End while* /
226
+ } /* End if* /
227
+ else
228
+ {
229
+ /* have data on the fd waiting to be read. Read and display it.
230
+ must read whatever data is available completely(in ET mode)
231
+ won't get a notification again for the same data.* /
232
+ int done = 0;
233
+ while(1)
234
+ {
235
+ ssize_t count;
236
+ char buf[ 512] ;
237
+ count = read(events[ i] .data.fd, buf, sizeof buf);
238
+ if(count == -1)
239
+ {
240
+ /* If errno != EAGAIN, that means have read all data.
241
+ So go back to the main loop.* /
242
+ if(errno != EAGAIN)
243
+ {
244
+ perror("read");
245
+ done = 1;
246
+ }
247
+ break;
248
+ }
249
+ else if(count == 0)
250
+ {
251
+ /* End of file. The remote has closed the connection.* /
252
+ done = 1;
253
+ break;
254
+ }
255
+ /* Write the buffer to standard output* /
256
+ s = write(1, buf, count);
257
+ if(s == -1)
258
+ {
259
+ perror("write");
260
+ abort();
261
+ }
262
+ }
263
+ if(done)
264
+ {
265
+ fprintf(stdout, "Closed connection on descriptor %d\n",
266
+ events[ i] .data.fd);
267
+ /* Closing the descriptor will make epoll
268
+ remove it from the set of descriptors which are monitored.* /
269
+ close(events[ i] .data.fd);
270
+ }
271
+ }
272
+ }/* End for* /
273
+ }
274
+ free(events);
275
+ close(sfd);
276
+ return EXIT_SUCCESS;
277
+ }
278
+ ```
0 commit comments