Epoll&Kqueue设计相似:基于已准备事件的eventloop
1.调用名为<font color="#d32f2f" face="Comic Sans MS" style="">epoll_create</font>和<font face="Comic Sans MS" color="#d32f2f">kqueue</font>的<u>syscall</u>,创建一个eventloop
2.调用名为<font face="Comic Sans MS" color="#d32f2f">socket</font>的syscall向OS申请一个socket的FD(文件描述符)
3.调用名为<font face="Comic Sans MS" color="#d32f2f">epoll_ctl</font>和<font face="Comic Sans MS" color="#d32f2f">kevent</font>的syscall,注册该socket的Read事件(以便在可读时收到OS的通知)
4.调用名为<font face="Comic Sans MS" color="#d32f2f">epoll_wait</font>或<font face="Comic Sans MS" color="#d32f2f">kevent</font>的syscall,等待来自OS的事件通知,会阻塞当前线程
5.收到通知时,根据事件类型作不同处理,一般有以下几种事件:<br> a.某socket的close事件,一般调用<font color="#1976d2" face="Comic Sans MS">syscall.Close()</font>关闭fd<br> b.client的连接请求,一般调用<font color="#1976d2" face="Comic Sans MS">syscall.Accept()</font><font face="Comic Sans MS" color="#000000">创建并</font>维护新的socket<br> c.某socket收到数据,且已准备好被读取,一般调用<font color="#1976d2" face="Comic Sans MS">syscall.Read()</font>读取数据<br>
IOCP:基于已完成事件的eventloop(<u>待优化</u>)
1.调用名为<font face="Comic Sans MS" color="#d32f2f">CreateIoCompletionPort()</font>的syscall,创建一个eventloop,<br>得到一个<b>root_CP</b>对象(complete port),这个root_CP对象是一个<br>专职的、负责所有其他new conn的CP对象的I/O完成事件的接收和分发处理
2.启动指定数量个系统线程(一般等于CPU核数),用于处理不同I/O完成事件,<br>如<font color="#2196f3"><b>读完成/写完成</b></font>)线程内部逻辑:<br>
2.1 启动一个while死循环,<u>下面是循环内的逻辑</u>
2.2 初始化一些马上会用到的对象,如一个socket句柄—sock,<br>一个CP对象—hComPort、已传输bytes长度变量—bytesTransfered、<br>包含数据的I/O对象—ioInfo
2.3 阻塞调用<font color="#d32f2f" face="Comic Sans MS">GetQueuedCompletionStatus</font><font color="#d32f2f">(参数为刚<br>初始化的各对象指针)</font>,该调用会在有一个I/O完成事件发生时返回,<br>返回时相关数据会写入上述各指针对象
2.4 当OS回调上述函数时,判断若是读完成事件:<br> a.若bytesTransfered=0,表示传输EOF,需<font color="#d32f2f" face="Comic Sans MS">closesocket</font><font color="#d32f2f">()</font><br> b.若不为0,则从ioInfo<font color="#1976d2"><b>直接取出数据(此时数据已从内核态copy到用户态,<br> 无需再执行系统调用读取数据)</b></font>,并作相应处理,最后执行<br> 系统调用<font color="#d32f2f" face="Comic Sans MS">WSASend</font><font color="#d32f2f">()</font>往socket写入数据;(<i>WSASend也是非阻塞调用</i>)
注:IOCP是在数据拷贝完成后通知我们,<br><b style="">区别于</b>Epoll&Kqueue在数据准备好拷贝时通知我们
2.5 若是(server自己的)写完成事件,一般没有特别处理,可以简单记录log
2.6 再次执行系统调用<font color="#d32f2f" face="Comic Sans MS">WSARecv</font><font face="Comic Sans MS" color="#d32f2f">(sock, &ioInfo...)</font>继续等待client发送数据
<font color="#9e9e9e">2.7 重复while循环</font>
3.执行系统调用<font face="Comic Sans MS" color="#d32f2f">WSASocket()</font>申请一个socket句柄—root_sock,并<font color="#d32f2f" face="Comic Sans MS">bind()</font>地址<br>然后开始<font color="#d32f2f" face="Comic Sans MS">Listen(root_sock); </font><font face="Comic Sans MS" color="#212121"><i>Listen是非阻塞调用</i></font>
4.在主线程中启动一个while死循环,循环内部逻辑:
4.1 阻塞系统调用<font face="Comic Sans MS" color="#d32f2f">accept</font><font color="#d32f2f">()</font>,等待client连接
4.2 当有client连接时,accept()返回一个socket句柄—new_sock,<br><span style="font-size: inherit;">并将其作为参数再次调用</span><font color="#d32f2f" face="Comic Sans MS" style="font-size: inherit;">CreateIoCompletionPort(new_sock, root_CP...)</font>
4.3 初始化一个PER_IO_DATA类型的ioInfo对象
4.4 将new_sock和ioInfo对象作为参数执行系统调用<font face="Comic Sans MS" color="#d32f2f">WSARecv</font><font face="Comic Sans MS" color="#d32f2f">(new_sock, &ioInfo...)</font>,<br>此处为<i>非阻塞调用;(注:收到读完成事件的处理逻辑在第二步启动的线程内完成)</i>
<font color="#9e9e9e">4.5 重复while循环</font>