【IO模型】
阻塞IO:
非阻塞IO:
IO多路复用
select()
弊端:
**1.**一个进程只能监听1024文件描述符<br /> **2.**select()是轮询机制<br /> **3.**涉及到用户态和内核态的数据拷贝
poll()
原理和select类似,但是没有最大文件描述符限制
1.优化了文件描述符的个数限制
2.poll()是轮询机制
3.涉及到用户态和内核态的数据拷贝
#include
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
**struct pollfd *fds:**存放想要监听的文件描述符的数组<br /> struct pollfd {<br /> int fd; /* 文件描述符 */<br /> short events; /* 请求事件类型 */<br /> short revents; /* 返回类型 */<br /> };<br /> 当事件就绪后,revents会被自动填充<br /> **events/revents:**POLLIN(读事件)<br /> <br /> **nfds:**最大监听数<br /> **timeout:**毫秒级超时检测,填1000是1秒检测<br /> -1,阻塞,不进行超时检测
返回值:
**成功:**返回发生事件的个数<br /> **失败:**返回<0<br /> 设置超时检测:==0
使用步骤:
**1.**先创建数组<br /> struct pollfd fds[最大长度]<br /> **2.**把要监听的文件描述符添加到数组<br /> fds[下标].fd = 目标文件描述符<br /> fds[下标].events = POLLIN<br /> **3.**poll()阻塞监听事件<br /> **4.**针对不同事件做逻辑处理<br /> <br /> 01234 67<br />
epoll()
**1.**没有最大文件描述符的限制<br /> **2.**异步IO,当有事件产生,文件描述符会调用callback回调函数<br /> **3.没有**用户态到内核态数据的拷贝<br /> <br />**epoll机制的关键点:**一棵树和一张表<br />**epoll**的实现需要**3个函数:**<br /> **1. **epoll_create()<br /> **2. **epoll_ctl()<br /> **3. **epoll_wait()<br />#include <sys/epoll.h>
int epoll_create(int size); //创建红黑树根节点
成功:返回epoll文件描述符
失败:返回-1
//控制epoll属性
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
**epfd:**epoll_create函数的返回句柄<br /> **op:**表示动作类型。有三个宏来表示:<br /> EPOLL_CTL_ADD:注册新的fd到epfd中<br /> EPOLL_CTL_MOD:修改已注册fd的监听事件<br /> EPOLL_CTL_DEL:从epfd中删除一个fd<br /> **fd:**需要监听的fd。<br /> **event:**告诉内核需要监听什么事件<br /> EPOLLIN:表示对应文件描述符可读<br /> EPOLLOUT:可写<br /> EPOLLPRI:有紧急数据可读;<br /> EPOLLERR:错误;<br /> EPOLLHUP:被挂断;<br /> EPOLLET:触发方式,水平触发;<br /> ET模式:表示状态的变化;<br />**成功:**返回0<br />**失败:**返回-1
任务:找资料查询epoll水平触发和边缘触发的区别
<br /> //等待事件到来
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
**功能:**<br />等待事件的产生,类似于select嗲用<br /> **epfd:**句柄;<br /> **events:**用来从内核得到事件的集合<br /> **maxevents:**表示每次能处理事件最大个数<br /> **timeout:**超时时间,毫秒,0立即返回,-1阻塞<br /> **返回值:**<br />**成功:**返回发生事件的文件描述数<br />**失败:**返回-1<br /> <br /> <br />typedef union epoll_data {<br /> void *ptr;<br /> int fd;<br /> uint32_t u32;<br /> uint64_t u64; <br /> } epoll_data_t;struct epoll_event {<br /> uint32_t events; /* Epoll events */<br /> epoll_data_t data; /* User data variable */<br /> };
【服务器模型】
1.循环服务器:
每一个服务器只能和一个客户端连接,当客户端退出之后才可以和下一个客户端通信
1.socket()
2.bind()
3.listen()
4.while(1)
{
accept();
recv/send;
}
2.并发服务器
可以实现一个服务器**同时和多个**客户端之间进行连接
1.IO多路复用
select()<br /> poll()<br /> epoll()
2.多进程fork()
当有客户端发来连接请求后,就创建父子进程,由子进程负责数据收发,父进程用来关闭多余的文件描述符<br />
流程:
void handler(int sigo)<br /> {<br /> while (waitpid(-1, NULL, WNOHANG) > 0); //一个SIGCHLD可能对应多个僵尸进程,循环收尸<br /> }int sockfd = socket(...); <br /> bind(...); <br /> listen(...);<br /> signal(SIGCHLD, handler); //注册SIGCHLD信号,当产生SIGCHLD信号时调用handler函数<br /> while(1) { <br /> int connfd = accept(...); <br /> if (fork() == 0) { <br /> close(sockfd);<br /> while(1) <br /> { <br /> recv(...); <br /> process(...); <br /> send(...); <br /> }<br /> close(connfd); <br /> exit(...); <br /> } <br /> close(connfd); <br /> }
3.多线程
当有客户端发来连接请求后,就**产生**子线程,由子线程负责数据收发。<br />
流程:
void pthread_handler(void arg)
{
int acceptfd = ((int )arg);
while(1)
{
recv/send()
close(acceptfd);
}
pthread_exit(NULL);
}
main:
int sockfd = socket(…);
bind(…);
listen(…);
while(1)
{
acceptfd = accept();
pthread_create(&tid,NULL,pthread_handler,(void *)&acceptfd)
}
网络信息检索和套接字属性设置
getsockopt() / setsockopt()
获取 / 设置一个套接口选项
bind failed:Address already in use(绑定失败,已在指定地址)
#include
#include
int getsockopt(int sockfd, int level, int optname,void optval, socklen_t optlen);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
功能:
参数:
**sockfd:**套接字<br /> **level: **层次 <br /> SOL_SOCKET 应用层<br /> IPPROTO_IP IP层 / 网络层<br /> IPPRO_TCP 传输层<br /> **optname:**具体值<br /> SO_REUSEADDR<br /> **optval:**值 (有对应的类型)<br /> 重用本地地址和端口 <br /> 允许或不允许<br /> **optlen:**大小<br /> <br /> 允许地址端口重用,代码如下:<br /> int optval = 1;<br /> socklen_t optval_len = sizeof(optval);<br /> setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,optval_len);<br />
