下面是服务器端的代码:
#pragma once//////////////////////////////////////////////////////////////////////////
#include <Winsock2.h>
#pragma comment( lib, "WS2_32.LIB")
#include <process.h> // 编译器需要开启多线程(MT)
//////////////////////////////////////////////////////////////////////////
// 参数定义
const UINT MAX_LISTEN = 5; // 最大监听数量
const UINT MAX_WAIT_TIME = 100; // 最大等待事件
const UINT CLIENT_EACH_THREAD = 5; // 每线程具备的客户端数量
const UINT MAX_THREAD_NUM = 5; // 线程池中的最大线程数
//////////////////////////////////////////////////////////////////////////typedef struct _tagClientInfo
{
SOCKET socket;
BOOL bUsed;
}tagClientInfo, *LPClientInfo;typedef struct _tagThread
{
HANDLE m_hThread;
UINT m_uiSpare;
tagClientInfo m_aryClientInfo[CLIENT_EACH_THREAD];
WSAEVENT m_aryEvent[CLIENT_EACH_THREAD];
LPVOID m_pParam;
} tagThread, *LPThread;
//////////////////////////////////////////////////////////////////////////template <class T>
class CNetServer
{
public:
CNetServer(void);
virtual ~CNetServer(void);public:
static BOOL Initialize(void);
static BOOL Uninitialize(void); BOOL Create( USHORT usPort,
INT nType = SOCK_STREAM,
INT nProtocol = IPPROTO_TCP);
BOOL Close(void);protected:
SOCKET m_Socket;
SOCKADDR_IN m_Addr; WSAEVENT m_wsaEvent;
HANDLE m_hThread;
tagThread m_aryThread[MAX_THREAD_NUM];
BOOL m_bExit; // 线程退出标志
volatile LONG m_lThreadNum; // 线程数 进入+1 退出-1
static unsigned _stdcall ListenThread(LPVOID pVoid);
static unsigned _stdcall ClientThread(LPVOID pVoid);
BOOL OnAccept(void);
BOOL OnRead(SOCKET clientSocket);
BOOL OnSend(SOCKET clientSocket);};//////////////////////////////////////////////////////////////////////////
// 实现部分template <class T>
CNetServer<T>::CNetServer(void)
: m_hThread(NULL)
, m_wsaEvent(NULL)
{
}template <class T>
CNetServer<T>::~CNetServer(void)
{
}
//-------------------------------------------------------------------------
// Function Name    :Initialize [static]
// Parameter(s)     :void
// Return           :成功与否
// Memo             :初始化Winsock环境
//-------------------------------------------------------------------------
template <class T>
BOOL CNetServer<T>::Initialize(void)
{
WSAData wsaData;
memset( &wsaData, 0, sizeof(wsaData));
return ( WSAStartup( MAKEWORD( 2, 2), &wsaData) == 0 );
}//-------------------------------------------------------------------------
// Function Name    :Uninitialize [static]
// Parameter(s)     :void
// Return           :成功与否
// Memo             :卸载Winsock环境
//-------------------------------------------------------------------------
template <class T>
BOOL CNetServer<T>::Uninitialize(void)
{
return ( WSACleanup() == 0 );
}//-------------------------------------------------------------------------
// Function Name    :Create
// Parameter(s)     :USHORT usPort 端口号 [in]
// :INT nType 类型 [in]
// :INT nProtocol 协议 [in]
// Return           :成功与否
// Memo             :创建服务器
//-------------------------------------------------------------------------
template <class T>
BOOL CNetServer<T>::Create( USHORT usPort,
INT nType /* = SOCK_STREAM */,
INT nProtocol /* = IPPROTO_TCP */)
{
// 创建套接字
m_Socket = socket( AF_INET, nType, nProtocol);
if( m_Socket == INVALID_SOCKET )
return FALSE; memset( &m_Addr, 0, sizeof(m_Addr));
m_Addr.sin_port = htons(usPort);
m_Addr.sin_family = AF_INET;
m_Addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定
if( bind( m_Socket, (LPSOCKADDR)&m_Addr, sizeof(m_Addr)) == SOCKET_ERROR )
return FALSE; // 监听
if( listen( m_Socket, MAX_LISTEN) == SOCKET_ERROR )
return FALSE; // 创建Event
m_wsaEvent = WSACreateEvent();
if( m_wsaEvent == NULL )
return FALSE; // 设置异步事件
if( WSAEventSelect( m_Socket, m_wsaEvent, FD_ACCEPT) == SOCKET_ERROR )
return FALSE; // 初始化线程池
for( int i = 0; i < MAX_THREAD_NUM; i++)
{
for( int j = 0; j < CLIENT_EACH_THREAD; j++)
{
m_aryThread[i].m_aryClientInfo[j].bUsed = FALSE;
m_aryThread[i].m_aryEvent[j] = NULL;
}

m_aryThread[i].m_uiSpare = CLIENT_EACH_THREAD;
m_aryThread[i].m_pParam = this; m_aryThread[i].m_hThread = (HANDLE)_beginthreadex( NULL
, 0
, &ClientThread
, &m_aryThread[i]
, CREATE_SUSPENDED
, NULL
);
} // 启动监听线程
m_lThreadNum = 0l;
m_bExit = FALSE;
m_hThread = (HANDLE)_beginthreadex( NULL,
0,
&ListenThread,
this,
0,
NULL);
if( m_hThread == NULL )
return FALSE;
return TRUE;
}//-------------------------------------------------------------------------
// Function Name    :Close
// Parameter(s)     :void
// Return           :成功与否
// Memo             :关闭服务器
//-------------------------------------------------------------------------
template <class T>
BOOL CNetServer<T>::Close(void)
{
// 等待线程退出
m_bExit = TRUE;
while( m_lThreadNum > 0 )
{
Sleep(MAX_WAIT_TIME);
} // 关闭事件
WSACloseEvent(m_wsaEvent);
m_wsaEvent = NULL; // 关闭线程
::CloseHandle(m_hThread); // 关闭Socket
closesocket(m_Socket); return TRUE;
}

