前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么是socket?

什么是socket?

作者头像
Monica2333
发布2020-06-19 17:50:41
1.2K0
发布2020-06-19 17:50:41
举报
文章被收录于专栏:码农知识点码农知识点

Socket编程进行的是端到端的通信,基于网络层和传输层的实现。在网络层,Socket 函数需要指定到底是 IPv4 还是IPv6。传输层需要指定是tcp还是udp。 基于TCP协议的socket调用过程:

tcp socket调用过程.jpg

其中用于监听连接的socket和真正用来传数据的socket是不同的socket。在tcp中,socket是一个文件流,在内核中的表现形式是一个文件,含有文件描述符,以及用来读写数据的读缓冲队列和写缓冲队列。 数据结构如下:

socket数据结构.jpg

基于UDP协议的socket调用过程:

udp socket调用过程.jpg

UDP不需要维护连接状态,每次通信都调用sendto 和 recvfrom,都可以传入 IP和端口。

服务器如何建立更多连接?

存在的问题:一个tcp连接服务器需要的信息包括对端 IP和 对端端口,理论最大 TCP 连接数 = 客户端 IP 数×客户端端口数。对于IPv4,最大 TCP 连接数 = 客户端 IP 数(232)×客户端端口数(216) =2^48个。但是受文件描述符(socket就是一个文件)和内存限制,单机操作系统不可能支撑这么大的连接数。

解决办法:

1.多进程方式

在Linux下,创建子进程使用fork函数,会复制文件描述符的列表,内存空间和一条记录当前执行到哪个程序的进程。通过返回值来区分父进程和子进程,子进程返回0,父进程返回的是子进程的pid。进程复制示意图如下:

进程复制.jpg

2.多线程方式

相比进程,线程较为轻量化。在 Linux 下,通过 pthread_create 创建一个线程,也就是调用哦do_fork。虽然新的线程会在task列表上多处一项,但是很多资源是和父线程共用的,只是多了一个指向子线程的引用而已。线程复制示意图如下:

线程复制.jpg

3.IO多路复用

线程的资源是有限的,操作系统仍无法支撑很多线程解决C10K问题,io多路复用就是通过一个线程维护多个socket。

示意图如下:

io多路复用.png

select

如果某个线程管理着所有的socket,它们的文件描述符都放在fd_set中。select通过不断轮询fd_set集合来监听socket的变化。

缺点:

(1)被监控的fds集合限制为1024,1024太小了,我们希望能够有个比较大的可监控fds集合

(2)fds集合需要从用户空间拷贝到内核空间的问题,我们希望不需要拷贝

(3)当被监控的fds中某些有数据可读的时候,我们希望通知更加精细一点,就是我们希望能够从通知中得到有可读事件的fds列表,而不是需要遍历整个fds来收集。

poll:相比select,poll改变了fds集合的描述方式,使用了pollfd结构而不是select的fd_set结构,使得poll支持的fds集合限制远大于select的1024。只是解决了第一个问题。

epoll:采用了Linux的socket 事件wakeup callback机制,当某个文件描述符发送变化的时候,就会主动通知。示意图如下:

epoll机制.jpg

epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄,在这个文件项中保存着一个红黑树,包含了所有监听的socket。当epoll_ctl添加一个socket时,会向红黑树中加入一个节点,它的结构中保存着epoll对象,同时该节点也会指向一个文件结构,关联着被通知的socket fds。epoll_wait则是等待事件的产生。 (1)fds拷贝问题: epoll引入了epoll_ctl系统调用,将高频调用的epoll_wait和低频的epoll_ctl隔离开。同时,epoll_ctl通过(EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL)三个操作来分散对需要监控的fds集合的修改,做到了有变化才变更,将select或poll高频、大块内存拷贝(集中处理)变成epoll_ctl的低频、小块内存的拷贝(分散处理),避免了大量的内存拷贝。同时,对于高频epoll_wait的可读就绪的fd集合返回的拷贝问题,epoll通过内核与用户空间mmap(内存映射)同一块内存来解决。mmap将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理内存地址(不管是用户空间还是内核空间都是虚拟地址,最终要通过地址映射映射到物理地址),使得这块物理内存对内核和对用户均可见,减少用户态和内核态之间的数据交换。 (2)按需遍历fds问题: 通过上面的socket的睡眠队列唤醒逻辑我们知道,socket唤醒睡眠在其睡眠队列的wait_entry(process)的时候会调用wait_entry的回调函数callback,并且,我们可以在callback中做任何事情。为了做到只遍历就绪的fd,我们需要有个地方来组织那些已经就绪的fd。为此,epoll引入了一个中间层,一个双向链表(ready_list),一个单独的睡眠队列(single_epoll_wait_list),并且,与select或poll不同的是,epoll的process不需要同时插入到多路复用的socket集合的所有睡眠队列中,相反process只是插入到中间层的epoll的单独睡眠队列中,process睡眠在epoll的单独队列上,等待事件的发生。同时,引入一个中间的wait_entry_sk,它与某个socket sk密切相关,wait_entry_sk睡眠在sk的睡眠队列上,其callback函数逻辑是将当前sk排入到epoll的ready_list中,并唤醒epoll的single_epoll_wait_list。而single_epoll_wait_list上睡眠的process的回调函数就明朗了:遍历ready_list上的所有sk,挨个调用sk的poll函数收集事件,然后唤醒process从epoll_wait返回。 (3)这种通知方式使得监听的 Socket 数据增加的时候,效率不会大幅下降,能够同时监听的 Socket 的数目也非常的多了。上限就为系统定义的能够打开的最大文件个数。

参考资料: https://time.geekbang.org/column/article/9293 https://cloud.tencent.com/developer/article/1005481 http://www.cnblogs.com/Anker/p/3265058.html

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档