socket 编程入门教程(五)UDP原理:2、设计UDP client类

socket 编程入门教程(五)UDP原理:2、设计UDP client类
UDP的客户端看起来几乎就是服务器端的翻版,甚至比服务器端更简单——因为不需要bind()本机地址:
class  UDPClientSock:  public  BaseSock {
protected :
    mutable sockaddr_in lastfromSockAddr;
    sockaddr_in destinationSockAddr;
    
char *  preBuffer;
    
int  preBufferSize;
    mutable 
int  preReceivedLength;
public :
    
explicit  UDPClientSock( int  pre_buffer_size  =   32 );
    
virtual   ~ UDPClientSock();
    
void  UDPSetDest( const   char *  dest_IP,
            
const  unsigned  short &  dest_port);
    
void  UDPSetDest( const  sockaddr_in &  dest_sock_addr);
    
int  UDPReceive()  const ;
    
int  UDPSendtoDest( const   char *  send_data,
            
const   int &  data_length)  const ;
};
在最初设计这个类的时候,我曾经考虑过安排一个服务器地址的私有数据成员,并且在构造函数里面指定服务器的地址。但是,后来我觉得使用“目的地”比“服务器”更加能体现出UDP无连接的本质特点。TCP之所以有个服务器,是因为TCP的客户端只能和自己的服务器端通讯。而UDP的客户端可以与任何一个UDP端口通讯——只要知道对方的地址(IP地址和UDP端口)就可以发送数据包。况且,在网络情况越来越复杂的今天,很多服务器都不仅仅使用一个IP地址或者域名,比如网站和游戏服务器,而对于客户端来说,只是在意连接到了指定的网站,比如google,而并不清楚是连接到google的哪个服务器。程序内部可能会根据网络条件对具体连接的服务器地址进行调整,所以,可以随时根据具体情况指定“目的地”,而不是一开始就指定一个“服务器”地址,这种策略显得更加灵活。
通常情况下,客户端也并不在意lastfromSockAddr,因为最后一次来向的地址,往往就是目的地服务器的地址。我们说过,服务器的端口是指定的,这是为了让客户端明确的知道,可以去连接。而客户端的端口的端口则是系统指定的——我们并没有在客户端调用bind(),所以socket机制会自动帮我们绑定一个端口。通常客户端自己也不需要知道这个端口号是多少,只有接收到这次UDP数据报的服务器端知道,并且按照这个端口号将服务器的信息传送过来——没有收到这个端口发出的数据报的UDP端口很难知道这个系统指定的端口号是多少。但是,因为这个UDP端口实际上是可以接受来自其他任何UDP端口的数据的,所以,如果你需要验证发送某次数据的地址是不是你所期望的,比如是不是来自服务器,可能就会用到lastfromSockAddr。
UDPClientSock::UDPClientSock( int  pre_buffer_size):
preBufferSize(pre_buffer_size), preReceivedLength(
0 )
{
    preBuffer 
=   new   char [preBufferSize];
    memset(
& lastfromSockAddr,  0 sizeof (lastfromSockAddr));
    memset(
& destinationSockAddr,  0 sizeof (destinationSockAddr));

    sockFD 
=  socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
if  (sockFD  <   0 ) {
        sockClass::error_info(
" sock() failed. " );
    }
}

UDPClientSock::
~ UDPClientSock()
{
    delete [] preBuffer;
    close(sockFD);
}
其它4个类方法,跟server端的简直一模一样。
void  UDPClientSock::UDPSetDest( const   char *  dest_IP,
                               
const  unsigned  short &  dest_port)
{
    destinationSockAddr.sin_family 
=  AF_INET;
    destinationSockAddr.sin_addr.s_addr 
=  inet_addr(dest_IP);
    destinationSockAddr.sin_port 
=  htons(dest_port);
}

void  UDPClientSock::UDPSetDest( const  sockaddr_in &  dest_sock_addr)
{
    destinationSockAddr.sin_family 
=  dest_sock_addr.sin_family;
    destinationSockAddr.sin_addr.s_addr 
=  dest_sock_addr.sin_addr.s_addr;
    destinationSockAddr.sin_port 
=  dest_sock_addr.sin_port;
}

int  UDPClientSock::UDPReceive()  const
{
    socklen_t
 from_add_len  =   sizeof (lastfromSockAddr); //use int in win32
    preReceivedLength 
=  recvfrom(    sockFD,
                                    preBuffer,
                                    preBufferSize,
                                    
0 ,
                                    (sockaddr
* ) & lastfromSockAddr,
                                    
& from_add_len);
    
if  ( preReceivedLength  <   0 ) {
        sockClass::error_info(
" recv() failed. " );
    }

    
return  preReceivedLength;
}

int  UDPClientSock::UDPSendtoDest( const   char *  send_data,
                                 
const   int &  data_length)  const
{
    
int  send_message_size  =  sendto(    sockFD,
                                    send_data,
                                    data_length,
                                    
0 ,
                                    (sockaddr
* ) & destinationSockAddr,
                                    
sizeof (destinationSockAddr));
    
if  (send_message_size  <   0 ) {
        sockClass::error_info(
" send() failed. " );
    }
    
if  (send_message_size  !=  data_length) {
        sockClass::error_info(
            
" send() sent a different number of bytes than expected. " );
    }
    
return  send_message_size;
}

你可能感兴趣的:(socket 编程入门教程(五)UDP原理:2、设计UDP client类)