• 基于UDP丢包统计程序设计


    资源下载地址:https://download.csdn.net/download/sheziqiong/85897389
    资源下载地址:https://download.csdn.net/download/sheziqiong/85897389

    UDP 通信程序设计

    【实验名称】

    基于 UDP 丢包统计程序设计

    【实验目的】

    选择一个操作系统(Linux 或者 Windows),编制 UDP/IP 通信程序,完成一定的通信功能。

    【实验要求】

    在发送 UDP 数据包时做一个循环,连续发送 100 个数据包;在接收端统计丢失的数据包。

    实验时,请运行 Wireshark 软件,对通信时的数据包进行跟踪分析。

    【实验原理】

    在这里插入图片描述

    以上为一般 UDP 网络编程的流程图,在本次实验中仅涉及客户端发送数据和服务器接收数据,因此本次实验的实

    验流程图如下:

    在这里插入图片描述

    【实验内容】根据流程图开始编程,下面进行代码分析:

    客户端代码 UDP_Cli.cpp

    1.1 /创建 Socket/

    SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockCli < 0)
    {   cout << "Failed." << endl;
        return -1;
    }
    cout << "Create socket successfully." << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    调用库函数 socket 创捷套接字,若返回值 <0 则说明创建套接字失败,退出程序。

    socket 声明如下:

    WINSOCK_API_LINKAGE SOCKET WSAAPI socket(int af,int type,int protocol);
    
    • 1

    第一个参数指明了协议簇,目前支持 5 种协议簇,最常用的有 AF_INET(IPv4 协议)和 AF_INET6(IPv6 协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和 SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为 0。 在本次实验中使用 AF_INET 协议簇,SOCK_DGRAM 数据报接口,第三个参数为 UDP 的 protocol。

    /*向指定地址和端口收发数据*/
    char recvBuf[BUFSIZE];      //接受数据的缓冲区     string sendBu= "Hello server! This is a packet. Data:";      //发送数据的缓冲区     char tmp[BUFSIZE];
    SOCKADDR_IN addr_server; //服务器的地址数据结构     addr_server.sin_family = AF_INET;
    addr_server.sin_port = htons(6666);                        //端口号为6666     addr_server.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");   //127.0.0.1为本电脑IP地址     int server_len = sizeof(addr_server);     for (int i = 1; i <= 100; i++){
    itoa((rand() % 100000), tmp, 10);
    string sendBuf = sendBu + tmp;
    err = sendto(sockCli, sendBuf.data(), sendBuf.size(), 0, (SOCKADDR *)&addr_server,  sizeof(SOCKADDR)); //发送         if (err < 0){             cout << "Sendto failed."<< endl;             return -1;
    }         else {
        cout << "Packet " << i << " has been sent." << endl;
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用 sendto 函数向客户端发送 100 个数据包,若发送成功则输出报告,失败则退出程序。每个数据包包括一句固定的问候语和需要发送的数据,在这里为 0~99999 的一个随机数,以字节为单位发送。

    WINSOCK_API_LINKAGE int WSAAPI sendto(SOCKET s,const char *buf,int len,int flags,const str uct sockaddr *to,int tolen);
    
    • 1

    sendto 函数:UDP 使用 sendto()函数发送数据,他类似于标准的 write(),但是在 sendto()函数中要指明目的地址。前三个参数等同于函数 read()的前三个参数,flags 参数是传输控制标志。参数 to 指明数据将发往的协议地址,他的大小由 addrlen 参数来指定。

    它返回发送数据的长度大于或等于 0 说明发送成功,失败则返回-1。

    /*关闭套接字*/
    closesocket(sockCli);
    
    • 1
    • 2

    发送完毕后关闭套接字。

    服务端代码 UDP_Ser.cpp

    1.2 /创建 Socket/

    int sockSev = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockSev < 0)
    {   cout << "Failed to create socket." << endl;
        return -1;
    }
    cout << "Create socket successfully." << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    过程和客户端大致相同。

    1.3 /绑定 socket 和端口号/

    SOCKADDR_IN addr_server;                                    //服务器的地址数据结构     addr_server.sin_family = AF_INET;     addr_server.sin_port = htons(6666);                        //端口号为6666     addr_server.sin_addr.S_un.S_addr=inet_addr("172.19.1.207");   //172.19.1.207为本电脑IP 地址
    if (bind(sockSev, (SOCKADDR *)&addr_server, sizeof(addr_server)) == SOCKET_ERROR)
    {   cout<<"Failed to bind."<< endl;
        closesocket(sockSev);
        WSACleanup();
        return 0;
    }     else
        cout << "Bind successfully." << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    创建服务器的地址数据结构并对其进行协议簇、端口号和 IP 地址的配置,再使用 bind 函数将创建好的 socket 绑定到该地址上。

    WINSOCK_API_LINKAGE int WSAAPI bind(SOCKET s,const struct sockaddr *name,int namelen);
    
    • 1
    • bind 函数描述:把一个地址族中的特定地址赋给 socket 参数解释: s:指的是通过 socket()创建的描述字,唯一标识一个 socket。
    • name:一个指针,指向要绑定的协议地址。
    • namelen:该地址结构体的长度
    /*向指定地址和端口收发数据*/
    char recvBuf[BUFSIZE];      //接受数据的缓冲区
    SOCKADDR_IN addr_client;            //用于接收用户的ip地址和端口号等信息     int client_len = sizeof(addr_client);     int count = 0;     while(true){
    int last = recvfrom(sockSev, recvBuf, BUFSIZE, 0, (SOCKADDR *)&addr_client, &clien t_len);
    if (last <= 0)
    {   cout << "Recvfrom Error!" << endl;
        continue;
    }         else {
        cout << "Recvfrom:" << setw(7) << recvBuf;
        cout << "   Count:" << ++count << endl;
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用 recvfrom 函数监听发送来的数据,若接收成功则输出结果。同时使用 count 来累计成功接收到的数据包的个数。

    WINSOCK_API_LINKAGE int WSAAPI recvfrom(SOCKET s,char *buf,int len,int flags,struct socka ddr *from,int *fromlen);
    
    • 1

    参数解释: s:标识一个已连接套接口的描述字。

    buf:接收数据缓冲区。 len:缓冲区长度。 flags:调用操作方式。 from:(可选)指针,指向装有源地址的缓冲区。 fromlen:(可选)指针,指向 from 缓冲区长度值。

    由于 Windows 系统下使用 socket 需进行注册,注册过程如下:

    • 【实验结果】
    • 局域网环境下:
    • 同时运行客户端程序和服务器程序,客户端界面如下:

    在这里插入图片描述

    可以看到 socket 创建成功,并陆续向目的地址发送数据包。

    成功发送 100 个数据包,如下所示:

    在这里插入图片描述

    此时在服务器端看到服务器的 socket 创建成功,并成功 bind 上本机地址,开始陆续接收到来自客户端发送的数据,同时统计接收到的数据包的数量。

    在这里插入图片描述

    最终成功接收到 100 个数据包,无丢包的情况发生:

    在这里插入图片描述

    同时使用 wireshark 抓取数据包,可以看到 127.0.0.1(本机地址)发送了 100 个 UDP 类型的数据包:

    在这里插入图片描述

    在这里插入图片描述

    可以看到发送方的 ip 地址(主机地址)目的地的 ip 地址(也是主机地址)均为 127.0.0.1,目的地端口为 6666,正是本次实验所使用的端口。

    互联网环境下运行客户端程序,向目的地址发送 100 个数据包,可以看到每发送一个数据包就显示了发送成功,同时监听到发回来的信号:

    在这里插入图片描述

    最终成功发送 100 个数据包,成功接收到发回的 100 个数据包。

    在这里插入图片描述
    资源下载地址:https://download.csdn.net/download/sheziqiong/85897389
    资源下载地址:https://download.csdn.net/download/sheziqiong/85897389

  • 相关阅读:
    网络请求与数据解析
    【黑马程序员】Python文件、异常、模块、包
    Spring Boot自动装配原理超详细解析
    编程小白如何成为大神?大学新生的最佳入门攻略
    逐步手撕轮播图3(分步教程)
    Linux 下的 动静态库
    golang mapstructure库实践
    云原生之K8S------K8S持久化存储PV、PVC
    VMware:一个多云+AI的未来
    深入理解Android音视频同步机制(一)ExoPlayer的avsync逻辑
  • 原文地址:https://blog.csdn.net/sheziqiong/article/details/125600883