程序实例(1) ---- chating room

程序实例(1) ---- chating room
客户端向服务器发送数据,并显示服务器反馈的内容。
服务器采用IOCP模型,把每个客户端发送的数据向所有连接的客户端发送一遍。
运行一个服务器,可以连接多个运行的客户端。


服务器:
#include  < iostream >
#include 
< winsock2.h >
#pragma comment (lib, 
" Ws2_32.lib " )

using   namespace  std;


char             SERVER_IP[ 32 ]     =   " 127.0.0.1 " ;
const  DWORD        SERVER_PORT          =   1555 ;
const  DWORD        BUFFER_SIZE          =   1024 ;

// OverLapped结构体扩展
struct  OVERLAPPEDPLUS
{
    OVERLAPPED ol;
    
char     buf[BUFFER_SIZE];     // 在SOCKET上收数据的buffer
     char     clientIP[ 32 ];         // 客户端IP
    u_short    clientPort;        // 客户端port
    DWORD    index;                  // SOCKET在数组中的index,从0开始
};

SOCKET    sock_array[
64 ]     =  {NULL};     // 保存客户端SOCKET
DWORD    sock_num         =   0 ;         // 记录SOCKET数


// 线程函数声明
DWORD CALLBACK ServerWorkerThread( void *  CompletionPortID) ;

// Start function
void  StartServer()
{
    WSADATA wd;
    ::WSAStartup(MAKEWORD(
2 , 2 ),  & wd);

    sockaddr_in addr;
    addr.sin_family            
=  AF_INET;
    addr.sin_port            
=  htons(SERVER_PORT);
    addr.sin_addr.s_addr    
=  inet_addr(SERVER_IP);

    SOCKET s 
=  ::socket(AF_INET, SOCK_STREAM,  0 );
    
if ( 0   !=  ::bind(s, ( const  sockaddr * ) & addr,  sizeof (addr)))
    {
        cout
<< " bind error:  " << WSAGetLastError() << endl;
        ::closesocket(s);
        ::WSACleanup();
        
return ;
    }
    ::listen(s, 
2 );

    
// 创建完成端口
    HANDLE CompetionPort  =  ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0 , 0 );

    
// 获得CPU数,创建CPU数*2+2个线程
    SYSTEM_INFO SystenInfo;
    ::GetSystemInfo(
& SystenInfo);
    
for (DWORD i = 0 ; i < SystenInfo.dwNumberOfProcessors * 2 + 2 ++ i)
    {
        DWORD ThreadID 
=   0 ;
        HANDLE ThreadHandle 
=  ::CreateThread(NULL, 0 ,ServerWorkerThread,CompetionPort, 0 , & ThreadID);
        ::CloseHandle(ThreadHandle);
    }

    cout
<< " Server Started! " << endl << endl;
    
while (TRUE)
    {
        sockaddr_in client_addr;
        
int  fromlen  =   sizeof (sockaddr_in);
        SOCKET ns 
=  ::accept(s, (sockaddr * ) & client_addr, NULL);

        
// SOCKET入数组
        sock_array[sock_num]  =  ns;
        
++ sock_num;

        
// 新客户端连接进来
        ::CreateIoCompletionPort((HANDLE)ns,CompetionPort,(DWORD)ns, 0 );
        
        
// 在堆中创建OverLapped扩展结构体的对象
        OVERLAPPEDPLUS *  olp  =  (OVERLAPPEDPLUS * )::GlobalAlloc(GPTR,  sizeof (OVERLAPPEDPLUS));

        
// 根据SOCKET取客户端的IP和PORT,保存在OVERLAPPEDPLUS里
        ZeroMemory( & client_addr,  sizeof (sockaddr_in));
        ::getpeername(ns, (sockaddr
* ) & client_addr,  & fromlen);
        strcpy_s(olp
-> clientIP,  32 , inet_ntoa(client_addr.sin_addr));
        olp
-> clientPort  =  ntohs(client_addr.sin_port);
        cout
<< endl << olp -> clientIP << " : " << olp -> clientPort << "  Connected! " << endl << endl;

        
// 保存SOCKET的index
        olp -> index  =  sock_num - 1 ;

        WSABUF    wBuffer;
        wBuffer.buf 
=  olp -> buf;
        wBuffer.len 
=  BUFFER_SIZE;
        DWORD    dwFlag 
=   0 ;        
        DWORD     dwRecvd 
=   0 ;
    
        
// 在指定SOCKET上投递一个异步的IO请求
        ::WSARecvFrom(ns,  & wBuffer,  1 & dwRecvd,  & dwFlag, (sockaddr * ) & client_addr,  & fromlen,  & (olp -> ol), NULL);
    }
    ::closesocket(s);
    ::WSACleanup();
}

