• IOCP八:同时发送一个buffer


    实验过程:

            1.Server端将19.4M的mfc.zip文件装入全局变量buffer
            2.一次投递100个接受连接
            3.Client创建num个线程连接Server
            4.连接到来后,所有的worker将发送指针指向同一缓冲区(即buffer)投递发送
            5.Client接收数据


    实验结果:
            1.本机测试:100个Client线程成功接收
            2.局域网内其它电脑:20个Client线程成功接收


    实验结论:
            IOCP同时投递的多个WSASend可以指向同一发送缓冲区


    实验客户端:
            Client_IOCP_OneBufferSend


    实验代码:

    #include <WinSock2.h>
    #include <Windows.h>
    #include <iostream>
    #include <fstream>
    #include <process.h>
    #include <string>
    #include <MSWSock.h>
    #include <set>
    #include "autoLock.h"
    
    #pragma comment(lib, "Ws2_32.lib")
    #pragma comment(lib, "Kernel32.lib")
    #pragma comment(lib, "Mswsock.lib")
    
    #define BUF_LEN 1024
    
    char *src;
    int srcLen;
    
    enum OperateType
    {
    	OP_RECV,
    	OP_SEND,
    	OP_ACCEPT,
    };
    
    typedef struct PER_HANDLE_DATA
    {
    	SOCKET s;
    	SOCKADDR_IN addr;
    	
    	/*参考性添加, 防止重复delete PER_HANDLE_DATA, 目前没有找到更好的办法*/
    	HANDLE mutex;
    	bool flag;
    }PER_HANDLE_DATA, *LPPER_HANDLE_DATA;
    
    typedef struct PER_IO_DATA
    {
    	OVERLAPPED overlapped;
    	SOCKET cs;
    	int no;
    	char buf[BUF_LEN]; 
    	int operationType;
    }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(4444);
    
    	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;
    	int ret = WSASend(s, &databuf, 1, &dwRecv, dwFlags, &ppiod->overlapped, NULL);
    	if(SOCKET_ERROR == ret && WSA_IO_PENDING != GetLastError())
    		return false;
    
    	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;
    
    	PostRecv(s, 2);
    
    	while(std::cin.get())
    	{
    		std::string str = "nihaihaoma";
    		bool ret = PostSend(s, str.c_str(), str.length());
    		if(false == ret)
    			break;
    	}
    
    	_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(0 == dwNum && OP_ACCEPT != type)
    		{
    			std::cout<<"The Connection Be Closed : "<<GetLastError()<<std::endl;
    
    			//在一个socket上投递多个WSARecv需要考虑连接被Client断开时所有异步WSARecv均返回不会重复delete PER_HANDLE_DATA
    			AutoLock lock(pphd->mutex);
    			if(true == pphd->flag)
    			{
    				closesocket(pphd->s);
    				delete pphd;
    				pphd->flag = false;
    			}
    			delete ppiod;
    			continue;
    		}
    
    		//错误发生
    		if(false == ret)
    		{
    			std::cout<<"An Error Occurs : "<<GetLastError()<<std::endl;
    			if(pphd != NULL)
    			{
    				closesocket(pphd->s);
    				delete pphd;
    			}
    			delete ppiod;
    			continue;
    		}
    
    		if(OP_RECV == type)
    		{
    			//
    			std::cout<<"接收完成"<<std::endl;
    			std::cout<<"接收端口号 :"<<pphd->s<<std::endl;
    			//
    
    			ppiod->buf[dwNum] = '';
    			std::cout<<"Receiver : "<<ppiod->no<<"  "<<ppiod->buf<<std::endl;
    
    			ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    			ZeroMemory(ppiod->buf, BUF_LEN);
    			WSABUF databuf;
    			databuf.buf = ppiod->buf;
    			databuf.len = BUF_LEN;
    
    			DWORD dwRecv = 0;
    			DWORD dwFlags = 0;
    			WSARecv(pphd->s, &databuf, 1, &dwRecv, &dwFlags, &ppiod->overlapped, NULL);
    		}
    		else if(OP_SEND == type)
    		{
    			//
    			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);
    			p->mutex = CreateMutex(NULL, false, NULL);
    			p->flag = true;
    
    			CreateIoCompletionPort((HANDLE)cs, hcp, (DWORD)p, 0);
    
    			ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
    			ppiod->operationType = OP_SEND;
    
    			WSABUF databuf;
    			databuf.buf = src;
    			databuf.len = srcLen;
    
    			DWORD dwRecv = 0;
    			DWORD dwFlags = 0;
    			int ret = WSASend(cs, &databuf, 1, &dwRecv, dwFlags, &ppiod->overlapped, NULL);
    			if(SOCKET_ERROR == ret && WSA_IO_PENDING != GetLastError())
    				return false;
    
    			PostAccept(pphd->s);
    		}
    
    		/*worker处理完一次发送后睡0.1秒,防止所有请求都被一个worker处理了*/
    		Sleep(100);
    	}
    
    	return 0;
    }
    
    int main()
    {
    	WSADATA ws;
    	if(WSAStartup(MAKEWORD(2, 2), &ws) != 0)
    	{
    		std::cout<<"WSAStartup error : "<<GetLastError()<<std::endl;
    		return -1;
    	}
    	
    	std::ifstream is("C:\Documents and Settings\Administrator\桌面\mfc.zip", std::ios::binary);
    	is.seekg(0, is.end);
    	int len = is.tellg();
    	is.seekg(0, is.beg);
    
    	src = new char[len+10]();
    	std::string tmp = std::to_string((ULONGLONG)len);
    	memcpy(src, tmp.c_str(), tmp.length());
    	is.read(src+10, len);
    
    	srcLen = len + 10;
    
    	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 = new PER_HANDLE_DATA;
    	pphd->s = s;
    	CreateIoCompletionPort((HANDLE)s, hcp, (DWORD)pphd, 0);
    
    	for(int i = 0; i < 100; ++i)
    	{
    		bool ret = PostAccept(s);
    		if(false == ret)
    		{
    			std::cout<<"PostAccept Failed"<<std::endl;
    			continue;
    		}
    	}
    
    	//退出控制
    	/*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;
    }
    

    Client代码

    #include <WinSock2.h>
    #include <Windows.h>
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <process.h>
    
    #pragma comment(lib, "Ws2_32.lib")
    
    sockaddr_in addr = {0};
    HANDLE semaphore;
    
    unsigned int __stdcall F(void *arg)
    {
    	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
    	if(INVALID_SOCKET == s)
    		return false;
    
    	addr.sin_addr.S_un.S_addr = inet_addr("192.168.4.18");
    	addr.sin_family = AF_INET;
    	addr.sin_port = htons(4444);
    
    	WaitForSingleObject(semaphore, INFINITE);
    
    	int ret = connect(s, (sockaddr*)&addr, sizeof(addr));
    	if(SOCKET_ERROR == ret)
    	{
    		std::cout<<"Connect Server Failed : "<<GetLastError()<<std::endl;
    		return -1;
    	}
    
    	{
    		char tmp[10] = {0};
    		ret = recv(s, tmp, 10, 0);
    		if(SOCKET_ERROR == ret)
    		{
    			std::cout<<"Receive Length Error : "<<GetLastError()<<std::endl;
    			_endthreadex(0);
    			return 0;
    		}
    
    		int len = std::stoi(tmp);
    
    		char *chmax = new char[len]();
    
    		int cursor = 0;
    		char ch[100*1024] = {0};
    		while(cursor != len)
    		{
    			ret = recv(s, ch, 100*1024, 0);
    			if(SOCKET_ERROR == ret)
    			{
    				std::cout<<"Recv Data Failed : "<<GetLastError()<<std::endl;
    				return -1;
    			}
    
    			if(0 == ret)
    				break;
    
    			memcpy(chmax+cursor, ch, ret);
    			cursor += ret;
    		}
    
    		std::string filename = "C:\mfc-" + std::to_string((ULONGLONG)GetCurrentThreadId()) + ".zip";
    		std::ofstream os(filename.c_str(), std::ios::binary);
    		os.write(chmax, len);
    
    		os.close();
    	}
    
    	_endthreadex(0);
    	return 0;
    }
    
    int main()
    {
    	WSADATA ws;
    
    	if(WSAStartup(MAKEWORD(2, 2), &ws) != 0)
    		return false;
    
    	int num = 20;
    	std::vector<HANDLE> vec;
    	semaphore = CreateSemaphore(NULL, 0, 100, NULL);
    
    	for(int i = 0; i < num; ++i)
    	{
    		HANDLE h = (HANDLE)_beginthreadex(NULL, 0, F, NULL, 0, NULL);
    		if(h != 0)
    			vec.push_back(h);
    	}
    
    	Sleep(1000);
    	ReleaseSemaphore(semaphore, vec.size(), NULL);
    
    	for(int i = 0; i < vec.size(); ++i)
    		WaitForSingleObject(vec[i], INFINITE);
    
    	std::cout<<"over"<<std::endl;
    	std::cin.get();
    	return 0;
    }


  • 相关阅读:
    DirectUI精髓之一 控件布局的自动缩放(弹簧特性)
    windows mobile6.5截屏工具
    实现的ATL(AtlSimpleArray)数组任意插入辅助函数
    动态库中单例一记
    ASP.NET组件设计Step by Step(4)
    Asp.net 中服务端控件事件是如何触发的
    PagesSection.EnableEventValidation 属性
    ASP.NET事件回传机制
    (服务器控件)页面框架处理回发数据的过程
    ASP.NET底层架构
  • 原文地址:https://www.cnblogs.com/chaikefusibushiji/p/7475623.html
Copyright © 2020-2023  润新知