#include "stdio.h"
#include "stdlib.h"
#include "winsock2.h"
#include "mswsock.h"
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "mswsock.lib")
/*
重叠IO有三种方法:
1. 一次投递分配一次IO,完成时释放
2. 为每个套接口绑定一个IO
3. 采用IO池,每次投递从池中取出一个IO,完成时放回
特点:
1. 当内存分配速度达到一定程度时,系统内存会呈线性增加
2. 一个套接口可能有多个投递操作
3. 在大型的服务器开发中,推荐该方法
多次IO投递分析:
1. 多次OP_READ,例如,连续投递三次OP_READ,当投递完成时,系统分别会把接收到的数据拷贝到OV的缓冲区中
2. 多次OP_WRITE,由于不知道成功发送了多少字节,所以必须保留数据到发送缓冲区,在操作完成之前,不能再投递发送操作了,
否则会造成数据发送混乱。
解决办法:
发送数据时,
int SendData(pSocket, szbuf, len)
{
EnterCriticalSection(&pSocket->cs);
memcpy(pSocket->gBufSend + pSocket->gLenSend, szbuf, len);
pSocket->gLenSend += len;
if (pSocket->nIOSend == 0)
{
WSASend(...);
pSocket->nIOSend ++;
}
LeaveCriticalSection(&pSocket->cs);
}
投递完成时,
case IO_WRITE:
{
EnterCriticalSection(&pSocket->cs);
memcpy(pSocket->gBufSend, pSocket->gBufSend + dwTrans, pSocket->gLenSend - dwTrans);
pSocket->gLenSend -= dwTrans;
if (pSocket->gLenSend > 0 && pSocket->nIOSend == 0)
{
WSASend(...);
pSocket->nIOSend ++;
}
LeaveCriticalSection(&pSocket->cs);
注意:坚决禁止在一个套接口上多次投递WRITE操作,使用SendData()方法可以保证始终只有一次WRITE操作
*/
///////////////////////////////////////////////////////////////////////////////
#define MAX_BUFF_SIZE 4096
#define MAX_PACK_SIZE 1024
//HANDLE结构,作为每个SOCKET的KEY,保存每个SOCKET的用户缓冲区
struct PER_HANDLE_DATA
{
SOCKET s;
int nIOSend;
int nIORecv;
char gBufRecv[MAX_BUFF_SIZE];
int gLenRecv;
char gBufSend[MAX_BUFF_SIZE];
int gLenSend;
};
typedef struct PER_HANDLE_DATA PER_HANDLE_DATA_t;
//IO结构,向套接口投递操作时使用
struct PER_IO_DATA
{
OVERLAPPED ol;
SOCKET s;
char szBuf[MAX_PACK_SIZE*2];
DWORD dwBytes;
DWORD dwFlags;
int op;
#define OP_ACCEPT 1
#define OP_READ 2
#define OP_WRITE 3
#define OP_QUIT 4
};
typedef struct PER_IO_DATA PER_IO_DATA_t;
///////////////////////////////////////////////////////////////////////////////
//向侦听套接口投递ACCEPT操作
void PostAccept(SOCKET socks, PER_IO_DATA_t *pIO)
{
pIO->s = socket(AF_INET, SOCK_STREAM, 0);
pIO->op = OP_ACCEPT;
AcceptEx(socks, pIO->s, pIO->szBuf,
0,
sizeof(struct sockaddr) + 16,
sizeof(struct sockaddr) + 16,
&pIO->dwBytes,
&pIO->ol);
}
//向客户套接口投递RECV操作
void PostRecv(SOCKET sockc, PER_IO_DATA_t *pIO)
{
WSABUF wbuf;
wbuf.buf = pIO->szBuf;
wbuf.len = sizeof(pIO->szBuf);
pIO->op = OP_READ;
WSARecv(sockc, &wbuf, 1, &pIO->dwBytes, &pIO->dwFlags, &pIO->ol, NULL);
}
//向客户套接口投递Send操作
void PostSend(SOCKET sockc, PER_IO_DATA_t *pIO, char *szbuf, int nlen)
{
WSABUF wbuf;
wbuf.buf = szbuf;
wbuf.len = nlen;
pIO->op = OP_WRITE;
WSASend(sockc, &wbuf, 1, &pIO->dwBytes, pIO->dwFlags, &pIO->ol, NULL);
}
PER_IO_DATA_t *GetIO()
{
PER_IO_DATA_t *IO = new PER_IO_DATA_t;
memset(IO, 0, sizeof(PER_IO_DATA_t));
return IO;
}
void FreeIO(PER_IO_DATA_t *pIO)
{
delete pIO;
}
///////////////////////////////////////////////////////////////////////////////
void PostAccept(SOCKET socks, PER_IO_DATA_t *pIO);
void PostRecv(SOCKET sockc, PER_IO_DATA_t *pIO);
PER_IO_DATA_t *GetIO(); //模拟从IO池获取
void FreeIO(); //将IO放回IO池
int main()
{
HANDLE hIOCP = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (hIOCP == NULL)
{
printf("CreateIoCompletionPort() failed!/n");
return -1;
}
WSADATA wsa;
WSAStartup(MAKEWORD(2,2), &wsa);
SOCKET socks = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(9988);
int ret = bind(socks, (struct sockaddr*)&sin, sizeof(sin));
if (ret == SOCKET_ERROR)
{
printf("bind() failed!/n");
return -1;
}
listen(socks, 5);
PER_HANDLE_DATA_t *Handle = new PER_HANDLE_DATA_t;
memset(Handle, 0, sizeof(PER_HANDLE_DATA_t));
Handle->s = socks;
::CreateIoCompletionPort((HANDLE)Handle->s, hIOCP, (DWORD)Handle, 0);
for (int i = 0; i < 1; i ++)
{
PostAccept(socks, GetIO());
}
while (TRUE)
{
PER_HANDLE_DATA_t *pHandle;
PER_IO_DATA_t *pIO;
DWORD dwBytes;
int ret =::GetQueuedCompletionStatus(hIOCP, &dwBytes,
(LPDWORD)&pHandle, (LPOVERLAPPED*)&pIO, INFINITE);
if (ret == 0)
{
printf("[%d] error/n", pHandle->s);
closesocket(pHandle->s);
delete pHandle;
FreeIO(pIO);
continue;
}
else if (dwBytes == 0 &&
(pIO->op == OP_READ || pIO->op == OP_WRITE) )
{
printf("[%d] 连接断开/n", pHandle->s);
closesocket(pHandle->s);
delete pHandle;
FreeIO(pIO);
continue;
}
switch (pIO->op)
{
case OP_ACCEPT:
{
printf("接受[%d]的连接/n", pIO->s);
PER_HANDLE_DATA_t *Handle = new PER_HANDLE_DATA_t;
memset(Handle, 0, sizeof(PER_HANDLE_DATA_t));
Handle->s = pIO->s;
::CreateIoCompletionPort((HANDLE)Handle->s, hIOCP, (DWORD)Handle, 0);
FreeIO(pIO);
PostRecv(Handle->s, GetIO());
Handle->nIORecv ++;
PostAccept(pHandle->s, GetIO());
}
break;
case OP_READ:
{
pHandle->nIORecv --;
memcpy(pHandle->gBufRecv + pHandle->gLenRecv,
pIO->szBuf, dwBytes); //将已接收到字节拷贝到全局接收缓存
pHandle->gLenRecv += dwBytes;
pHandle->gBufRecv[pHandle->gLenRecv] = 0;
FreeIO(pIO);
printf("[%d] %s/n", pHandle->s, pHandle->gBufRecv);
memcpy(pHandle->gBufSend, pHandle->gBufRecv, pHandle->gLenRecv);
pHandle->gLenSend += pHandle->gLenRecv;
pHandle->gLenRecv = 0;
if (pHandle->nIOSend == 0)
{
PostSend(pHandle->s, GetIO(), pHandle->gBufSend, pHandle->gLenSend);
pHandle->nIOSend ++;
}
PostRecv(pHandle->s, GetIO());
}
break;
case OP_WRITE:
{
pHandle->nIOSend --;
memcpy(pHandle->gBufSend, pHandle->gBufSend + dwBytes, pHandle->gLenSend - dwBytes);
pHandle->gLenSend -= dwBytes;
FreeIO(pIO);
if (pHandle->gLenSend > 0 &&
pHandle->nIOSend == 0)
{
PostSend(pHandle->s, GetIO(), pHandle->gBufSend, pHandle->gLenSend);
pHandle->nIOSend ++;
}
}
break;
case OP_QUIT:
{
}
break;
}
}
return 0;
}