前面一节我们简单的介绍了事件通知的重叠IO模型,有兴趣的可以点击
C++ Windows Socket五种I/O模型之重叠IO模型(一)查看一下,这一章主要是介绍一下完成例程模型。
基本原理
完成例程在投递一个请求之后如(WSARecv)之后,系统完成之后不再以事件通知你,而是在完成之后自动调用你提供的LPWSAOVERLAPPED_COMPLETION_ROUTINE 类型的的回调函数来处理消息。
WSARecv
在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,和事件通知重叠唯一不同的是最后一个参数: lpCompletionRoutine,名称你可以随便起,但是他参数是固定的,下面详细接收
int WSARecv(
SOCKET s,// 当然是投递这个操作的套接字
LPWSABUF lpBuffers, //接收缓冲区,与Recv函数不同,需要一个由WSABUF结构构成的数组
DWORD dwBufferCount, // 数组中WSABUF结构的数量
LPDWORD lpNumberOfBytesRecvd,// 如果接收操作立即完成,这里会返回函数调用接收到的字节数
LPDWORD lpFlags, // 具体内容比较多,具体可以看MSDN,可以对接收数据进行筛选,我们这里设置为0即可
LPWSAOVERLAPPED lpOverlapped,// “绑定”的重叠结构
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //在这你要设定成你回调函数的名称,比如:CompletionROUTINE
);
LPWSAOVERLAPPED_COMPLETION_ROUTINE 类型回调函数
这是完成例程重叠模式关键函数,名称你可以随便命名,但是查询Windows文档中可以看到他的参数是固定如下:
LPWSAOVERLAPPED_COMPLETION_ROUTINE LpwsaoverlappedCompletionRoutine;
void LpwsaoverlappedCompletionRoutine(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
)
{...}
参数:
dwError 表示完成状态,0 表示成功,其他代表不同错误码
cbTransferred 表示都到数据长度,如果出现错误,值为 0
lpOverlapped 返回的重叠结构
dwFlags 与呼叫关联的标志
SleepEX
这一个函数是完成回调的一个重要函数,如果不调用这个函数,回调函数不会执行,函数原型:
DWORD WINAPI SleepEx(
DWORD dwMillesconds,
BOOL balertable);
参数:
dwMillesconds 线程挂起的最短时间,以毫秒为单位。
balertable 如果设置为FALSE,这个函数直到设定的最短时间才会返回,即使中间有回调发生,函数也不回返回,并且不会执行回调函数。
如果设置为TRUE,且调用此函数的线程与调用扩展I/O函数(ReadFileEx或WriteFileEx)的线程相同,则当超时时间已过或出现I/O完成回调函数时,该函数将返回。如果发生I/O完成回调,则调用I/O完成函数。如果一个APC被排队到线程(QueueUserAPC),则该函数在计时器超时或调用APC函数时返回。(简而言之,就是如果要调用回调,此处设置为TRUE)
基本函数就介绍到这个地方,是具体的代码(服务端):
编译Vs2019
#include#include #define MSGSIZE 1024 #include using namespace std; #pragma comment(lib,"ws2_32.lib") SOCKET g_sNewSocket; bool g_bNewConnectionArrived = FALSE; typedef struct { WSAOVERLAPPED overlap; SOCKET socket; WSABUF buffer; char szMessage[MSGSIZE]; DWORD NumberOfBytesRecvd; DWORD Flags; } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA; //回调函数 void CALLBACK CompletionROUTINE(DWORD dwError,DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped,DWORD dwFlags) { LPPER_IO_OPERATION_DATA lap = (LPPER_IO_OPERATION_DATA)lpOverlapped; if (dwError!=0 || cbTransferred==0) { closesocket(lap->socket); HeapFree(GetProcessHeap(), 0, lap); } else { cout << lap->szMessage << endl; //清空重叠结构和缓冲区 ZeroMemory(&lap->overlap, sizeof(WSAOVERLAPPED)); ZeroMemory(lap->szMessage,MSGSIZE); lap->buffer.buf = lap->szMessage; //继续投递重叠结构 WSARecv(lap->socket, &lap->buffer, 1, &lap->NumberOfBytesRecvd, &lap->Flags, &lap->overlap, CompletionROUTINE); } } void workThread() { while (true) { if (g_bNewConnectionArrived) { LPPER_IO_OPERATION_DATA lap = (LPPER_IO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA)); lap->buffer.len = MSGSIZE; lap->buffer.buf = lap->szMessage; lap->socket = g_sNewSocket; //重叠投递 WSARecv(g_sNewSocket, &lap->buffer, 1, &lap->NumberOfBytesRecvd, &lap->Flags, &lap->overlap, CompletionROUTINE); g_bNewConnectionArrived = false; } SleepEx(1000, TRUE); } } bool initSocket() { WSADATA wsdata; WORD sVer = MAKEWORD(2, 2); int ret = WSAStartup(sVer, &wsdata); if (ret != 0) { return false; } return true; } int main() { //初始化com if (!initSocket()) { return -1; } sockaddr_in Sa, recvSa; int len = sizeof(Sa); Sa.sin_port = htons(8888); Sa.sin_addr.S_un.S_addr = INADDR_ANY; Sa.sin_family = AF_INET; SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { cout << "Create socket fail" << endl; return -1; } if (bind(sock, (sockaddr*)&Sa, len) == SOCKET_ERROR) { cout << "bind fail" << endl; return -1; } if (listen(sock, 5) == SOCKET_ERROR) { cout << "listen fail" << endl; return -1; } //创建工作线程 thread t(workThread); t.detach(); while (1) { g_sNewSocket= accept(sock, (sockaddr*)&recvSa, &len); if (g_sNewSocket!=INVALID_SOCKET) { g_bNewConnectionArrived =true; } } return 0; }



