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;
}
浙公网安备 33010602011771号