套接字socket重疊io

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章