see more:http://wufazuce.com/select.html
前言
从零写一个基于select的最简单网络服务,从最基本的socket/bind/listen/accpet函数,了解每个函数每个参数代表的意义。
- 测试文件描述符fd是如何变化的;
- select事件触发的方式;
- 参考redis中select
的实现;
- 读写函数read/write
和recv/send
有什么区别;
- listen中第二个参数backlog
的作用以及表象实测;
实现select服务端
源码:https://github.com/yinnnnn/cpp_demo/blob/master/select2.c
几点实测及结论
0、文件描述符fd
是如何变化的。
- 自增的,0(标准输入),1(标准输出),2(错误输出)是保留的: https://en.wikipedia.org/wiki/File_descriptor
- 如果中间有一个fd close了,后面的新连接会重新使用原来的fd值,所以后面接收到的client的fd不一定比前面的大。
- 不管是打开文件的fd还是网络请求的fd都是通用的,不会混合!
- 可以通过
lsof -p 433
来查看单个进程打开的文件描述符
1、每次select
有事件触发后,再次监听必须把所有的fd重新添加到监听中,因为事件发生后fd已经被内核修改了。
FD_ZERO(&rfds);
for (int i = 0; i <= max; i++) {
//要剔除掉已经close的
printf("set fd=%d\n", i);
FD_SET(i, &rfds);
}
redis中的reset操作是通过memcpy
实现的。
https://github.com/antirez/redis/blob/unstable/src/ae_select.c
memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));
memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));
2、select
有事件触发后必须遍历整个fd
列表(根据FD_ISSET(i, &rfds)
)才能知道是哪些fd
有事件触发。
3、可将bind的fd
和接收到client请求的fd
一起放入select
中监听,不管是有新连接还是原来的client连接有读写事件都能触发select
,达到真正的实时响应。
4、读写函数read/write
和recv/send
有什么区别,应该使用哪种
recv和send多一个参数来控制读写: http://blog.csdn.net/petershina/article/details/7946615
redis采用read/write
5、backlog设置:未accpet的队列数
测试一:不是select响应的个数
- 如果 /proc/sys/net/ipv4/tcp_abort_on_overflow 设为 1, 直接回RST包,结束连接. (http://www.jianshu.com/p/fe2228a77429)
- server端的状态是: SYN_RECV
- redis默认是511
define CONFIG_DEFAULT_TCP_BACKLOG 511