socket通信高级模型
来源:互联网 发布:redis java集群 编辑:程序博客网 时间:2024/06/06 08:41
前几天我刚刚学习了socket网络编程的基本知识,整体的流程是创建socket,要么就是bind/listen/accept/通信,要么就是connect/通信,基本上每一步都是阻塞的,也就是必须得等到有了回应才会返回.当然,我们也可以设置超时参数提前返回,但是超时返回之后我们如果还要连接,仍然需要重新走一遍这样的过程.如果某一时刻有很多请求来连接,或者很多应答来返回,很多就需要等待了,结果自然就是很多超时.
此时,我们需要一种高级的机制.也就是说我们不等,我们把它交给某个调度者(一般就是可爱的OS),然后继续处理其他连接,当某个事情发生时(比如连接建立了,读/写完成了等等),调度者通知我们,我们过去处理,这样当然不会减少每个请求的处理时间,但是会提高我们单位时间处理请求的数量,俗语就是提高了吞吐量.
原生的Linux提供给我们三个socket高级模型供我们使用.虽然我现在还没有把握说哪些场合使用哪些模型最好,但此处我们只谈谈他们的特点而已,至于怎么使用,我想还是需要经验.
1.select模型
主要部份是select系统调用,
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
select阻塞在三个fd集合上,readfds是等待读入的fd集合,writefds是等待输出集合,exceptfds是异常的集合,只要三个集合的任意一个集合中出现了已经准备好的fd,那么select就会立即返回(超时也会返回的),然后我们通过比对判断是哪个fd,然后进行相关操作
fd_set是不透明的类型,需要通过接口来访问
void FD_CLR(int fd, fd_set *set); // 从set中删除fdint FD_ISSET(int fd, fd_set *set); // fd是否在set中而且准备好了void FD_SET(int fd, fd_set *set); // 将fd纳入set中void FD_ZERO(fd_set *set); // 清空set,一般用于初始化基本的流程一般如下:
struct timeval tv = { /* time */};fd_set rs; FD_ZERO(&rs);FD_SET(fd, &rs);ret = select(fd + 1, &rs, NULL, NULL, &tv); // NULL as tv will wait foreverif (ret < 0){ // error}else if (ret == 0){ // time-out}else if (FD_ISSET(fd, &rs)){ // fd is ready}// optFD_CLR(fd, &rs);注意,select中的nfds必须比集合中的最大fd要大1,否则会出现很奇怪的行为(我们需要确定哪个fd最大)
select的缺点主要有两个,一个是集合内fd的数量是有限的,有一个上限不能超过,对于那些很多连接的服务器来说是不好的;另一个则是我们每次需要遍历整个集合来找到合适的fd,比较耗费时间.
2.poll
通过系统调用poll来完成
int poll(struct pollfd *fds, nfds_t nfds, int timeout);我们把需要等待的fd添加到fds队列中,队列的数目nfds我们可以自定义(大小任意),然后调用这个就会阻塞等待任意的fd就绪(或者超时)
队列的结构如下:
struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */};events表示的是该fd等待哪些事件,而revents表示的是当fd准备就绪的时候,发生了什么事件.比如fd等待的是读,挂断,那么当任一事件发生时,revents就会记录究竟是哪件事件发生
主要事件有:
POLLIN: fd可以读入数据
POLLOUT: fd可以写数据了(二者都是指不会阻塞,也就是连接就绪,只差操作了)
POLLPRI: fd有紧急数据(TCP传送时的一种特殊数据)
POLLHUP: fd被挂断
POLLERR: fd出现错误
POLLNVAL: fd还没有open/socket(即fd是无效的fd)
这些都是作为位标来进行测试的,通过"|"可以连接起来,通过对应的"&"进行判断
一般来说poll的处理流程如下:
int t = 1; // 1msstruct pollfd fds[MAX_NUM];int num = 0;fds[num].fd = rfd; // rfd's been open-edfds[num].events = POLLIN | POLLHUP | POLLERR;num++;ret = poll(&fds, num, t); // negative as t will wait foreverif (ret < 0){ // error}else if (ret == 0){ // time-out}// ret is the num of ready fdselse{ for (i = 0; i < num; i++) { if (fds[i].revents != 0) { ret--; // event happens } } assert(ret == 0);}
poll解决了select数量限制的问题,但是还没有解决其遍历求解的毛病,还是需要一个一个查找,当然,有个进步,就是poll返回值是发生事件的fd数量,如果我们只需要获得数量的话,也不错(不过这种应用场景好奇怪)
3.epoll
通过三个相合的调用共同完成
int epoll_create(int size); // size is just a hintint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
EPOLL_CTL_ADD: 给epfd增加fd和event
EPOLL_CTL_DEL: 给epfd删除fd和event
EPOLL_CTL_MOD: 修改fd对应的event
我们来看看事件是什么结构
typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64;} epoll_data_t;struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */};一个events和一个data,而data做成了union,我们可以存放任意的数据,或者就单单存放对应的fd(因为到时候不知道对应的fd)
events有:
EPOLLIN: fd可以读入数据
EPOLLOUT: fd可以写数据了(二者都是指不会阻塞,也就是连接就绪,只差操作了)
EPOLLPRI: fd有紧急数据(TCP传送时的一种特殊数据)
EPOLLHUP: fd被挂断
EPOLLERR: fd出现错误
EPOLLONESHOT: fd只能在对应的事件上使用一次,一旦有任一事件发生,该fd就被删除了,需要手工添加
EPOLLET: 默认的情况时如果fd没有及时处理,下次wait的时候还会出现,但如果设置了EPOLLET, 那么只有在wait之后发生的事件才会唤起fd,早已发生的则不会
epoll_wait则会对添加到epfd的所有fd及事件进行等待,events是响应事件的缓冲区,maxevents是缓冲区的大小,我们不需要赋值,这个是作为wait的结果返回的
epoll流程如下:
int epfd = epoll_create(0 /* ignore */);struct epoll_event ev;ev.events = EPOLLIN | EPOLLHUP | EPOLLET | EPOLLERR;ev.data.fd = fd;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);struct epoll_event evs[MAX_SIZE];ret = epoll_wait(epfd, evs, MAX_SIZE, -1);if (ret < 0){ // error}else if (ret == 0){ // time-out}else{ for (i = 0; i < ret; i++) { // do something }}epoll_wait返回的就是有多少fd准备好了,而events队列里就是每个准备好的fd对应的events,我们只需要遍历准备好的events队列就能处理所有的就绪fd了(不用在全体遍历了)
epoll就成功的解决了select和poll的难题,所以感觉这个非常赞,不过epoll是在linux 2.5.4添加的,如果你使用的旧版本的linux,拜托,怎么也得换成2.6吧..
这些仅仅是OS提供的关于socket通信,或者更广一点关于fd事件相应的基础部件,在这些之上,我们可以实现我们自己的调度模型,比如oop库版本的异步调度模型,就非常的酷,下次我们再回到这个话题的话,我们就自己设计一套异步调度模型
- socket通信高级模型
- Socket通信模型
- 通信模型socket.io
- socket通信模型
- socket通信网络模型
- Socket阻塞通信模型草图
- Socket网络通信方式/模型
- linux下socket通信之通信模型
- linux下socket通信之通信模型
- linux下socket通信之通信模型
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信Select模型(多路复用), 实用Socket通信模板。
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信Poll模型(多路复用), 实用Socket通信模板
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信EPoll模型(多路复用), 实用Socket通信模板
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信EPoll模型(多路复用), 实用Socket通信模板
- Linux——socket高级通信 select socket选项 Http
- Linux Socket通信 C/S模型
- linux 高并发socket通信模型
- linux 高并发socket通信模型
- objective-c加密算法AES
- java List<Long[]> 截取
- 在程序中点击一个按钮,会跳转到app store的评论页,是访问哪个链接?
- 自己写的关于处理json的小函数
- 2012首篇
- socket通信高级模型
- 2011互联网大盘点
- 2011年的工作总结
- 在MFC程序中使用控制台
- Ubuntu Natty locale issue
- 如何查看wsgi的版本
- ADF 的Excel导入方法总结 1
- maven 命令
- 148. Everythins is good when new, but friends when old. 东西是新的好,朋友是老的亲