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

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

人们通常用电话连线来说明TCP协议,而UDP协议,则常常用邮递来做比喻。与TCP有连接的信息传输方式不同,UDP协议被认为是对底层IP协议简单的扩展:协议并不保证每个数据包都会到达目的地,也不保证到达的顺序,而仅仅就是“尽力”的发送每一个数据包。我在这篇教程中有时候使用“数据包”有时候使用“数据报”,广义的说,这两个词意思类似,有代表一个有大小边缘的数据块。但是,用“数据包”的时候,我想强调的是这个数据块中所传送的数据部分;而“数据报”则更强调在数据块中对这段数据的信息和说明部分,比如IP首部,TCP和UDP首部,TCP和UDP报文段这些信息。TCP协议通过同步验证实现了TCP层面上的“数据流”传送,而下层的IP协议,依然是数据报形式的传送,这个我们在前面已经描述过,比如连接握手和断开握手,实际上都是发送的TCP数据报(TCP格式的IP数据报)。UDP格式的IP数据报为IP数据报指定了UDP端口,从而使这样的IP数据报的目的地能够精确到应用程序——没有端口指定的IP数据报目的地只能精确到具有IP地址的主机。另外,与TCP的无边缘保证相反,UDP数据包是有大小的,而其最大限制也即是IP数据包大小的最大限制:65,507字节(这里需要说明两点:1、IP数据包的理论最大值为2^16 – 1,即65,535字节,UDP数据报因为要包含UDP首部的信息,所以比这个值小一点;2、因为MTU的存在,实际传输中的IP数据包会被分封到1500字节以下。)
因为UDP是无连接的,就像一个邮筒,可以接受来自任何人的邮件;也可以发送给任何人的邮件。而每一次接受,都会得到来向的地址;每一次发送,也必须指明去向的地址。我们设计一个类,分别以lastfromSockAddr和destinationSockAddr表示最后一次来向的地址以及(下一次发送的)目的地地址。需要指出的是,因为防火墙的普遍存在,最后一次来向地址变得极其重要!这一点我们将在后面的讨论中看到。

class UDPServerSock: public BaseSock {

private:
    sockaddr_in serverSockAddr;
protected:
    mutable sockaddr_in lastfromSockAddr;
    sockaddr_in destinationSockAddr;
    
char* preBuffer;
    
int preBufferSize;
    mutable 
int preReceivedLength;
public:
    
explicit UDPServerSock(
            unsigned 
short server_port,
            
int pre_buffer_size = 32);
    
virtual ~UDPServerSock();
    
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;
};

我们把最后一次来向地址以及预接收缓存中的收到的数据长度设置成mutable是因为我们希望接收UDPReceive()这个方法看起来是不改变对象的。每一次接收,实际上都会刷新lastfromSockAddr,而作为服务器,往往也是通过lastfromSockAddr去决定destinationSockAddr的。

UDPServerSock::UDPServerSock(unsigned short server_port,
                             
int pre_buffer_size):
preBufferSize(pre_buffer_size), preReceivedLength(
0)
{

    preBuffer 
= new char[preBufferSize];
    memset(
&serverSockAddr, 0, sizeof(serverSockAddr));
    memset(
&lastfromSockAddr, 0, sizeof(lastfromSockAddr));
    memset(
&destinationSockAddr, 0, sizeof(destinationSockAddr));

    serverSockAddr.sin_family = AF_INET;
    serverSockAddr.sin_addr.s_addr 
= htonl(INADDR_ANY);
    serverSockAddr.sin_port 
= htons(server_port);
    

    sockFD = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
if (sockFD < 0) {

        sockClass::error_info(
“sock() failed.“);
    }

    if (bind(    sockFD,
                (sockaddr
*)&serverSockAddr,
                
sizeof(serverSockAddr)) < 0) {

        sockClass::error_info(
“bind() failed.“);
    }
}

UDPServerSock::~UDPServerSock()
{

    delete [] preBuffer;
    close(sockFD);
}

构造函数依然使用socket()建立sockFD,然后通过bind()将本机的SockAddr(主要是指定了端口)绑定到这个sockFD上。
我们重载了UDPSetDest()这个方法,可以有两种方式去指定目标地址destinationSockAddr——既可以指定IP地址和端口,也可以直接赋值以sockaddr_in结构。

void UDPServerSock::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 UDPServerSock::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;
}

最后是接收和发送。我们知道TCP里面recv()返回0表示连接正常断开,而UDP里面,则仅仅就是表示收到0字节的数据包。可见,数据大小为0,并不代表数据包为空,因为这个数据包实际也是一个数据报,包含着TCP数据报的各种必要信息。

int UDPServerSock::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 UDPServerSock::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;
}

转载于:https://my.oschina.net/GeorgeSu/blog/264205

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/109831.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java double 保留两位小数

    java double 保留两位小数java保留两位小数问题:方式一:四舍五入  double  f  =  111231.5585;  BigDecimal  b  =  new  BigDecimal(f);  double  f1  =  b.setScale(2,  BigDecimal.ROUND_HALF_UP).doubleValue();  保留两位小数  —–

    2022年9月24日
  • 数字图像处理标准图像Lena的故事「建议收藏」

    数字图像处理标准图像Lena的故事「建议收藏」熟悉图像处理或者压缩的工程师、研究人员和学生经常在他们的实验或者项目任务里使用“Lenna”或者“Lena”的图像。Lenna图像已经成为被广泛使用的测试图像。今天,Lenna图像的使用被认为是数字图像历史上最重要的事件之一。然而,很少有人看过原始的图像并知道完整的关于Lenna的故事。这里3sBeta将综合收集的材料对此做一个详细的梳理。。。1.Lena图像的来源在数

    2022年6月19日
  • CSS自定义鼠标指针样式「建议收藏」

    CSS自定义鼠标指针样式「建议收藏」还记得Web1.0时代的那些苦逼岁月吗?你想尽一切办法来优化你的网站.还要饱受IE6惨无人道的虐待,举个栗子,IE中那些害死人不偿命的滚动条,我一直记得第三方类库CometCursor.CometCursor非常强悍,主要用来创建和加载自定义鼠标光标样式。现在可能你会觉得当初的那些实现手段特别老土,但有时又确实需要定制一下光标图案,那么一起来看看CSS怎么实现吧,It’sSoEasy,哪里不会点哪里!

    2022年5月20日
  • 最全面的外包公司的解释

    最全面的外包公司的解释1.最新北京软件外包公司排行榜软件外包企业排名:北京华盛恒辉科技有限公司(北京)北京五木恒润科技有限公司(北京)东软集团Neusoft(沈阳)博彦科技BeyondSoft(北京)海辉软件HiSoft(大连)文思VanceInfo(北京)浙大网新Insigma(杭州)奥博杰天Objectiva(北京)浪潮Inspur(济南)软通动力iSoftStone(北京)2.国内一些比…

    2022年5月29日
  • 关于个人能力提升

    关于个人能力提升

    2020年11月12日
  • Bootstrap 流式布局

    Bootstrap 流式布局流式布局同理,将Bootstrap的流式栅格放到class="container-fluid"的流式容器中,即可创建流式布局。流式布局将填满整个视口宽度。如:&lt;divclass="container-fluid"&gt; &lt;divclass="row-fluid"&gt;   &lt;divclass="span2"&gt;     &lt;!–

    2022年10月24日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号