zoukankan      html  css  js  c++  java
  • IOCP十:Client退出后投递WSARecv

    实验过程:

            过程一:
                    1.Server等待Client到来
                    2.Client进入
                    3.Server接受连接,发送"nihaihaoma"
                    4.Client接收"nihaihaoma",马上退出
                    5.Server进入发送完成处理,Sleep(8000)
                      int ret = WSARecv(s,...)投递接收
            结果:
                    WSARecv以ret=SOCKET_ERROR, GetLastError()= 10054失败 
                    (10054远程主机强迫关闭了一个现有的连接)

            过程二:
                    1.Server等待Client到来
                    2.Client进入
                    3.Server接受连接,发送"nihaihaoma"
                    4.Client接收"nihaihaoma",closesocket(s)后马上退出
                    5.Server进入发送完成处理,Sleep(8000)
                      int ret = WSARecv(s,...)投递接收
            结果:
                    投递成功
                    投递成功的WSARecv马上从GetQueuedCompletionStatus以ret=true, dwNum=0, GetLastError()=997返回
                    (997重叠 I/O 操作在进行中)

    深入探索:
            netstat -ano | findstr "4444" 查看端口状态,显示出异常:
                    1.直接退出:连接在Client退出的那刻被暴力关闭,跳过4步握手,不再可用
                    2.打声招呼再退出:
                                                     回调期间:
                                                                         Client    =    FIN_WAIT_2
                                                                         Server   =   CLOSE_WAIT
                                                      回调结束:
                                                                         Client    =    TIME_WAIT
                                                                         Server   =   CLOSED
                                                      符合四步分手优雅关闭连接

    综上所述:
            1.连接在Client不辞而别的那刻被暴力关闭、跳过4步握手、不再可用——>WSARecv投递在一个不可用连接上,失败在所难免
            2.Client调用closesocket(s)再退出,连接在发送回调中仍旧存在、呈Server->Client的单向传输——>WSARecv投递成功
              跳出回调IOCP关闭连接,故投递成功的WSARecv从GetQueuedCompletionStatus返回


    测试客户端:与IOCP九—Client退出后投递请求相同


    实验代码:

    #include <WinSock2.h>
    #include <Windows.h>
    #include <iostream>
    #include <process.h>
    #include <string>
    #include <MSWSock.h>
    #include <set>
    
    #pragma comment(lib, "Ws2_32.lib")
    #pragma comment(lib, "Kernel32.lib")
    #pragma comment(lib, "Mswsock.lib")
    
    #define BUF_LEN 1024
    
    enum OperateType
    {
    	OP_RECV,
    	OP_SEND,
    	OP_ACCEPT,
    };
    
    typedef struct PER_HANDLE_DATA
    {
    	SOCKET s;			//记录是哪个socket上的请求
    	SOCKADDR_IN addr;	//记录该socket对应的客户端地址和端口
    }PER_HANDLE_DATA, *LPPER_HANDLE_DATA;
    
    typedef struct PER_IO_DATA
    {
    	OVERLAPPED overlapped;	//第一项必须为OVERLAPPED
    	SOCKET cs;				//记录客户端socket
    	char buf[BUF_LEN];		//发送:此buf存储待发送数据,接收:此buf存储到来的数据
    	int operationType;		//记录完成的请求类型:是接收?是发送? 还是连接?
    	int no;
    }PER_IO_DATA, *LPPER_IO_DATA;
    
    SOCKET SocketInitBindListen()
    {
    	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
    	if(INVALID_SOCKET == s)
    	{
    		std::cout<<"create socket failed : "<<GetLastError()<<std::endl;
    		return INVALID_SOCKET;
    	}
    
    	SOCKADDR_IN	addr;
    	addr.sin_family = AF_INET;
    	addr.sin_addr.S_un.S_addr = INADDR_ANY;
    	addr.sin_port = htons(4445);
    
    	int ret = bind(s, (sockaddr*)&addr, sizeof(addr));
    	if(SOCKET_ERROR == ret)
    	{
    		std::cout<<"bind failed : "<<GetLastError()<<std::endl;
    		return SOCKET_ERROR;
    	}
    
    	ret = listen(s, 10);
    	if(SOCKET_ERROR == s)
    	{
    		std::cout<<"listen fail : "<<GetLastError()<<std::endl;
    		return SOCKET_ERROR;
    	}
    
    	return s;
    }
    
    bool PostAccept(SOCKET listenSocket)
    {
    	SOCKET cs = socket(AF_INET, SOCK_STREAM, 0);
    	if(INVALID_SOCKET == cs)
    	{
    		std::cout<<"Create Socket Failed : "<<GetLastError()<<std::endl;
    		return false;
    	}
    
    	LPPER_IO_DATA ppiod = new PER_IO_DATA;
    	ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    	ppiod->operationType = OP_ACCEPT;
    	ppiod->cs = cs;
    
    	DWORD dwRecv;
    	int len = sizeof(sockaddr_in) + 16;
    	bool ret = AcceptEx(listenSocket, ppiod->cs, ppiod->buf, 0, len, len, &dwRecv, &ppiod->overlapped);
    	if(false == ret && ERROR_IO_PENDING != GetLastError())
    	{
    		std::cout<<"AcceptEx Failed : "<<GetLastError()<<std::endl;
    		return false;
    	}
    
    	return true;
    }
    
    bool PostSend(SOCKET s, const char *buf, int len)
    {
    	LPPER_IO_DATA ppiod = new PER_IO_DATA;
    	ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    	ppiod->operationType = OP_SEND;
    	memset(ppiod->buf, 0, BUF_LEN);
    	memcpy(ppiod->buf, buf, len);
    
    	WSABUF databuf;
    	databuf.buf = ppiod->buf;
    	databuf.len = len;
    
    	DWORD dwRecv = 0;
    	DWORD dwFlags = 0;
    	WSASend(s, &databuf, 1, &dwRecv, dwFlags, &ppiod->overlapped, NULL);
    
    	return true;
    }
    
    bool PostRecv(SOCKET s, int n)
    {
    	LPPER_IO_DATA ppiod = new PER_IO_DATA;
    	ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    	ppiod->operationType = OP_RECV;
    	ppiod->no = n;
    	memset(ppiod->buf, 0, BUF_LEN);
    
    	WSABUF databuf;
    	databuf.buf = ppiod->buf;
    	databuf.len = BUF_LEN;
    
    	DWORD dwRecv = 0;
    	DWORD dwFlags = 0;
    	int ret = WSARecv(s, &databuf, 1, &dwRecv, &dwFlags, &ppiod->overlapped, NULL);
    	if(SOCKET_ERROR == ret && WSA_IO_PENDING != GetLastError())
    		return false;
    
    	return true;
    }
    
    unsigned int __stdcall Func(void *arg)
    {
    	SOCKET s = (SOCKET)arg;
    
    	Sleep(3000);
    
    	std::string str = "nihaihaoma";
    	PostSend(s, str.c_str(), str.length());
    
    	_endthreadex(0);
    	return 0;
    }
    
    unsigned int __stdcall ThreadFunc(void *arg)
    {
    	HANDLE hcp = (HANDLE)arg;
    	if(NULL == hcp)
    	{
    		std::cout<<"thread arg error"<<std::endl;
    		return -1;
    	}
    
    	DWORD dwNum = 0;
    	LPPER_HANDLE_DATA pphd;
    	LPPER_IO_DATA ppiod;
    	while(true)
    	{
    		bool ret = GetQueuedCompletionStatus(hcp, &dwNum, (LPDWORD)&pphd, (LPOVERLAPPED*)&ppiod, WSA_INFINITE);
    
    		//线程退出控制,没有释放申请的堆空间,还不完善
    		if(-1 == dwNum)
    		{
    			std::cout<<"Thread Exit"<<std::endl;
    			_endthreadex(0);
    			return 0;
    		}
    
    		int type = ppiod->operationType;
    
    		//连接关闭
    		if(dwNum == 0 && type != OP_ACCEPT)
    		{
    			std::cout<<"The Connection Be Closed: "<<GetLastError()<<std::endl;
    			std::cout<<"ret="<<ret<<" operateType="<<ppiod->operationType<<" no="<<ppiod->no<<std::endl;
    
    			closesocket(pphd->s);
    			delete pphd;
    			delete ppiod;
    			continue;
    		}
    
    		//其他错误
    		if(false == ret)
    		{
    			std::cout<<"An Error Occurs : "<<GetLastError()<<std::endl;
    
    			closesocket(pphd->s);
    			delete pphd;
    			delete ppiod;
    			continue;
    		}
    
    		if(OP_RECV == type)
    		{
    			//
    			std::cout<<"接收完成"<<std::endl;
    			std::cout<<"接收端口号 :"<<pphd->s<<std::endl;
    			//
    		}
    		else if(OP_SEND == type)
    		{
    			//
    			std::cout<<"发送完成"<<std::endl;
    			std::cout<<"完成发送的端口号 : "<<pphd->s<<std::endl;
    			//
    
    			Sleep(8000);
    			DWORD errcode;
    
    			ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    			ppiod->operationType = OP_RECV;
    
    			WSABUF databuf;
    			databuf.buf = ppiod->buf;
    			databuf.len = BUF_LEN;
    
    			DWORD dwRecv = 0;
    			DWORD dwFlags = 0;
    
    			std::cout<<"开始投递WSARecv"<<std::endl;
    			int ret = WSARecv(ppiod->cs, &databuf, 1, &dwRecv, &dwFlags, &ppiod->overlapped, NULL);
    			if(SOCKET_ERROR == ret && (errcode = GetLastError()) != WSA_IO_PENDING)
    				std::cout<<"WSARecv Error: "<<errcode<<std::endl;
    			else
    				std::cout<<"成功"<<std::endl;
    		}
    		else if(OP_ACCEPT == type)
    		{
    			//
    			std::cout<<"连接完成"<<std::endl;
    			//
    
    			SOCKET cs = ppiod->cs;
    			int len = sizeof(sockaddr_in) + 16;
    			int localLen, remoteLen;
    			LPSOCKADDR localAddr, remoteAddr;
    			GetAcceptExSockaddrs(ppiod->buf, 0, len, len, (SOCKADDR **)&localAddr, &localLen, (SOCKADDR **)&remoteAddr, &remoteLen);
    
    			LPPER_HANDLE_DATA p = new PER_HANDLE_DATA;
    			p->s = cs;
    			memcpy(&p->addr, remoteAddr, remoteLen);
    			char *ch = inet_ntoa(p->addr.sin_addr);
    
    			CreateIoCompletionPort((HANDLE)cs, hcp, (DWORD)p, 0);
    
    			ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    			ppiod->operationType = OP_SEND;
    			ppiod->no = 0;
    
    			std::string str = "nihaihaoma";
    			strcpy(ppiod->buf, str.c_str());
    
    			WSABUF databuf;
    			databuf.buf = ppiod->buf;
    			databuf.len = str.length();
    
    			DWORD dwRecv = 0;
    			DWORD dwFlags = 0;
    			WSASend(cs, &databuf, 1, &dwRecv, dwFlags, &ppiod->overlapped, NULL);
    
    			PostAccept(pphd->s);
    			PostAccept(pphd->s);
    		}
    	}
    
    	return 0;
    }
    
    int main()
    {
    	WSADATA ws;
    	if(WSAStartup(MAKEWORD(2, 2), &ws) != 0)
    	{
    		std::cout<<"WSAStartup error : "<<GetLastError()<<std::endl;
    		return -1;
    	}
    
    	HANDLE hcp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    	if(NULL == hcp)
    	{
    		std::cout<<"create completion port failed : "<<GetLastError()<<std::endl;
    		return -1;
    	}
    
    	std::set<HANDLE> setWorkers;
    	SYSTEM_INFO si;
    	GetSystemInfo(&si);
    	for(int i = 0; i < si.dwNumberOfProcessors * 2 + 2; i++)
    	{
    		HANDLE worker = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (LPVOID)hcp, 0, NULL);
    		if(NULL == worker)
    		{
    			std::cout<<"create thread failed : "<<GetLastError()<<std::endl;
    			return -1;
    		}
    		setWorkers.insert(worker);
    	}
    
    	SOCKET s = SocketInitBindListen();
    	if(INVALID_SOCKET == s)
    	{
    		std::cout<<"socket init failed"<<std::endl;
    		return -1;
    	}
    
    	LPPER_HANDLE_DATA pphd = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
    	pphd->s = s;
    	CreateIoCompletionPort((HANDLE)s, hcp, (DWORD)pphd, 0);
    
    	bool ret = PostAccept(s);
    	if(false == ret)
    	{
    		std::cout<<"PostAccept Failed"<<std::endl;
    		return -1;
    	}
    
    	//退出控制
    	/*std::cin.get();
    	for(int i = 0; i < setWorkers.size(); i++)
    		PostQueuedCompletionStatus(hcp, -1, NULL, NULL);*/
    
    	auto iter = setWorkers.begin();
    	for(; iter != setWorkers.end(); iter++)
    		WaitForSingleObject(*iter, INFINITE);
    
    	WSACleanup();
    
    	std::cin.get();
    	return 0;
    }
    


  • 相关阅读:
    PDF文件中的Form保存问题
    Understanding IP Fragmentation
    tcp ip guide IPsec IKE
    Windows安全事件日志中的事件编号与描述
    Cisco PIX fix up and Juniper firewall FTP ALG
    很好的IPSec介绍,详细解释了IKE协商的2个阶段的作用
    virtualbox 下运行Ubuntu 8.10的分辨率和guest additions的问题。
    Fixing the ‘Do you want to display nonsecure items’ message
    windows xp 开始菜单里面所有项目右键不起作用。
    HP backup and recovery manager
  • 原文地址:https://www.cnblogs.com/chaikefusibushiji/p/7475611.html
Copyright © 2011-2022 走看看