#pragma once
#include<Windows.h>
#include<WinSock2.h>
#include<stdio.h>
#include<process.h>
#include<vector>
#pragma comment(lib,"ws2_32.lib")
//端口号
#define SERVER_PORT 9527
//IO操作类型
#define OP_READ 1
#define OP_WRITE 2
#define OP_ACCEPT 3
#define OP_END -1
#define BUF_SIZE 1024
//自定义结构,即“完成键”(单句柄数据)
typedef struct tagPER_HANDLE_DATA
{
SOCKET Socket;
SOCKADDR_STORAGE ClientAddr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;
//单个I/O操作数据
typedef struct tagPer_IO_DATA
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
char buffer[1024];
int BufferLen;
int OperationType; //IO操作类型
}PER_IO_DATA, *LPPER_IO_DATA;
#include"SelectServer.h"
SOCKET listenSock;
HANDLE completePort;
//线程句柄列表
std::vector<HANDLE> workthread;
//IO操作状态转换
//可以考虑这里使用状态模式
void ChangeHandleData(LPPER_IO_DATA perIOdata, int state)
{
switch (state)
{
case OP_ACCEPT:
perIOdata->OperationType = OP_READ;
memset(&(perIOdata->Overlapped), 0, sizeof(OVERLAPPED));
memset(&(perIOdata->DataBuf), 0, sizeof(WSADATA));
perIOdata->DataBuf.buf = perIOdata->buffer;
perIOdata->DataBuf.len = BUF_SIZE;
break;
case OP_READ:
perIOdata->OperationType = OP_WRITE;
memset(&(perIOdata->Overlapped), 0, sizeof(OVERLAPPED));
//发送给客户端的数据
char sendClientInfo[] = "sendClient";
perIOdata->DataBuf.buf = sendClientInfo;
perIOdata->DataBuf.len = strlen(sendClientInfo);
break;
case OP_WRITE:
perIOdata->OperationType = OP_READ;
memset(&(perIOdata->Overlapped), 0, sizeof(OVERLAPPED));
memset(&(perIOdata->DataBuf), 0, sizeof(WSADATA));
perIOdata->DataBuf.buf = perIOdata->buffer;
perIOdata->DataBuf.len = BUF_SIZE;
break;
}
}
void SendHandleData(LPPER_IO_DATA perIoData, LPPER_HANDLE_DATA perHandleData)
{
int result = 0;
DWORD dwSize;
DWORD nFlag = 0;
if (perIoData->OperationType == OP_READ)
{
memset(&(perIoData->DataBuf), 0, sizeof(WSADATA));
memset(&(perIoData->Overlapped), 0, sizeof(OVERLAPPED));
if (SOCKET_ERROR == WSARecv(perHandleData->Socket, &(perIoData->DataBuf), 1, &dwSize, &nFlag, &(perIoData->Overlapped), NULL))
{
//如果调用失败
//调用WSAGetLastError查看错误码
printf("WSARecv() Failed:%d\n", WSAGetLastError());
return;
}
}
else if (perIoData->OperationType == OP_WRITE)
{
result = WSASend(perHandleData->Socket, &(perIoData->DataBuf), 1, &dwSize, nFlag, &(perIoData->Overlapped), NULL);
if ((result == SOCKET_ERROR) && (::WSAGetLastError() != ERROR_IO_PENDING))
{
printf("WSASend() Failed:%d\n", ::WSAGetLastError());
::closesocket(perHandleData->Socket);
}
}
else if (perIoData->OperationType == OP_END)
{
::closesocket(perHandleData->Socket);
}
}
//处理线程
UINT WINAPI WorkThread(LPVOID lpaameter)
{
return 0;
}
int main()
{
PER_HANDLE_DATA* perhandledata = NULL;
WSADATA data = { 0 };
if (::WSAStartup(MAKEWORD(2, 2), &data) != 0)
{
printf("Call WSAStartup Failed!");
return 0;
}
listenSock = WSASocket(AF_INET, SOCK_STREAM,IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (listenSock == INVALID_SOCKET)
{
printf("Call socket Failed!");
return 0;
}
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(listenSock, (sockaddr*)&addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
printf("Call bind Failed!");
return 0;
}
if (::listen(listenSock, SOMAXCONN) == SOCKET_ERROR)
{
printf("Call listen Failed!");
return 0;
}
SYSTEM_INFO si;
::GetSystemInfo(&si);
size_t threadCount = si.dwNumberOfProcessors * 2;
//创建完成端口
completePort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, threadCount);
if (completePort == NULL)
{
printf("Create CompletionPort Failed!");
return 0;
}
//创建处理线程
for (size_t i = 0; i < threadCount; i++)
{
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, WorkThread, completePort, 0, NULL);
if (hThread)
{
workthread.push_back(hThread);
}
}
int remotLen;
SOCKADDR_IN adr;
remotLen = sizeof(SOCKADDR_IN);
while (TRUE)
{
SOCKET hAccept = ::accept(listenSock, (sockaddr*)&adr, &remotLen);
if (hAccept == INVALID_SOCKET)
{
continue;
}
perhandledata = (PER_HANDLE_DATA*)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
perhandledata->Socket = hAccept;
memcpy(&perhandledata->ClientAddr, &adr, remotLen);
if (perhandledata == NULL)
{
printf("perhandledata == null!");
::closesocket(hAccept);
}
if (::CreateIoCompletionPort((HANDLE)hAccept, completePort, (DWORD)perhandledata, 0) == NULL)
{
//这里应该详细考察调用失败的原因
::GlobalFree((HGLOBAL)perhandledata);
::closesocket(hAccept);
continue;
}
LPPER_IO_DATA perIodata = (LPPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
ChangeHandleData(perIodata, OP_ACCEPT);
SendHandleData(perIodata, perhandledata);
}
return 0;
}