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, 0sizeof(serverSockAddr));
    memset(
&lastfromSockAddr, 0sizeof(lastfromSockAddr));
    memset(
&destinationSockAddr, 0sizeof(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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 3DCNN参数解析:2013-PAMI-3DCNN for Human Action Recognition「建议收藏」

    3DCNN参数解析:2013-PAMI-3DCNN for Human Action Recognition「建议收藏」3DCNN参数解析:2013-PAMI-3DCNNforHumanActionRecognition参数分析Input:7@60×\times×40,7帧,图片大小60×\times×40hardwired:H1产生5通道信息,分别是gray,gradient-x,gradient-y,optflow-x,optflow-y。前三个对于每一张图片都计算得…

    2022年6月11日
    43
  • 网站防止攻击

    网站防止攻击1、什么是XSSXSS又叫CSS(CrossSiteScript),跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常呼略其危害性。跨站脚本攻击最大的魅力是通过HTML注入劫持用户的浏览器,任意构造用户当前浏览的HTM

    2022年7月20日
    19
  • virtualbox vboxmanage命令[通俗易懂]

    virtualbox vboxmanage命令[通俗易懂]查看当前虚拟机VBxoManagelistvms查看当前正在运行的虚拟机VBoxManagelistrunningvms启动虚拟机VBoxManagestartvm虚拟机名无前端图形界面方式启动虚拟机VBoxManagestartvm虚拟机名–typeheadless使用VRDP方式通过命令行启动虚拟机:(33…

    2022年5月2日
    99
  • pycharm的查找替换_pycharm调用其他py文件

    pycharm的查找替换_pycharm调用其他py文件1、打开要修改的文件2、ctrlr调出替换功能,如图所示:3、上面红框是需要更改的部分,下面红框是想要更改为部分,编辑后,点击“replaceall”即可示例原始页面ctrlr调出替换功能,如图所示在上一栏输入被替换字段,下一栏输入想换成的字段点击replaceall结果…

    2022年8月28日
    5
  • 十进制小数转为二进制小数方法是什么_二进制转十进制计算题

    十进制小数转为二进制小数方法是什么_二进制转十进制计算题首先需要明确一点就是并不是所有的十进制小数都能用二进制数精确表示的。这是由于进制的局限性导致的。例如三进制的0.1,用十进制表示是无限循环小数0.333333…..十进制的小数转换为二进制的方法为十进制小数乘2,取出整数作为二进制小数第一位,余下的小数再乘以2,取出整数作为小数第二位,如此反复,直至余下的小数为0。例:十进制0.3750.375*2=0.75————0(0.75的整数部分为0)0.75*2=1.5————1(1.5的整数…

    2022年9月24日
    2
  • 向量内积的推导_向量数量积的坐标公式推导

    向量内积的推导_向量数量积的坐标公式推导基本式几何對稱性:。線性函數:設。固定時,而且同樣道理,固定時,转载于:https://www.cnblogs.com/kyostone/p/5743252.html…

    2025年12月4日
    4

发表回复

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

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