• 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;
    }


  • 相关阅读:
    安全编码1
    VPP tips
    VPP概述汇总
    C语言安全编码摘录
    TCP-proxy
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.4. Matplotlib: plotting
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.3. NumPy: creating and manipulating numerical data
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.2. The Python language
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.1. Python scientific computing ecosystem
    25马5跑道,求最快的五匹马的需要比赛的次数
  • 原文地址:https://www.cnblogs.com/chaikefusibushiji/p/7475623.html
Copyright © 2020-2023  润新知