基础
TCP层之下都是一个不可靠的传输
TCP层开始可靠
我们开发者不想去关注三次握手四次挥手,去维护状态之类的
只想关注中间的数据传输,所以操作系统给我们提供了一个socket
每次三次握手之后系统就帮我们生成了一个 socket,只操作socket就行
linux中socket以文件描述符FD作为标识,同样FD可能也是其他的标识
客户端调用connect ,close 三次,四次挥手
sever中socket的个数 = 1个监听的 + 已建立连接的
服务端同时关注多个已建立连接的socket就诞生了 IO模型(同时操作socket的方案)
3个方案(阻塞,非阻塞,多路复用)
阻塞IO
多个线程同步读写socket,线程陷入内核态,一直阻塞等待数据
当读写成功后,切换为用户态,继续执行业务
开发难度小,代码简单
内核态切换开销大(与用户态不断切换)
非阻塞IO
业务不断地去轮询socket,看那个有业务过来,socket不会卡住
不会陷入内核态,在用户态(业务)
理论上在一个线程执行操作,相对之前的阻塞socket,编码要求会高
epoll多路复用
event poll (事件池)(监听),返回发生的事件列表,业务直接去调用相应的socket
监听事件的任务从业务转移给了操作系统
go抽象epoll
netpoll network poller
go的网络层实现了基于协程和socket结合,隐去了epoll这一层
epoll抽象层是为了统一各个操作系统对多路复用器的实现
epoll_create 新建复用器,_ctl 插入监听事件 _wait 等待返回列表
init() 新建epoll,并拿到FD文件描述符
polldesc记录了协程和sicket的对应关系
netpoll 传出的是一个协程列表,协程可读,可写
先调用 _wait 然后结合 polldesc 返回列表
network poller
初始化:原子操作判断是否重复,调用init()
pallcache 列表头 有Lock 缓存polldesc(description)
rg,wg表示想读或者写这个socket的地址
go:linkname 小写方法调用
新增
分配后初始化,调用封装不同平台的open方法
socket已经可读写
gc是不断的循环跑的一个东西
除了业务之外的系统,gc
都是调用的g0协程 ,在g0栈上操作
runtime循环调用netpoll ,(g0)
go如何抽象epoll
socket底层通信过程
建立连接,通信,关闭
目前涉及到net包对TCP网络操作的抽象
net.Listen
新建socket,并执行Bind(绑定端口)
新建一个NetFD(net包对 sicket的描述)
NetFD 其中有一个 polldesc
整个方法返回一个TCPListener对象
同时将FD加入监听
TCPListener本质上一个listen状态的socket
socket(Lieten)socket(established)
只有一行的方法可能是汇编,也可能是go link
TCPListener调用其Accept得到socket(established)
goroutine - per - connection
一个协程一个连接的编程方法