解决方案 »

  1.   

    //-------------------------------------------------------------------------
    // Function Name    :ListenThread
    // Parameter(s)     :LPVOID pVoid 线程参数 [in]
    // Return           :unsigned
    // Memo             :监听线程
    //-------------------------------------------------------------------------
    template <class T>
    unsigned _stdcall CNetServer<T>::ListenThread(LPVOID pVoid)
    {
    T * pThis = static_cast<T*>(pVoid); InterlockedIncrement( &(pThis->m_lThreadNum) );

    WSANETWORKEVENTS wsaEvents;
    DWORD dwIndex = 0; // 循环等待Accept
    while( !pThis->m_bExit )
    {
    dwIndex = WSAWaitForMultipleEvents( 1, &(pThis->m_wsaEvent), FALSE, MAX_WAIT_TIME, FALSE);

    if( dwIndex == WSA_WAIT_FAILED ||
    dwIndex == WSA_WAIT_TIMEOUT )
    {
    continue;
    } // 枚举网络异步事件
    memset( &wsaEvents, 0, sizeof(wsaEvents));
    WSAEnumNetworkEvents( pThis->m_Socket, pThis->m_wsaEvent, &wsaEvents); if( (wsaEvents.lNetworkEvents & FD_ACCEPT) == FD_ACCEPT )
    {
    pThis->OnAccept();
    }
    WSAResetEvent(pThis->m_wsaEvent);
    } InterlockedDecrement( &(pThis->m_lThreadNum) ); return 0;
    }
    //-------------------------------------------------------------------------
    // Function Name    :OnAccept
    // Parameter(s)     :void
    // Return           :BOOL
    // Memo             :接受连接
    //-------------------------------------------------------------------------
    template <class T>
    BOOL CNetServer<T>::OnAccept(void)
    {
    sockaddr_in clientAddr;
    memset( &clientAddr, 0, sizeof(clientAddr));
    int nLen = sizeof(clientAddr); // 接受连接
    SOCKET ClientSocket = accept( m_Socket, (struct sockaddr*)&clientAddr, &nLen);
    if( ClientSocket == INVALID_SOCKET )
    {
    return FALSE;
    }
    // 查找可用的线程
    for( int i = 0; i < MAX_THREAD_NUM; i++)
    {
    if( m_aryThread[i].m_uiSpare <= 0 )
    {
    continue;
    } for( int j = 0; j < CLIENT_EACH_THREAD; j++)
    {
    if( m_aryThread[i].m_aryClientInfo[j].bUsed )
    {
    continue;
    } m_aryThread[i].m_aryClientInfo[j].socket = ClientSocket;
    m_aryThread[i].m_aryEvent[j] = WSACreateEvent(); if( WSAEventSelect( m_aryThread[i].m_aryClientInfo[j].socket
    , m_aryThread[i].m_aryEvent[j]
    , FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR )
    {
    WSACloseEvent( m_aryThread[i].m_aryEvent[j] );
    closesocket(ClientSocket);
    return FALSE;
    }// if if( m_aryThread[i].m_uiSpare == CLIENT_EACH_THREAD )
    {
    ResumeThread(m_aryThread[i].m_hThread);
    } m_aryThread[i].m_aryClientInfo[j].bUsed = TRUE;
    m_aryThread[i].m_uiSpare --;
    return TRUE;
    }// for
    } closesocket(ClientSocket);    return FALSE;
    }
    //-------------------------------------------------------------------------
    // Function Name    :ClientThread
    // Parameter(s)     :LPVOID pVoid
    // Return           :unsigned
    // Memo             :
    //-------------------------------------------------------------------------
    template <class T>
    unsigned _stdcall CNetServer<T>::ClientThread(LPVOID pVoid)
    {
    LPThread pThread = static_cast<LPThread>(pVoid);
    T * pT = static_cast<T*>(pThread->m_pParam); DWORD dwIndex = 0;
    WSANETWORKEVENTS wsaEvents; InterlockedIncrement( &(pT->m_lThreadNum) ); while( !pT->m_bExit )
    {
    dwIndex = WSAWaitForMultipleEvents( CLIENT_EACH_THREAD
    , pThread->m_aryEvent
    , FALSE
    , MAX_WAIT_TIME
    , FALSE
    ); if( dwIndex == WSA_WAIT_FAILED ||
    dwIndex == WSA_WAIT_TIMEOUT )
    {
    continue;
    } dwIndex = dwIndex - WSA_WAIT_EVENT_0; // 枚举网络异步事件
    memset( &wsaEvents, 0, sizeof(wsaEvents));
    WSAEnumNetworkEvents( pThread->m_aryClientInfo[dwIndex].socket
    , pThread->m_aryEvent[dwIndex]
    , &wsaEvents
    ); if( (wsaEvents.lNetworkEvents & FD_READ) == FD_READ )
    {
    pT->OnRead( pThread->m_aryClientInfo[dwIndex].socket );
    }
    else if( (wsaEvents.lNetworkEvents & FD_CLOSE) == FD_CLOSE )
    {
    closesocket( pThread->m_aryClientInfo[dwIndex].socket );
    pThread->m_aryClientInfo[dwIndex].bUsed = FALSE;
    pThread->m_uiSpare ++;
    // 挂起线程
    if( pThread->m_uiSpare == CLIENT_EACH_THREAD )
    {
    SuspendThread(pThread->m_hThread);
    }
    continue;
    } WSAResetEvent(pThread->m_aryEvent[dwIndex]);
    }// while InterlockedDecrement( &(pT->m_lThreadNum) );
    return 0;
    }
    template <class T>
    BOOL CNetServer<T>::OnRead(SOCKET clientSocket)
    {
    return TRUE;
    }template <class T>
    BOOL CNetServer<T>::OnSend(SOCKET clientSocket)
    {
    return TRUE;
    }
      

  2.   

    下面是测试代码服务器端 客户端就不写了:
    #include "stdafx.h"
    #include "NetServer.h"class CMyServer : public CNetServer<CMyServer>
    {
    public:
    CMyServer () {};
    ~CMyServer() {};
    BOOL OnRead(SOCKET clientSocket)
    {
    char szBuf[MAX_PATH] = {'\0'};;
    if( recv( clientSocket, szBuf, MAX_PATH, 0) == SOCKET_ERROR )
    {
    return FALSE;
    } return this->OnSend(clientSocket);
    }; BOOL OnSend(SOCKET clientSocket)
    {
    int nBytes = 2048;
    char szBuf[2048] = {"tttttttttt"}; int nLeft = nBytes;
    int nIndex = 0;
    while( nLeft > 0 )
    {
    int nRet = send( clientSocket, &szBuf[nIndex], nLeft, 0);
    if( nRet == SOCKET_ERROR )
    {
    return FALSE;
    }
    nLeft -= nRet;
    nIndex += nRet;
    } return TRUE;
    }
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
    CMyServer::Initialize(); CMyServer server;
    server.Create( 30010);
    Sleep(50000); server.Close(); CMyServer::Uninitialize();
    return 0;
    }
      

  3.   

    呵呵,写的还比较幼稚。1)封装得还不够完全,应用层不应该再负责接收数据了,而是负责处理数据了。
    2)使用固定长度的char szBuf[2048]接收数据,很容易出现粘包问题,从而导致好象“丢包”了。
    3)建议楼主看看:http://www.vczx.com/article/show.php?id=1041 后再改进改进。
      

  4.   

    大致看了一下,感觉不错。
    关键是要考虑到多种使用要求。
    比如说有人可能想以最快的速度写一个程序,但不考虑错误处里和负载性能。
    也有人要写专业的server程序。
    所以,你的架构里要支持很多变化的功能。刚开始需求不会很快明确的,还需要很长时间才能成熟啊。
    希望楼主将来能有更好的产品。
      

  5.   

    自己封装来 封装去 都不如意主要是需求多变 决定先用ACE套着用以后自己写的成熟了再用上吧谢谢楼上的大哥们