select、epoll、socket

socket()函数:
socket函数对应一个普通文件的打开操作,普通文件的打开操作返回一个文件描述符,socket用于创建一个socket描述符,它唯一标识一个socket,socket函数的三个参数分别为:

  • domain,协议族(AF_INET)协议族对应觉决定了socket的地址类型,AF_INET对应ipv4(32位)与端口号(16)的组合
  • type,指定socket类型,常用的有SOCK_STREAM,SOCK_DGRAM,SOCK_PACKET等等,
  • protocol,代表使用tcp传输协议还是udp传输协议
    并不是type和protocol随意组合的
    当使用socket创建一个socket时,返回的socket描述字并没有一个具体地址,需要调用bind函数绑定一个地址,否则当调用connect和listen时系统随机分配一个端口
    bind函数就是把一个地址协议族的特定地址赋值给socket,例如,AF_INET就将一个ipv4的地址和端口号赋值给socket,
    bind参数的三个参数分别为
    socketfd:socket描述字
    addr:指定要赋值给socketfd的协议地址,是一个socketaddr* 型的指针,这个结构根据创建socket时地址协议族不同而不通过,ipv4和ipv6是不同的
    addrlen:对应地址长度
    通常服务器在启动时会绑定一个众所周知地址,用于提供服务,客户可以通过它来来连接服务器,而客户就不用指定,有系统自动分配一个端口号和ip地址组合,这就是为什么通常服务器在listen之前就会调用bind(),而客户端就不调用,而是在connect的时候由系统随机生成一个
    tcp服务器在依次调用socket()bind()listen()之后就会监听指定的socket地址了,tcp客户端通过调用socket和connect之后就可以向服务器发送连接请求了,tcp服务器监听到这个请求后就会调用accept取接受请求,这样连接就建立好了,
    accept():第一个参数为就是服务器的socket描述字,第二个参数指向struct sockaddr的指针,用于返回客户端协议地址,第三个参数为协议地址长度,如果accept成功,那么其返回值是由内核自动生成的一个全新描述字,代表与返回客户的tcp连接
    accept参数的socket是服务器开始调用socket()函数生成的,称为监听socket描述字,而accept返回的是一个已连接的socket描述字,一个服务器通常只创建一个监听套接字,它服务器的生命周期中一直存在,内核为每一个服务器进程接受的客户创建了一个已连接套接字,当服务器完成对某个客户服务,相应的已连接socket描述字就会关闭

客户端的connect在三次握手的第二个次握手返回,accept在三次握手的第三次返回

select、poll、epoll都是io多路复用的机制,可以监视多个描述符,一旦某个描述符就绪(读就绪或者写就绪)
能通过通知程序通知相应的读写操作,但是select、poll、epoll本质上都是同步io,因为他们都需要在读写事件就绪后自己进行读写,读写过程是阻塞的
select函数监视文件描述符分三类,分别是writefds、readfds、exceptfds,调用select函数后会阻塞,直到有描述符就绪,有数据、可读、可写或者超时,函数返回,select函数返回可以通过遍历fdset来寻找就绪的描述符
select有一个缺点就是单个进程能够监视的文件描述符的数量存在最大限制,linux上一般为1024
select采用轮询的方式扫描文件描述符,文件描述符数量越多性能越差
内核/用户数据拷贝频繁,操作复杂

poll
和select函数一样,poll返回后通过轮询pollfd来获取就绪描述符

epoll
epoll_create,epoll_ctl,epoll_wait三个函数,epoll_create函数创建文件描述符,参数size并不是限制所能监听的描述符最大数,只是对内核初始分配内部数据结构的一个建议,返回是epoll描述符。-1表示创建失败,epoll
_ctl控制对指定描述符fd执行op操作,event是与fd关联的监听事件,op操作有三种,epoll_ctl_add,epoll_ctl_mod,epoll_ctl_del,分别添加删除和修改对fd的监听事件,epoll_wait等待epfd上的io事件,最多返回maxevents个事件

epoll主要有以下优势:
监视的描述符数量不受限制,它所支持的fd上限是最大可以打开文件的数目,这个数字远大于2048,
io的效率不会随着监视fd的数量的增长而下降,epoll不同于select和poll的轮询方式,而是通过fd定义的回调函数来实现的,只有就绪的fd才会执行回调函数
支持电平触发和边沿触发两种方式
mmap加速内核与用户空间信息传递,epoll是通过内核与用户空间同一块内存,避免无畏的内存拷贝

epoll高效在于,调用epoll_ctl往里塞入上百万个句柄时,epoll_wait仍然可以飞快返回,并将有效的将发生事件的句柄给我们用户,这是由于在调用epol_create时,内核除了帮我们在epoll文件系统建立file结点,在内核cache建立红黑树用于存储epoll_ctl传来的socket,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表中的数据即可,有数据就返回,没数据就sleep。
在内核里一切皆文件,所以epoll向内核注册了一个文件系统,用于存储上述被监控的socket,当调用epoll_create事,就会在虚拟的epoll文件系统里创建一个file结点,
epoll在被内核初始化的时候,,同时会开辟出epoll自己的内核高速cache区,用于存储想要监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速查找,插入和删除

  • 调用epoll_create创建epoll对象,这个对象包含一个红黑树和一个双向链表,并与底层建立回调机制
  • 调用epoll_ctl向epoll对象添加这100万个链接的套接字
  • 调用epoll_wait收集发生事件的连接

就绪list的维护,当执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪的list链表里,所以当一个socket上有数据到了,内核把网卡上的数据copy到内核中就把socket插入到准备就绪链表里了。

猜你喜欢

转载自blog.csdn.net/qq_30035749/article/details/88856013