Go中的网络编程(一)| 青训营笔记

93 阅读2分钟

TCP通讯基本原理

网络分层

Pasted image 20230523202540.png

TCP通信过程

Pasted image 20230523202721.png

Socket

  • 很多系统都提供Socket作为TCP网络连接的抽象
  • Linux -> Internet domain -> SOCK_STREAM
  • Linux中Socket以"文件描述符"FD作为标识

Socket通信过程

Pasted image 20230523202927.png

Pasted image 20230523203134.png

IO模型

IO模型指的是同时操作Socket的方案

阻塞

Pasted image 20230523203714.png 同步读写Socket时,线程陷入内核态; 当读写成功后,切换回用户态,继续执行; 优点:开发难度小,代码简单

非阻塞

Pasted image 20230523203941.png 如果暂时无法收发数据,会返回错误; 应用会不断轮询,直到Socketi可以读写; 优点:不会陷入内核态,自由度高; 缺点:需要自旋轮询

多路复用 - Linux epoll

Pasted image 20230523204128.png 注册多个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()

  1. 调用epollcreate(汇编实现, 底层是epoll_create) 新建epoll
  2. 新建一个pipe管道用于中断epoll
  3. 将"管道有数据到达"时间注册到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()
    • 但不是返回事件,而是返回等待事件的协程列表