-
IO完成端口模型的实现要点:
- 创建一个具有重叠(Overlapped)属性的socket(可以用
WSASocket()
来创建); - 创建一个IO完成端口对象(简称CP对象)句柄(使用
CreateIOCompletionPort()
创建); - 创建多个线程(比如可以创建线程池),并将上述CP对象句柄传递给创建的线程(作为线程函数的参数传递进去,CP对象将通过该句柄分配到线程)。然后,在线程中调用
GetQueuedCompletionStatus()
进行阻塞(注意,结束时一定要主动关闭线程句柄或者使用对应的end函数结束线程,一般情况下产生线程后,可以立即通过closeHandle()
来关闭线程句柄,此时线程仍然会运行,且没有内存泄漏); - 将上述创建的CP对象与通过
accept()
获取的socket(此socket也是具有重叠属性的)进行绑定(也是使用CreateIOCompletionPort()
进行绑定)。绑定后,当此socket上的IO完成时,就会自动唤醒在线程上阻塞的GetQueuedCompletionStatus()
函数,从而唤醒相关线程; - 使用
WSARecv()
或者WSASend()
进行异步IO操作,操作完成后,系统自动激活GetQueuedCompletionStatus()
函数,进而激活某个线程去处理。
- 创建一个具有重叠(Overlapped)属性的socket(可以用
-
注意,使用
CreateIOCompletionPort()
创建CP对象时,要指定此对象最大可同时运行的最大线程数量(假设指定为2)。后面如果GetQueuedCompletionStatus()
在多个线程上(假设有5个线程)进行等待,那么也顶多只会激活2个线程,而不会激活5个线程; -
《Win32多线程程序设计》给出的线程池中线程建议个数是
CPU核心数 * 2 + 2
; -
结构体变量的地址值与结构体中第一个成员的地址值相同;
-
注意当使用
CreateIOCompletionPort()
对socket和CP对象进行绑定操作时,其第三个参数用于传递相关信息(可以自定义一个结构体传入),且这个信息会在GetQueuedCompletionStatus()
调用时由第三个参数获取。另外,使用使用WSARecv()
或者WSASend()
时传入的OVERLAPPED
指针也会由GetQueuedCompletionStatus()
函数的第四个参数来获取。也就是说,CreateIOCompletionPort()
可以传递一个自定义结构体给GetQueuedCompletionStatus()
函数,而WSARecv()
或者WSASend()
也可以传递一个OVERLAPPED
结构体给GetQueuedCompletionStatus()
函数(从而也可以在OVERLAPPED
结构体中存放一个自定义结构体传过去); -
如果
GetQueuedCompletionStatus()
调用失败,参数LPOVERLAPPED可能返回的是nullptr,也可能不是nullptr,需要注意,可以通过GetLastError()
获取失败时的错误码,并检查错误码对应的错误原因; -
一般情况下,服务器端的IOCP模型会先在主线程中使用
WSARecv()
接收数据,然后在工作线程中先得到接收的数据,然后立即再调用WSASend()
返回相关的回复数据后,再立即调用WSARecv()
接收数据。并且,每次调用WSARecv()
接收数据时传入的OVERLAPPED
结构体都是新申请的内存,新创建的结构,要注意在合适的地方销毁这个结构; -
如果
GetQueuedCompletionStatus()
中得到的传输字节数为0,也表示对方传递了EOF
,此时注意销毁相关内存; -
在服务器端和客户端保持较长连接的前提下频繁发送大小不一的消息时(最典型的就是网游服务器端),才能发挥IOCP模型或者epoll模型的优势;
-
Linux下创建的线程不需要关闭所谓的线程文件描述符,但是Windows下创建线程后需要关闭线程句柄。
TCP/IP网络编程学习笔记(十三)
最新推荐文章于 2022-04-03 20:02:42 发布