主动方发生在FIN_WAIT_2状态,这个状态时,主动方不可以在应用层发送数据了,但是应用层还可以接收数据,这个状态称为半关闭
#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要关闭的socket的描述符
how: ???允许为shutdown操作选择以下几种方式:
????SHUT_RD(0): ???关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。
???????????????????该套接字不再接收数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。
????SHUT_WR(1):?????关闭sockfd的写功能,此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。
????SHUT_RDWR(2): ??关闭sockfd的读写功能。相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR。
如果对方异常断开,本机检测不到,一直等待,浪费资源
需要设置tcp的保持连接,作用就是每隔一定的时间间隔发送探测分节,如果连续发送多个探测分节对方还未回,就将次连接断开
int ?keepAlive = 1;
setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
心跳包: ?最小粒度
乒乓包: ?携带比较多的数据的心跳包
int opt = 1;
????setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
注意: 程序中设置某个端口重新使用,在这之前的其他网络程序将不能使用这个端口
阻塞等待??消耗资源
非阻塞忙轮询 消耗cpu
多路IO
多路IO转接(多路IO复用): 内核监听多个文件描述符的属性(读写缓冲区)变化
如果某个文件描述符的读缓冲区变化了,这个时候就是可以读了,将这个事件告知应用层
windwos ??使用select ??select跨平台
poll 少用
epoll ??linux?
#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);
功能: 监听多个文件描述符的属性变化(读,写,异常)
???????void FD_CLR(int fd, fd_set *set);
???????int ?FD_ISSET(int fd, fd_set *set);
???????void FD_SET(int fd, fd_set *set);
???????void FD_ZERO(fd_set *set);
参数:
????nfds ?: 最大文件描述符+1
????readfds : 需要监听的读的文件描述符存放集合
????writefds :需要监听的写的文件描述符存放集合 ??NULL
????exceptfds : 需要监听的异常的文件描述符存放集合 ?NULL
????timeout: 多长时间监听一次 ??固定的时间,限时等待 ??NULL 永久监听
????struct timeval {
???????????????long ???tv_sec; ????????/* seconds */ 秒
???????????????long ???tv_usec; ???????/* microseconds */微妙
???????????};
??返回值: 返回的是变化的文件描述符的个数
注意: 变化的文件描述符会存在监听的集合中,未变化的文件描述符会从集合中删除
????select文件描述符请求剩余,之后在遍历,备份。
实现原理:
优缺点:
优点: 跨平台
缺点:
文件描述符1024的限制???由于?FD_SETSIZE的限制
只是返回变化的文件描述符的个数,具体哪个那个变化需要遍历
每次都需要将需要监听的文件描述集合由应用层符拷贝到内核
大量并发,少了活跃,select效率低
假设现在 4-1023个文件描述符需要监听,但是5-1000这些文件描述符关闭了?
假设现在 4-1023个文件描述符需要监听,但是只有 5,1002 发来消息- 无解
优点: 相对于select没有最大1024文件描述符限制
请求和返回是分离
?poll API
#include <poll.h>
?int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能: 监听多个文件描述符的属性变化
参数:
????fds : 监听的数组的首元素地址
????nfds: 数组有效元素的最大下标+1
????timeout : 超时时间 -1是永久监听 >=0 限时等待
数组元素:
struct pollfd
????????????struct pollfd {
???????????????int ??fd;?????????/* file descriptor */ 需要监听的文件描述符
???????????????short events; ????/* requested events */需要监听文件描述符什么事件 ?EPOLLIN 读事件 ??EPOLLOUT写事件
???????????????short revents; ???/* returned events */ 返回监听到的事件 ???EPOLLIN 读事件 ??EPOLLOUT写事
???????????};
优点:
没有文件描述符1024的限制
?请求和返回是分离的
缺点和select一样:
每次都需要将需要监听的文件描述符从应用层拷贝到内核
每次都需要将数组中的元素遍历一遍才知道那个变化了
大量并发,少量活跃效率低
实现原理:
epollAPI?
?a> 创建红黑树
????#include <sys/epoll.h>
??????int epoll_create(int size);
????参数:
????size : ?监听的文件描述符的上限, ?2.6版本之后写1即可,
????返回: ?返回树的句柄
b> ?上树 ?下树 修改节点
????epoll_ctl
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数:
????epfd : 树的句柄
????op : EPOLL_CTL_ADD 上树 ??EPOLL_CTL_DEL 下树 EPOLL_CTL_MOD 修改
????fd : 上树,下树的文件描述符(lfd,cfd)
????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 */ 需要监听的文件描述符
???????????};
将cfd上树
???int epfd = ?epoll_create(1);
struct epoll_event ev;
ev. data.fd = cfd;
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD,cfd, &ev);
c> 监听
????#include <sys/epoll.h>
???????int epoll_wait(int epfd, struct epoll_event *events,
??????????????????????int maxevents, int timeout);
????功能: 监听树上文件描述符的变化
????epfd : 数的句柄
????events : 接收变化的节点的数组的首地址
????maxevents : ?数组元素的个数
????timeout : -1 永久监听 ?大于等于0 限时等待
返回值: 返回的是变化的文件描述符个数
半关闭,主动方关闭了,应用层不能发送数据了,但是能接受数据,这种状态为半关闭状态
对方如果断开连接,一直等待,浪费资源,探测分节就是心跳包用来检测tcp的连接
设置端口复用,可以将端口重复使用
之前的阻塞等待,像是一个事件创建一个还要去遍历,非阻塞忙轮询,就像雇佣了一个人来回查看是否有新的事件,多路io端口复用是内核检测文件描述符的变化,再告知应用层
select(Windows),epoll(Linux),poll都是检测的,
要知道select的函数如何写,并且需要了解如何去构造,我们需要自己构造文件描述符的集合,并且需要遍历,
select实现原理:需要把文件描述符的集合拷贝内核空间,再拷贝回去应用空间,而且需要遍历每一位,读文件还需要遍历,文件描述符太少,只是返回文件描述符的个数,具体变化还需要处理,
poll没有文件描述符1024限制,监听文件描述的变化,EPOLLIN读事件,EPOLLOUT写事件。
poll优点就是修改了文件描述符的限制1024,其他缺点和select一样
?epoll创建一个红黑树,将要监听的文件描述符上树,监听,监听到的文件描述符变化,发给数组接收,创建一个红黑树epoll_create,返回树的句柄,epoll_ctl(句柄,epoll_ctl_add(上树),epoll_ctl_dev(下树),文件描述符,上树的节点)上树,下树,监听epoll_wait(句柄,事件,数组元素个数,-1)返回文件描述变化个数