//
///author:Jeson Yang
///date:2014.09.28
//
#include <WinSock2.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define TCP_PORT 10066
#define MAX_BUFF_LENGTH 1024
typedef struct PER_IO_OPERATION_DATA
{
WSAOVERLAPPED overlap;
WSABUF buf;
char szMessage[MAX_BUFF_LENGTH];
DWORD numberOfBytesRecved;
DWORD flag;
}PER_IO_OPERATION_DATA,*LPPER_IO_OPERATION_DATA;
DWORD WINAPI WorkerThread(LPVOID CompletionPortId);
void main()
{
WORD dwVersion = MAKEWORD(2, 2);
WSAData wsaData;
WSAStartup(dwVersion, &wsaData);
//初始化完成端口
HANDLE completionport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
SYSTEM_INFO sysInfo;
DWORD threadId;
::GetSystemInfo(&sysInfo);
for (int i = 0; i < (int)sysInfo.dwNumberOfProcessors; i++)
{
CreateThread(NULL, 0, WorkerThread, completionport, 0, &threadId);
}
//创建套接字
SOCKET sClient;
SOCKET sScok = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN localAddr, clientAddr;
::memset(&localAddr, 0, sizeof(SOCKADDR_IN));
//绑定套接字
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);//inet_addr("127.0.0.1");
localAddr.sin_port = htons(TCP_PORT);
if (::bind(sScok, (SOCKADDR*)&localAddr, sizeof(sockaddr)))
{
::OutputDebugString(_T("bind error!"));
return;
}
//开始监听
::listen(sScok, 1);
int iaddrSize = sizeof(SOCKADDR_IN);//此处应该先赋值,或者在accept出填NULL,如果此处为负数,accept将不会死锁在那里。
PER_IO_OPERATION_DATA *lpPerData = NULL;
while (true)
{
//如果有客户请求连接,则继续,否则等待
sClient = ::accept(sScok, (sockaddr*)&clientAddr, &iaddrSize);//NULL);
//client中保存用户信息
printf("Accepted client: %s: %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
//将这个最新到来的客户套接字和完成端口绑定到一起
//第三个参数表示传递的参数,这里就传递的客户套接字地址。最后一个参数为0 表示有和CPU一样的进程数。即1个CPU一个线程
CreateIoCompletionPort((HANDLE)sClient, completionport, (DWORD)sClient, 0);
lpPerData = (LPPER_IO_OPERATION_DATA)::HeapAlloc(::GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(PER_IO_OPERATION_DATA));
lpPerData->buf.len = MAX_BUFF_LENGTH;
lpPerData->buf.buf = lpPerData->szMessage;
//异步接收消息,立刻返回。
::WSARecv(sClient, &lpPerData->buf, 1, &lpPerData->numberOfBytesRecved, &lpPerData->flag,&lpPerData->overlap, NULL);
}
::PostQueuedCompletionStatus(completionport, 0xFFFFFFFF, 0, NULL);
::CloseHandle(completionport);
closesocket(sScok);
WSACleanup();
}
//工作者线程有一个参数,是指向完成端口的句柄
DWORD WINAPI WorkerThread(LPVOID CompletionPortId)
{
HANDLE completionPort = (HANDLE)CompletionPortId;
DWORD dwByteTransferred;
SOCKET sClient;
LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
while(true)
{
::GetQueuedCompletionStatus(completionPort,
&dwByteTransferred,
(PULONG_PTR)&sClient,
(LPOVERLAPPED *)&lpPerIOData,
INFINITE);
if (dwByteTransferred == 0xFFFFFFFF)
{
return 0;
}
if (dwByteTransferred == 0)
{
::closesocket(sClient);
::HeapFree(::GetProcessHeap(), HEAP_ZERO_MEMORY, lpPerIOData);//释放结构体
}
else
{
lpPerIOData->szMessage[dwByteTransferred] = '\0';
::send(sClient, lpPerIOData->szMessage, dwByteTransferred, 0);//将接收到的消息返回
memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
lpPerIOData->buf.len = MAX_BUFF_LENGTH;
lpPerIOData->buf.buf = lpPerIOData->szMessage;
//异步接收消息,立刻返回。
::WSARecv(sClient, &lpPerIOData->buf, 1, &lpPerIOData->numberOfBytesRecved, &lpPerIOData->flag,&lpPerIOData->overlap, NULL);
}
}
return 0;
}