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] = '\0'; 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; }