TCP通讯基本原理
网络分层
TCP通信过程
Socket
- 很多系统都提供Socket作为TCP网络连接的抽象
- Linux -> Internet domain -> SOCK_STREAM
- Linux中Socket以"文件描述符"FD作为标识
Socket通信过程
IO模型
IO模型指的是同时操作Socket的方案
阻塞
同步读写Socket时,线程陷入内核态; 当读写成功后,切换回用户态,继续执行;
优点:开发难度小,代码简单
非阻塞
如果暂时无法收发数据,会返回错误; 应用会不断轮询,直到Socketi可以读写;
优点:不会陷入内核态,自由度高; 缺点:需要自旋轮询
多路复用 - Linux epoll
注册多个Socket事件; 调用epool,当有事件发生,返回
优点:提供了事件列表,不需要查询各个Scoket; 缺点:开发难度大,逻辑复杂
Mac: kqueue, Windows: IOCP
总结
- 操作系统提供了Socket作为TCP通信的抽象
- IO模型指的是操作Socket的方案
- 阻塞模型最利于业务编写,但是性能差
- 多路复用性能好,但业务编写麻烦
net包对TCP通信的实现
阻塞模型 + 多路复用
- 在底层使用操作系统的多路复用IO
- 在协程层次使用阻塞模型
- 阻塞协程时,休眠协程
epoll抽象层
epoll抽象层是为了统一各个操作系统对多路复用器的实现 各个系统的多路复用都有一下功能(以linux epoll为例):
- 新建多路复用器 epoll_create()
- 往多路复用器里插入需要监听的时间 epoll_ctl()
- 查询发生了什么事件 epoll_wait()
Go Network Poller多路复用器的抽象
Go Network Poller对多路复用器的抽象和适配
新建多路复用器 netpollinit() -> epoll_create()
- 调用epollcreate(汇编实现, 底层是epoll_create) 新建epoll
- 新建一个pipe管道用于中断epoll
- 将"管道有数据到达"时间注册到epoll中
最重要的是拿到
epfd
插入事件 netpollopen() -> epoll_ctl()
EPOLL_IN EPOLL_OUT EPOLL_RDHUP EPOLL_ET
- 传入一个Socket的FD, 和pollDesc指针
- pollDesc指针记录了
- Socket相关详细信息
- 哪个协程在等待此Socket
- 将Socket可读/可写/断开注册到Epoll当中
查询事件 netpoll -> epoll_wait()
- 调用epoll_wait(), 查询有哪些时间发生
- 根据Socket相关的pollDesc信息, 返回哪些协程可以唤醒
总结
- Go将多路复用器的操作进行了抽象和适配
- 将新建多路复用器抽象为了netpollinit()
- 将插入监听事件抽象为了netpollopen()
- 将查询事件抽象为了netpoll()
- 但不是返回事件,而是返回等待事件的协程列表