// 工作者线程
DWORD CALLBACK ServerWorkerThread( void *  CompletionPortID)
{
    HANDLE CompletionPort 
=  (HANDLE) CompletionPortID; 
    DWORD BytesTransferred 
=   0
    SOCKET mySock; 
    OVERLAPPEDPLUS
*  polp; 
    DWORD dwSend 
=   0 , dwRecv  =   0 , dwFlag  =   0 ;

    
while (TRUE)
    {
        
// 从指定的CP队列中取出一个完成包,一个完成包表示一次IO操作的完成;若队列为空则阻塞
        ::GetQueuedCompletionStatus(CompletionPort,  & BytesTransferred, (LPDWORD) & mySock, (OVERLAPPED ** ) & polp, INFINITE);
        
        
if  (BytesTransferred  ==   0
        {
            
// 如果客户端关闭连接,则服务器端关闭SOCKET,释放申请的堆内存空间
             -- sock_num;
            ::closesocket(mySock);
            DWORD dwIndex 
=  polp -> index;
            ::GlobalFree(polp);
            cout
<< endl << polp -> clientIP << " : " << polp -> clientPort << "  Disconnected! " << endl << endl;

            
// 删掉的SOCKET后面往前挪
             for (DWORD i = dwIndex; i < sock_num;  ++ i)
            {
                sock_array[i] 
=  sock_array[i + 1 ];
            }
            
continue ;     // 不要关闭工作者线程
        }
        
        WSABUF    wBuffer;
        wBuffer.buf 
=  polp -> buf;
        wBuffer.len 
=  BUFFER_SIZE;      

        
// 将收到的数据反馈给每个客户端
         for (DWORD i = 0 ; i < sock_num;  ++ i)
        {
            ::send(sock_array[i], polp
-> buf, ( int )strlen(polp -> buf) + 1 0 );
        }
    
        
// 输出收到的数据,并投递一个WSARecv请求
        cout << polp -> clientIP << " " << polp -> buf << endl;
        ::WSARecv(mySock, 
& wBuffer,  1 & dwSend,  & dwFlag,  & (polp -> ol), NULL);
    }

    
return   0 ;
}

int  main()
{
    cout
<< " Input your IP: " << endl;
    cin
>> SERVER_IP;
    StartServer();
    
return   0 ;
}

客户端:
#include  < iostream >
#include 
< winsock2.h >
#pragma comment (lib, 
" Ws2_32.lib " )

using   namespace  std;

char  SERVER_IP[ 32 ]     =   " 127.0.0.1 " ;
const  DWORD    SERVER_PORT     =   1555 ;

// 线程函数声明
DWORD CALLBACK ReceiveThread( void *  CompletionPortID) ;

int  main()
{
    cout
<< " Input server IP: " << endl;
    cin
>> SERVER_IP;

    
// 初始化使用socket函数要用到的dll
    WSADATA wd;
    WSAStartup(MAKEWORD(
2 , 2 ),  & wd);

    
// 服务器地址信息
    sockaddr_in addr;
    addr.sin_family            
=  AF_INET;
    addr.sin_port              
=  htons(SERVER_PORT);
    addr.sin_addr.s_addr       
=  inet_addr(SERVER_IP);

    
// 创建套接字,并与服务器连接
    SOCKET s  =  ::socket(AF_INET, SOCK_STREAM,  0 );

    cout
<< " Connecting to:  " << SERVER_IP << " : " << SERVER_PORT << endl;
    
while ( 0   !=  ::connect(s, ( const  sockaddr * ) & addr,  sizeof (addr)))
    {

    }
    cout
<< " Connected! " << endl << endl;

    DWORD ThreadID 
=   0 ;
    
// 创建线程,传入参数为SOCKET
    HANDLE ThreadHandle  =  ::CreateThread(NULL, 0 ,ReceiveThread, & s, 0 , & ThreadID);
    ::CloseHandle(ThreadHandle);

    
// 发送数据
     while ( 1 )
    {
        
char  cData[ 1024 =  { 0 };
        cin.getline(cData, 
1024 );
        ::send(s, cData, (
int )strlen(cData) + 1 0 );
    }

    
// 关闭套接字
    ::closesocket(s);

    
// ws2.dll的收尾工作
    ::WSACleanup();
    
return   0 ;
}

DWORD CALLBACK ReceiveThread(
void *  p) 
{
    
while (TRUE)
    {
        
char  cBuffer[ 1024 =  { 0 };
        
if (SOCKET_ERROR  ==  ::recv( * (SOCKET * )p, cBuffer,  1024 0 ))
        {
            cout
<< " \nServer Closed! " << endl;
            
break ;
        }
        cout
<< " ****** " << cBuffer << endl;
    }

    
return   0 ;
}


你可能感兴趣的:(程序实例(1) ---- chating room)