一。UDP版的echo Server

与TCP版的Echo Server类似,我们从Server Socket类中派生出Echo Server类。


class 
  UDPEchoServer:  
 public 
  UDPServerSock{
 
 public 
 :
     
 explicit 
  UDPEchoServer(
                unsigned  
 short 
  server_port,
                 
 int 
  pre_buffer_size  
 = 
   
 32 
 );
     
 ~ 
 UDPEchoServer();
     
 bool 
  handEcho();
};

我们依然让handEcho()返回一个bool,true表示客户端“正常”离开(这里没用“断开”这个词是因为UDP是无连接的;另外,我们这里用recvfrom()返回小于0来表示客户端其实是“非正常”的离开了,比如连接被重置。事实上,作为UDP服务器,根本不关心客户端是在连还是已经离开),false表示客户端发出指令要求服务器端关闭。


UDPEchoServer::UDPEchoServer(
                unsigned  
 short 
  server_port,
                 
 int 
  pre_buffer_size):
UDPServerSock(server_port, pre_buffer_size)
{}

UDPEchoServer:: 
 ~ 
 UDPEchoServer()
{}

 
 bool 
  UDPEchoServer::handEcho()
{
     
 const 
  std:: 
 string 
  SHUTDOWN_CMD  
 = 
   
 " 
 /shutdown 
 " 
 ;
     
 while 
  (UDPReceive()  
 >= 
   
 0 
 ) {
        std:: 
 string 
  cmd(preBuffer, SHUTDOWN_CMD.size());
         
 if 
  (cmd  
 == 
  SHUTDOWN_CMD  
 &&
             
 preReceivedLength  
 == 
  static_cast<int>( 
 SHUTDOWN_CMD.size())) {
             
 return 
   
 false 
 ;
        }
        std::cout     
 << 
   
 " 
 Client (  
 " 
 
                     
 << 
  inet_ntoa(lastfromSockAddr.sin_addr)
                     
 << 
   
 " 
  :  
 " 
 
                     
 << 
  ntohs(lastfromSockAddr.sin_port)
                     
 << 
   
 " 
  ) sent a message. 
 " 
 
                     
 << 
  std::endl;
        UDPSetDest(lastfromSockAddr);
        UDPSendtoDest(preBuffer, preReceivedLength);
    }
     
 return 
   
 true 
 ;
}

这里跟TCP有些细微的差别。在TCP中,recv()返回0表示连接正常断开,而UDP中没有连接和断开的概念,recv()或者recvfrom()返回0表示收到一个0字节大小数据的数据报。另外,因为TCP是一对一连接的,所以一旦连接上,TCP服务器只能处理来自一个客户端的echo请求(后面会讲到多线程的使用,就可以让TCP同时处理多个客户端了);而UDP服务器则可以处理来自任何客户端的echo请求,为了返回信息到正确的客户端,我们的策略是,接收一个UDP数据包后,马上刷新发送目标地址为上一次接收地址,然后再回发数据,所以这里每次多了一个重新指定发送目的地的函数。


最后,主程序基本不需要改变:


int 
  main( 
 int 
  argc,  
 char 
 * 
  argv[])
{
     
 const 
  unsigned  
 short 
  DEFAULT_PORT  
 = 
   
 5000 
 ;
    unsigned  
 short 
  server_port  
 = 
  DEFAULT_PORT;
     
 if 
  (argc  
 == 
   
 2 
   
 && 
  atoi(argv[ 
 1 
 ])  
 > 
   
 0 
 ) {
        server_port  
 = 
  atoi(argv[ 
 1 
 ]);
    }

    UDPEchoServer echo_server(server_port);

     
 bool 
  go_on  
 = 
   
 true 
 ;
     
 while 
  (go_on){
        go_on  
 = 
  echo_server.handEcho();
    }

     
 return 
   
 0 
 ;
}


本章完整源代码下载:
Linux:
http:///files/c0l000h0s.htmlwin32:
http:///files/c0o000h08.html

 

二。UDP版的echo client

同样的,我们将UDP版的doEcho()也设计成返回bool:true表示循环继续;false表示关闭客户端。


class 
  UDPEchoClient:  
 public 
  UDPClientSock{
 
 public 
 :
     
 explicit 
  UDPEchoClient(
                 
 int 
  pre_buffer_size  
 = 
   
 32 
 );
     
 ~ 
 UDPEchoClient();
     
 bool 
  doEcho( 
 const 
  std:: 
 string 
 & 
  echo_message);
};

我们依然使用C++字符串。


UDPEchoClient::UDPEchoClient(
                 
 int 
  pre_buffer_size):
UDPClientSock(pre_buffer_size)
{}

UDPEchoClient:: 
 ~ 
 UDPEchoClient()
{}

 
 bool 
  UDPEchoClient::doEcho( 
 const 
  std:: 
 string 
 & 
  echo_message)
{
     
 if 
  ( UDPSendtoDest(echo_message.data(), echo_message.size())  
 < 
   
 0 
 ) {
         
 return 
   
 false 
 ;
    }
     
 if 
  (echo_message  
 == 
   
 " 
 /shutdown 
 " 
 ) {
         
 return 
   
 false 
 ;
    }
     
 if 
  (UDPReceive()  
 < 
   
 0 
 ) {
         
 return 
   
 false 
 ;
    }
    std::cout.write(preBuffer, preReceivedLength);
    std::cout  
 << 
  std::endl;
     
 return 
   
 true 
 ;
}

当echo_message为“空”的时候,即输入直接回车,是一个"",用C风格来说,即时'/0',从C++来说,是const char[1],其C++风格的长度echo_message.size()为0,这时候就会发送一个“0长度”的UDP数据包。


另外,我们小心设计了关闭服务器的请求,发送/shutdown后,客户端会自动返回false,表示会关闭,不再等待来自服务器的recvfrom()。否则,服务器已经关闭,recvfrom()则会一直阻塞。


int 
  main( 
 int 
  argc,  
 char 
 * 
  argv[])
{
    unsigned  
 short 
  server_port  
 = 
   
 5000 
 ;
     
 if 
  (argc  
 == 
   
 3 
   
 && 
  atoi(argv[ 
 2 
 ])  
 > 
   
 0 
 ) {
        server_port  
 = 
  atoi(argv[ 
 2 
 ]);
    }

    WinsockAPI winsockInfo;
    winsockInfo.showVersion();

    UDPEchoClient echo_client;
    echo_client.UDPSetDest(argv[ 
 1 
 ], server_port);

    std:: 
 string 
  msg;
     
 bool 
  go_on  
 = 
   
 true 
 ;
     
 while 
  (msg  
 != 
   
 " 
 /exit 
 " 
   
 && 
  go_on){
        std::cout  
 << 
   
 " 
 Echo:  
 " 
 ;
        std::getline(std::cin, msg);
        go_on  
 = 
  echo_client.doEcho(msg);
    }

     
 return 
   
 0 
 ;
}

主程序中,如果使用/exit,会先发送给服务器,然后再关闭。