zz基于完成端口的服务器代码

#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;
}

你可能感兴趣的:(zz基于完成端口的服务器代码)