編寫模型基本與本地重疊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;
}