四层网络分层
Go中主要在传输层 TCP/UDP协议上 去写Redis (net包)
系统提供Socket作为TCP网络连接的抽象,每个socket就是个连接好的(文件描述符FD作为标志)
socket通信过程
如何同时操作socket?阻塞、非阻塞、IO多路复用 epoll
os监控socket,非阻塞地去询问哪些事件发生了,os会返回事件列表,程序直接操作发生事件的socket
阻塞模型+多路复用:底层使用os的多路复用,协程层次使用阻塞模型开发,阻塞协程时进行协程休眠,等待唤醒
GO中如何抽象epoll?统一不同os的多路复用器(epoll\IOCP\Kqueue)
新建epoll_create() 注册监听事件epoll_ctl() 查询epoll_wait()
GO中使用network poller去实现不同平台的io多路复用
⇒netpollinit()\netpollopen()\netpoll() 和上面对应
netpollinit()
· 新建epoll · 新建pip管道用于中断epoll · 管道有数据到达事件注册在epoll中
netpollopen()
· 把fd传入epollctl · 还有个pd *pollDesc(记录了socket和协程的关系) · 把socket可读可写断开事件注册到epoll
netpoll()
调用epollwait(epfd, &events[128]EpollEvent)
把事件填到events。n<0 没有事件发生 retry;n>0 遍历事件数组,执行r/w表示模式(读写等)polldesc 最后返回一个协程的列表 代表有哪些socket可读写的协程列表
pollcache和pollDesc
带锁的pollDesc链表头,pollDesc是对socket的详细描述,包含rg wg读/写的等待协程
如何收发数据?
- G需要收发数据时,Socket已经可读写
· g0循环调用netpoll()方法(放在gcStart里面的 循环)
· 发现socket可读写时,判断mode(r/w) 取出wg/rg 设置为pdReady(1) 表明已经可读写
· 协程调用pollWait()
· 判断rg/wg是否pdReady(1)返回0 - Socket还不可读写
· g0循环调用netpoll()方法(放在gcStart里面的 循环)
· 协程调用pollWait()
· 发现pollDesc里面的wg/rg都是0 对应rg/wg设置为协程的地址
· gopark挂起休眠等待
· 一旦循环发现socket可读写 查看对应rg/wg 如果为地址 直接返回协程地址
· 调度器调度对应协程
GO如何抽象socket?
net包 go原生的网络包 实现TCP UDP HTTP等网络操作
net包里用netFD描述socket连接信息 底层记录了pollDesc等信息 还有包括他连接的地址端口等信息
net.Listen()
新建socket 执行bind;新建fd;返回TCPListener;TCPListener的FD信息加入监听(TCPListener对象本质是一个LISTEN状态的socket)
TCPListener.Accept()
直接调用socket的accept();如果失败则休眠等待新连接;新的socket包装为TCPConn变量返回;TCPConn的FD信息加入监听;TCPConn本质是一个ESTABLISHED状态的socket
TCPConn.Read()/Write()
直接调用socket的读写;失败则休眠等待可读写状态;被唤醒后调用系统socket