【IO模型】

阻塞IO:

最常用最简单,但是效率最低

非阻塞IO:

需要轮询,CPU资源消耗大

IO多路复用

select()

弊端:

  1. **1.**一个进程只能监听1024文件描述符<br /> **2.**select()是轮询机制<br /> **3.**涉及到用户态和内核态的数据拷贝

poll()

原理和select类似,但是没有最大文件描述符限制
1.优化了文件描述符的个数限制
2.poll()是轮询机制
3.涉及到用户态和内核态的数据拷贝

#include

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数:
  1. **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:**毫秒级超时检测,填10001秒检测<br /> -1,阻塞,不进行超时检测

返回值:
  1. **成功:**返回发生事件的个数<br /> **失败:**返回<0<br /> 设置超时检测:==0

使用步骤:

  1. **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. **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);

  1. **epfd:**epoll_create函数的返回句柄<br /> **op:**表示动作类型。有三个宏来表示:<br /> EPOLL_CTL_ADD:注册新的fdepfd中<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水平触发和边缘触发的区别

  1. <br /> //等待事件到来

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

  1. **功能:**<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;
  2. 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. 可以实现一个服务器**同时和多个**客户端之间进行连接

1.IO多路复用

  1. select()<br /> poll()<br /> epoll()

2.多进程fork()

  1. 当有客户端发来连接请求后,就创建父子进程,由子进程负责数据收发,父进程用来关闭多余的文件描述符<br />

流程:

  1. void handler(int sigo)<br /> {<br /> while (waitpid(-1, NULL, WNOHANG) > 0); //一个SIGCHLD可能对应多个僵尸进程,循环收尸<br /> }
  2. 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.多线程

  1. 当有客户端发来连接请求后,就**产生**子线程,由子线程负责数据收发。<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 / See NOTES /
#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);

功能:

设置套接字选项

参数:

  1. **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 />