套接字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;
}

 

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