编写模型基本与本地重叠io一致 . 本地OVERLAPPED
另:重叠io 并非是 非阻塞io,两者是不同的概念
重叠io是让操作系统帮忙在后台运行, win上的OVERLAPPED , linux上的 aio 系列函数,他们告诉你何时完成
非阻塞io是自己判断何时操作完成(例如WSAEWOULDBLOCK , EAGAIN), 比如EPOLL的ET模式: epoll select的限制 条件触发 边缘触发
函数调用:
1.WSASocket 可创建一个具有重叠io的套接字 ; 与*nix兼容的 socket 函数默认情况就是具有重叠io;
2.WSARecv / WSASend . 都具有一个overlapped的参数位置 与 一个WSABUF参数;
这两个函数类似于readv/writev . 一次可读写多个多个缓冲区;
OVERLAPPED 结构是用户与内核的一块共享区域. 用于传递值/结果.
WSABUF 参数可用于传递一个数组,一次读写多个缓冲区;
3. WSAWaitForMultipleEvents 用于等待操作完成, 前提是如果OVERLAPPED结构中含有Event对象;
4.WSAGetOverlappedResult 同上. 2个等待函数的区别是如果你像获取接受/发送多少字节那么就用WSAGetOverlappedResult ,
如果仅仅想等待事件完成,则两者都可以;
5.WSARecv / WSASend 最后一个参数是一个APC函数(CompletionROUTINE) :
意思是当操作完成后立即调用此函数.这种函数的用途相当于在 WSAGetOverlappedResult / WSAWaitForMultipleEvents 后面会执行的代码一样 .
这样意味着,如果你使用了APC函数.那么等待操作完成之类的函数不需要再调用了; 因此如果使用了APC函数.OVERLAPPED结构中的Event对象 你可以自由存放任何信息;
另外需要注意. APC函数被操作系统调用需要 当前线程是Alertable状态; 例如 SleepEx(INFINITE, TRUE) 第2个参数;
用2个例子说明上诉函数:
recv.c
#include "stdafx.h"
#include "../utils.h"
//APC函数. 当操作完成后被调用;
void CALLBACK CompletionROUTINE(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
)
{
char * buf = (char*)lpOverlapped->hEvent;
buf[cbTransferred] = 0;
printf("%d bytes recved!\n", cbTransferred);
printf("buf:%s\n", buf);
}
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}
SOCKADDR_IN serv_addr, client_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&client_addr, 0, sizeof(client_addr));
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9988);
//创建一个可用于重叠io的socket;
SOCKET hSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, NULL, WSA_FLAG_OVERLAPPED);
// socket函数,默认就是重叠io;
//SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0);
if (bind(hSocket, (SOCKADDR*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}
if (listen(hSocket, 10) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}
int clientsize = sizeof(client_addr);
DWORD recvsize = 0, flags = 0;
puts("accept");
//等待连接
SOCKET hClientSocket = accept(hSocket, (SOCKADDR*)&client_addr, &clientsize);
//准备一个overlapped结构
WSAOVERLAPPED overlapped = { 0 };
char buf[1024] = {0};
//准备一个WSABUF 给操作系统进行输入
WSABUF databuf;
databuf.buf = buf;
databuf.len = 1024;
//由于这里使用了APC函数, 所以hEvent对象可随意分配
overlapped.hEvent = (void*)&buf;
//注意, 第3个参数是1,意味这只有一个需要输入的地方;
if (WSARecv(hClientSocket, &databuf, 1, &recvsize, &flags, &overlapped, CompletionROUTINE) == SOCKET_ERROR){
//如果到这里说明,数据正在后台传输
if (WSAGetLastError() == WSA_IO_PENDING){
puts("--------->>recved in back");
}
else{
print_error(WSAGetLastError());
}
}
//注意这里, APC函数需要当前线程是Alertable状态
DWORD ret = SleepEx(INFINITE, TRUE);
//意味这APC函数执行完成
if (WAIT_IO_COMPLETION == ret){
puts("io completed");
}
else {
print_error(GetLastError());
}
//再次确认一下.这里如果不需要确认可无需再次;
ret = WSAGetOverlappedResult(hClientSocket, &overlapped, &recvsize,FALSE, &flags);
printf("get overlapped result , ret:%ld,recvsize:%ld\n",
ret, recvsize);
closesocket(hClientSocket);
closesocket(hSocket);
WSACleanup();
return 0;
}
#include "stdafx.h"
#include "../utils.h"
int _tmain(int argc, _TCHAR* argv[])
{
WSAData wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//具有重叠属性的socket;
SOCKET hSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN sendAddr;
memset(&sendAddr, 0, sizeof(sendAddr));
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = htons(9988);
sendAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(hSocket, (SOCKADDR*)&sendAddr, sizeof(sendAddr)) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}
puts("connect success");
//准备一个OVERLAPPED结构
WSAOVERLAPPED wsaoverlapped;
memset(&wsaoverlapped, 0, sizeof(wsaoverlapped));
//由于这里不使用APC函数, 因此创建一个事件对象,用于确认何时结束;
wsaoverlapped.hEvent = WSACreateEvent();
//准备一个WSABUF 给系统传送数据
WSABUF databuf;
char msg[] = "hey girl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
databuf.buf = msg;
databuf.len = strlen(msg) + 1;
DWORD sendBytes;
int serror = 0;
//这里只传送一份数据
if (WSASend(hSocket, &databuf, 1, &sendBytes, 0, &wsaoverlapped, NULL) == SOCKET_ERROR){
serror = WSAGetLastError();
//如果是后台传送的话
if (WSA_IO_PENDING == serror){
puts("send int background");
//两个函数随意选择一个等待操作完成
//WSAWaitForMultipleEvents(1, &wsaoverlapped.hEvent, TRUE, WSA_INFINITE, FALSE);
WSAGetOverlappedResult(hSocket, &wsaoverlapped, &sendBytes, TRUE, NULL);
}
else {
print_error(WSAGetLastError());
}
}
WSACloseEvent(wsaoverlapped.hEvent);
closesocket(hSocket);
WSACleanup();
printf("send bytes:%ld\n", sendBytes);
return 0;
}