立即学习:https://edu.csdn.net/course/play/6082/113762?utm_source=blogtoedu
完成例程(completion routines)
完成例程回调函数原型
void CALLBACK CompletionRoutineFunc(DWORD dwError,DWROD cbTransferred,LPWSAOVERLAPPED lpOverlapped,DWORD dwFlags)
参数:
第一个参数:表名一个重叠操作的完成的状态
第二个参数:指明了重叠操作期间,实际传输字节数
第三个参数:传输到最初的IO调用内的重叠结构
第四个参数:返回操作结束时可能用到的标志
SleepEx函数:
SleepEx(
DWORD dwMilliseconds,
BOOL bAlertable
);
参数:
第一个参数:等待的超时时间,如果设置为INFINITE就会一直等待下去。
第二个参数:是否置于警觉状态,如果为FALSE,则一定要等待超时时间完毕后才返回,我们这里希望重叠操作,一完成就能返回,要设置为TRUE ;
返回值:
如果指定的时间间隔过期,返回值为0
如果返回的函数是由于一个或多个IO完成回调函数而返回的函数,则返回值是WAIT_IO_COMPLETION,只有在bAlertable是正确的情况下,才会发生这种情况,如果调用SleepEx函数的线程与调用扩展IO函数的线程相同。
基于完成例程的重叠IO编程步骤
1、创建套接字在指定端口监听,并接受客户端连接请求。
2、为接受的套接字新建一个WSAOVERLAPPED结构
3、在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构,同时提供一个完成例程函数,注意函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDINIG(IO操作尚未完成)
4、使用伪事件数组,将调用WSAWaitForMultipleEvents函数,fAlertable设置为true,等待重叠IO请求完成,重叠请求完成以后,完成例程会自动执行,而且WSAWaitForMultipleEvent函数的返回WAIT_IO_COMPLETION说明一个或多个完成例程已经排队等待执行。
5、在完成例程内,可用一个完成例程投递另一个重叠WSARecv请求。
6、如果要想知道完成例程调用后的返回结果,调用WSAWaitForMultipleEvents函数(返回值WAIT_IO_COMPLETION)或者SleepEx函数等待重叠操作返回的结果。
7、重复5~6.
服务端代码
#include <iostream>
#include<WinSock2.h>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void CALLBACK CompletionRoutineFunc(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
typedef struct _MY_WSAOVERLAPPED
{
WSAOVERLAPPED overlap;
WSABUF Buffer;
DWORD NumberOfBytesRecvd;
DWORD Flags;
SOCKET socket;
_MY_WSAOVERLAPPED()
{
Buffer.buf = new char[64]{ '\0' };
Buffer.len = 64;
Flags = 0;
}
~_MY_WSAOVERLAPPED()
{
delete[]Buffer.buf;
Buffer.buf = NULL;
Buffer.len = 0;
}
}MY_WSAOVERLAPPED, * PMY_WSAOVERLAPPED;
int main()
{
WSADATA wd;
if (WSAStartup(MAKEWORD(2, 2), &wd) != 0)
{
cout << "wsastartup error " << WSAGetLastError() << endl;
exit(EXIT_FAILURE);
}
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
cout << "socket error " << WSAGetLastError() << endl;
exit(EXIT_FAILURE);
}
sockaddr_in addr;
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
int bindRet = bind(s, (sockaddr*)&addr, sizeof sockaddr);
if (bindRet == SOCKET_ERROR)
{
cout << "bind error " << WSAGetLastError() << endl;
exit(EXIT_FAILURE);
}
int listenRet = listen(s, 0);
if (listenRet == SOCKET_ERROR)
{
cout << "listen error " << WSAGetLastError() << endl;
exit(EXIT_FAILURE);
}
while (true)
{
SOCKET sClient = accept(s, NULL, NULL);
if (sClient == INVALID_SOCKET)
{
cout << "accept error" << WSAGetLastError() << endl;
continue;
}
cout << sClient << "进入聊天室" << endl;
char temp[64];
sprintf_s(temp,"欢迎%d进入客户端",sClient);
send(sClient,temp,strlen(temp),0);
PMY_WSAOVERLAPPED pOver = new MY_WSAOVERLAPPED;
pOver->socket = sClient;
int ret = WSARecv(pOver->socket, &pOver->Buffer, 1, &pOver->NumberOfBytesRecvd, &pOver->Flags, &pOver->overlap, CompletionRoutineFunc);
if (ret == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
cout << "wsarecv error " << WSAGetLastError << endl;
closesocket(sClient);
continue;
}
}
}
}
void CALLBACK CompletionRoutineFunc(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
PMY_WSAOVERLAPPED pover = (PMY_WSAOVERLAPPED)lpOverlapped;
if (dwError == 0 && cbTransferred > 0)
{
cout << pover->socket << " 说:" << pover->Buffer.buf << endl;
ZeroMemory(pover->Buffer.buf, 64);
int ret = WSARecv(pover->socket, &pover->Buffer, 1, &pover->NumberOfBytesRecvd, &pover->Flags, &pover->overlap, CompletionRoutineFunc);
if (ret == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
cout << "wsarecv error " << WSAGetLastError() << endl;
closesocket(pover->socket);
delete[]pover->Buffer.buf;
}
}
}
else
{
cout << pover->socket << "离开了" << endl;
closesocket(pover->socket);
delete[]pover->Buffer.buf;
}
}