服务器:
#pragma once
//https://www.cnblogs.com/xiaobingqianrui/p/9258665.html
//完成端口(IOCP)编程
#include <iostream>
#include <thread>
//网络编程三个文件
#include<WS2tcpip.h>
#include <winsock2.h>
#include <windows.h>
#include <minwindef.h>
#include <vector>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
HANDLE g_hIOCP;//完成端口描述符
enum class IO_OPERATION : int
{
IO_READ,
IO_WRITE
};
struct IO_DATA{
OVERLAPPED Overlapped;
WSABUF wsabuf;
int nBytes;
IO_OPERATION opCode;
SOCKET client;
};
char buffer[1024];//缓冲区,接受发送的数据都存在这里
inline DWORD WorkerThread()
{
IO_DATA *lpIOContext = nullptr;
DWORD nBytes = 0;
DWORD dwFlags = 0;
int nRet = 0;
DWORD dwIoSize = 0;
void * lpCompletionKey = nullptr;
LPOVERLAPPED lpOverlapped = nullptr;
while(true){
//缓冲区有数据时就会继续执行,没数据就会阻塞
GetQueuedCompletionStatus(g_hIOCP, &dwIoSize,reinterpret_cast<LPDWORD>(&lpCompletionKey),static_cast<LPOVERLAPPED *>(&lpOverlapped), INFINITE);
lpIOContext = reinterpret_cast<IO_DATA *>(lpOverlapped);
cout<<"["<<this_thread::get_id()<<"]线程"<<", Size:"<<sizeof(lpIOContext) << ", 地址:"<< lpIOContext<<endl;
if(dwIoSize == 0)
{
cout << "Client disconnect" << endl;
closesocket(lpIOContext->client);
delete lpIOContext;
continue;
}
if(lpIOContext->opCode == IO_OPERATION::IO_READ) // a read operation complete
{
cout<<"["<<this_thread::get_id()<<"]线程"<<"收到客户端的消息:"<< lpIOContext->wsabuf.buf<<endl;
ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));
lpIOContext->wsabuf.buf = buffer;
lpIOContext->wsabuf.len = strlen(buffer)+1;
lpIOContext->opCode = IO_OPERATION::IO_WRITE;
lpIOContext->nBytes = strlen(buffer)+1;
dwFlags = 0;//必须为0
string sendMsg = "send 线程";//要发给客户端的消息
strcpy_s(buffer, sendMsg.c_str());
nBytes = strlen(buffer)+1;
nRet = WSASend(
lpIOContext->client,
&lpIOContext->wsabuf, 1, &nBytes,
dwFlags,
&(lpIOContext->Overlapped), nullptr);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
cout << "WASSend Failed::Reason Code::"<< WSAGetLastError() << endl;
closesocket(lpIOContext->client);
delete lpIOContext;
continue;
}
cout<<"["<<this_thread::get_id()<<"]线程"<<"发给客户端的消息:"<< lpIOContext->wsabuf.buf<<endl;
memset(buffer, 0, sizeof(buffer));//重置
}
else if(lpIOContext->opCode == IO_OPERATION::IO_WRITE) //a write operation complete
{
// Write operation completed, so post Read operation.
lpIOContext->opCode = IO_OPERATION::IO_READ;
nBytes = 1024;
dwFlags = 0;
lpIOContext->wsabuf.buf = buffer;
lpIOContext->wsabuf.len = nBytes;
lpIOContext->nBytes = nBytes;
ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));
nRet = WSARecv(
lpIOContext->client,
&lpIOContext->wsabuf, 1, &nBytes,
&dwFlags,
&lpIOContext->Overlapped, nullptr);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
cout << "WASRecv Failed::Reason Code1::"<< WSAGetLastError() << endl;
closesocket(lpIOContext->client);
delete lpIOContext;//就是父线程的data
continue;
}
}
}
return 0;
}
inline void start()
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
if (0 != WSAStartup(sockVersion, &wsaData))
{
cout << "Failed to retrive socket version."<< endl;
return;
}
SOCKET socketServer = WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, nullptr,0,WSA_FLAG_OVERLAPPED);
sockaddr_in server{};
server.sin_family = AF_INET;
server.sin_port = htons(8000);
server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
bind(socketServer ,reinterpret_cast<sockaddr*>(&server),sizeof(server));
listen(socketServer, 8);
//IOCP
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);//获取当前系统的信息。在cmd中Systeminfo命令可获取
auto g_ThreadCount = sysInfo.dwNumberOfProcessors * 2;
g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,nullptr, 0, g_ThreadCount);
//创建一堆线程用来读写
for( decltype(g_ThreadCount) i = 0;i < g_ThreadCount; ++i){
std::thread t(&WorkerThread);
t.detach();
}
//读写操作
while(true)
{
sockaddr_in revClientAddr{};
int _revSize = sizeof(sockaddr_in);
//阻塞在这里等客户端请求连接
SOCKET socketClient = accept(socketServer, reinterpret_cast<SOCKADDR*>(&revClientAddr), &_revSize);
if (SOCKET_ERROR != socketClient){cout << "客户端连接成功" << endl;}
//将新连接的客户端关联到IOCP
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(socketClient), g_hIOCP, 0, 0)== nullptr)
{
cout << "Binding Client Socket to IOCP Failed::Reason Code::"<< GetLastError() << endl;
closesocket(socketClient);
}
else
{
auto data(new IO_DATA);
memset(buffer, 0 ,1024);
memset(&data->Overlapped, 0 , sizeof(data->Overlapped));
data->opCode = IO_OPERATION::IO_READ;
data->nBytes = 0;
data->wsabuf.buf = buffer;
data->wsabuf.len = sizeof(buffer);
data->client = socketClient;
DWORD nBytes= 1024 ,dwFlags=0;
//客户端连接成功后,需要调用WSARecv将数据发到缓冲区,才能启动工作线程,也就是GetQueuedCompletionStatus执行
//调用WSARecv之后,buffer中不一定有数据
int nRet = WSARecv(data->client, &data->wsabuf, 1, &nBytes, &dwFlags, &data->Overlapped, nullptr);
if(nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError())){
cout << "WASRecv Failed::Reason Code::"<< WSAGetLastError() << endl;
closesocket(data->client);
delete data;
}
cout<<"Size:"<<sizeof(data) << ", 地址:"<< data<<endl;
}
}
closesocket(socketServer);
WSACleanup();
}
客户端: