1高并发server:多路IO之select
1 select
A:select能监听的文件描写叙述符个数受限于FD_SETSIZE,一般为1024。单纯改变进程打开
的文件描写叙述符个数并不能改变select监听文件个数
B:解决1024下面client时使用select是非常合适的,但假设链接client过多,select採用的是轮询模型,会大大减少server响应效率。不应在select上投入很多其它精力
2
依赖的头文件
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds,fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
说明:
nfds:
监控的文件描写叙述符集里最大文件描写叙述符加1,由于此參数会告诉内核检測前多少个文件描写叙述符的状态
readfds:监控有读数据到达文件描写叙述符集合。传入传出參数
writefds:监控写数据到达文件描写叙述符集合,传入传出參数
exceptfds:监控异常发生达文件描写叙述符集合,如带外数据到达异常。传入传出參数
timeout:定时堵塞监控时间,3中情况
NULL,永远等下去
设置timeval,等待固定时间
设置timeval里时间均为0,检查描写叙述字后马上返回,轮询。
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /*
microseconds
微秒;一百万分之中的一个秒*/
};
void FD_CLR(int fd,fd_set *set);
把文件描写叙述符集合里fd清零
int FD_ISSET(int fd,fd_set *set);
測试文件描写叙述符集合里fd是否置1
void FD_SET(int fd,fd_set *set);
把文件描写叙述符集合里fd位置1
void FD_ZERO(fd_set *set);
把文件描写叙述符集合里全部位清0
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdG90b3R1enVvcXVhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
select模型
案例说明:
Server.c
|
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<netinet/in.h> #include<arpa/inet.h> #include<ctype.h> #include<unistd.h> #include"wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 int main(void) { int i,maxi,maxfd,listenfd,connfd,sockfd; /* * nready为select之后返回的被监控管理的数量, * 最大的被管理的文件最大的被管理的文件描写叙述符的 * 数量是1024 */ /*FD_SETSIZE默觉得1024*/ int nready,client[FD_SETSIZE]; /* FD_SETSIZE默觉得1024 */ ssize_t n; fd_set rset,allset; char buf[MAXLINE]; /*以下表示的是ip地址的长度*/ char str[INET_ADDRSTRLEN]; /*#define IN*/ socklen_t cliaddr_len; struct sockaddr_in cliaddr,servaddr; /*整个server仅仅有一个listenfd文件描写叙述符*/ //1、建立一个socket,案例中是针对TCP的 listenfd = Socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); //2、Bind Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); //3、正在链接的时候最大支持128个,当accept之后就无论了 Listen(listenfd,20); /*默认最大128*/ maxfd = listenfd; /*初始化*/ maxi = -1; /*client[]的下标*/ for(i = 0;i < FD_SETSIZE;i++){ client[i] = -1; /*用-1初始化client*/ } //将管理的文件描写叙述符集合的内容设置成为0 FD_ZERO(&allset); //将listenfd纳入allset中进行管理 FD_SET(listenfd,&allset); /*构造select监控文件描写叙述符集*/ for(;;){ /*每次循环时都又一次设置select监控信号集。这里相当于rset的一个备份*/ rset = allset; //第一个參数是最大描写叙述的最大值+1 nready = select(maxfd+1,&rset,NULL,NULL,NULL); if(nready < 0) { perr_exit("select error"); } //推断listenfd是否已经纳入管理 if(FD_ISSET(listenfd,&rset)) { cliaddr_len = sizeof(cliaddr); connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len); //打印ip地址和port号 printf("received from %s at PORT %d\n", inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,str,sizeof(str)), ntohs(cliaddr.sin_port)); for(i = 0;i < FD_SETSIZE;i++) { if(client[i] < 0) { client[i] = connfd;/*保存accept返回的文件描写叙述符到client[]里*/ break; } } } /*达到select能监控的文件个数上限1024*/ if(i == FD_SETSIZE) { fputs("to many clients\n",stderr); exit(1); } /*加入一个新的文件描写叙述符到监控信号集里*/ FD_SET(connfd,&allset); if(connfd > maxfd) maxfd = connfd; /*select第一个參数须要*/ if(i > maxi) maxi = i; /*更新client[]最大下标值*/ if(--nready == 0) /*假设没有很多其它的就绪文件描写叙述符,继续回到上面select * 负责处理未完毕的就绪文件描写叙述符*/ continue; } for(i = 0;i< maxi;i++) { if((sockfd = client[i]) < 0) continue; if(FD_ISSET(sockfd,&rset)) { if((n=Read(sockfd,buf,MAXLINE)) == 0) { /*当client关闭链接时,server端也关闭链接*/ Close(sockfd); /*解除select监控文件描写叙述符*/ FD_CLR(sockfd,&allset); client[i] = -1; } else { int j; for(j = 0; j< n;j++) { buf[j] = toupper(buf[j]); } Write(sockfd,buf,n); } if(--nready == 0) break; } } Close(listenfd); return 0; } |
Client.c
|
#include <stdio.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include"wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 int main(void) { struct sockaddr_in servaddr; char buf[MAXLINE]; int sockfd,n; //1.Socket sockfd = Socket(AF_INET,SOCK_STREAM,0); //设置ip和port号等相关信息 bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); //2.建立连接 Connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); while(fgets(buf,MAXLINE,stdin) != NULL) { //3.Write数据 Write(sockfd,buf,strlen(buf)); n = Read(sockfd,buf,MAXLINE); if(n == 0) { printf("the other side has been closed.\n"); } else { Write(STDOUT_FILENO,buf,n); } } Close(sockfd); return 0; } |
Wrap.h
|
#ifndef __WRAP_H_ #define __WRAP_H_ void perr_exit(const char *s); int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); void Bind(int fd, const struct sockaddr *sa, socklen_t salen); void Connect(int fd, const struct sockaddr *sa, socklen_t salen); void Listen(int fd, int backlog); int Socket(int family, int type, int protocol); ssize_t Read(int fd, void *ptr, size_t nbytes); ssize_t Write(int fd, const void *ptr, size_t nbytes); void Close(int fd); ssize_t Readn(int fd, void *vptr, size_t n); ssize_t Writen(int fd, const void *vptr, size_t n); static ssize_t my_read(int fd, char *ptr); ssize_t Readline(int fd, void *vptr, size_t maxlen); #endif |
Wrap.c
|
#include <stdlib.h> #include <errno.h> #include <sys/socket.h> void perr_exit(const char *s) { perror(s); exit(1); } int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) { int n; again: if ( (n = accept(fd, sa, salenptr)) < 0) { if ((errno == ECONNABORTED) || (errno == EINTR)) goto again; else perr_exit("accept error"); } return n; } void Bind(int fd, const struct sockaddr *sa, socklen_t salen) { if (bind(fd, sa, salen) < 0) perr_exit("bind error"); } void Connect(int fd, const struct sockaddr *sa, socklen_t salen) { if (connect(fd, sa, salen) < 0) perr_exit("connect error"); } void Listen(int fd, int backlog) { if (listen(fd, backlog) < 0) perr_exit("listen error"); } int Socket(int family, int type, int protocol) { int n; if ( (n = socket(family, type, protocol)) < 0) perr_exit("socket error"); return n; } ssize_t Read(int fd, void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = read(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } ssize_t Write(int fd, const void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = write(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } void Close(int fd) { if (close(fd) == -1) perr_exit("close error"); } ssize_t Readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = 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; } ssize_t Writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } static ssize_t my_read(int fd, char *ptr) { static int read_cnt; static char *read_ptr; static char read_buf[100]; if (read_cnt <= 0) { again: if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return -1; } else if (read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1; } ssize_t Readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; 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高并发server:多路IO之select的更多相关文章
- 3高并发server:多路IO之epoll
1 epoll epoll是Linux下多路复用IO接口select/poll的增强版本号,它能显著提高程序在大量并.发连接中仅仅有少量活跃的情况下的系统CPU利用率,由于它会复用文件描写叙述符 ...
- Linux C++ 网络编程学习系列(2)——多路IO之select实现
select实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/select 源码说明: server.cpp: 监听127.1:6666 ...
- ServerSocketChannel实现多Selector高并发server
参考hbase RpcServer,编写了一个简洁版多Selector server,对nio怎么用,Selector如何选择事件会有更深入的认识. client端发送消息:内容长度 + 内容,200 ...
- 应用层协议实现系列(一)——HTTPserver之仿nginx多进程和多路IO的实现
近期在尝试自己写一个Httpserver,在粗略研究了nginx的代码之后,决定仿照nginx中的部分设计自己实现一个高并发的HTTPserver,在这里分享给大家. 眼下使用的较多的Httpserv ...
- python开发学习-day09(队列、多路IO阻塞、堡垒机模块、mysql操作模块)
s12-20160312-day09 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...
- 高并发之网络IO模型
你好,我是坤哥 今天我们聊一下高并发下的网络 IO 模型 高并发即我们所说的 C10K(一个 server 服务 1w 个 client),C10M,写出高并发的程序相信是每个后端程序员的追求,高并发 ...
- select poll epoll Linux高并发网络编程模型
0 发展历程 同步阻塞迭代模型-->多进程并发模型-->多线程并发模型-->select-->poll-->epoll-->... 1 同步阻塞迭代模型 bind( ...
- 理论铺垫:阻塞IO、非阻塞IO、IO多路复用/事件驱动IO(单线程高并发原理)、异步IO
完全来自:http://www.cnblogs.com/alex3714/articles/5876749.html 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同 ...
- 多路IO复用模型--select, poll, epoll
select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用se ...
随机推荐
- AC日记——Success Rate codeforces 807c
Success Rate 思路: 水题: 代码: #include <cstdio> #include <cstring> #include <iostream> ...
- axis2
下载axis2-1.5.4-bin.zip文件并解压. 设置axis2的环境变量,如下图所示: 利用axis2中的wsdl2java.bat生成客户端程序. 先启动Tomcat7并在IE里运行http ...
- EASYUI datagrid批量修改与提交
http://www.cnblogs.com/szytwo/archive/2012/08/29/2662169.html 前台主要代码: <script type="text/jav ...
- HDU 2567 寻梦(字符串,插入)
#include<iostream> #include<stdio.h> #include<string.h> #include<cmath> usin ...
- ipython notebook install
1.python install (ubuntut系统默认2.7.x) Github: https://github.com/ipython/ipython 2.sudo apt-get instal ...
- 线段树+扫描线【p1884】[Usaco12FEB]过度种植(银)Overplanting …
Description 在一个笛卡尔平面坐标系里(则X轴向右是正方向,Y轴向上是正方向),有\(N(1<=N<=1000)\)个矩形,第i个矩形的左上角坐标是\((x1, y1)\),右下 ...
- 循环节(BFS)
循环节 时间限制: 1 Sec 内存限制: 64 MB提交: 56 解决: 16[提交][状态][讨论版] 题目描述 第一节是英语课.今天,老师又教了桐桐很多单词.桐桐发现所有单词都有循环节(大写 ...
- 【计算几何】【极角序】【二分】bzoj1914 [Usaco2010 OPen]Triangle Counting 数三角形
极角排序后枚举每个点,计算其与原点连线的左侧的半平面内的点与其组成的三角形数(二分/尺取),这些都不是黄金三角形. 补集转化,用平面内所有三角形的个数(C(n,3))减去这些即可. 精度很宽松,几乎不 ...
- 1.1(Mybatis学习笔记)初识Mybatis
一.Mybatis下载与使用 下载地址:https://github.com/mybatis/mybatis-3/releases 下载后解压目录: 需要将lib下的jar包和mybatid-x-x- ...
- React Native 让组件做到局部刷新
利用RN的状态机机制,我们可以通过this.setState({optional:...})来控制界面的刷新,但是一定会触发render方法,那如何保证不调用render方法从而做到界面的局部刷新呢? ...