• UDP编程详解



    UDP与TCP的不同之处是:他的通信不需要建立连接的过程。中文名称用户数据报协议。时OSI参考模型中一种无连接的传输层协议。提供面向事务的简单不可靠信息传送服务,UDP在IP报文中的协议号是17.与TCP(传输控制协议)一样,UDP协议直接位于IP(网际协议)协议的顶层,根据TCP/IP参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将数据压缩成数据包的形式,一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。

    报文格式

    源端口(2字节)+目的端口(2字节)+长度(2字节)+校验和(2字节)+数据
    DJmkGT.png

    通信过程

    UDP协议较TCP简单很多,减少了TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议。UDP客户端在发送数据时并不判断主机是否可达,服务器是否开启等问题,他不能确定数据是否成功到达服务器,他只是将数据简单的封了一个包,之后就丢出去了。

    UDP客户端流程

    1. 创建socket:socket()
    2. 通信:sendto() & recvfrom()
    3. 关闭socket:closesocket()

    UDP客户端编码

    void udpclient(int argc, char **argv)
    {
        int sock, port, count;
        struct hostent *host;
        struct sockaddr_in server_addr;
        const char *url;
    
        if (argc < 3)
        {
            rt_kprintf("Usage: udpclient URL PORT [COUNT = 10]
    ");
            rt_kprintf("Like: tcpclient 192.168.12.44 5000
    ");
            return ;
        }
    
        url = argv[1];
        port = strtoul(argv[2], 0, 10);
    
        if (argc > 3)
            count = strtoul(argv[3], 0, 10);
        else
            count = 10;
    
        /* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */
        host = (struct hostent *) gethostbyname(url);
    
        /* 创建一个socket,类型是SOCK_DGRAM,UDP类型 */
        if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
        {
            rt_kprintf("Socket error
    ");
            return;
        }
    
        /* 初始化预连接的服务端地址 */
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        server_addr.sin_addr = *((struct in_addr *)host->h_addr);
        rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
    
        /* 总计发送count次数据 */
        while (count)
        {
            /* 发送数据到服务远端 */
            sendto(sock, send_data, strlen(send_data), 0,
                   (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    
            /* 线程休眠一段时间 */
            rt_thread_delay(50);
    
            /* 计数值减一 */
            count --;
        }
    
        /* 关闭这个socket */
        closesocket(sock);
    }
    

    UDP服务器流程

    1. 创建socket:socket()
    2. 将创建的socket绑定到一个IP地址和端口号上:bind()
    3. 等待接收数据报,处理完成后将结果返回到客户端:recvfrom() & sendto(). recvfrom()会阻塞住等待接收消息
    4. 关闭socket:close()

    UDP服务器编码

    static void udpserv(int argc, char **argv)
    {
        int sock;
        int bytes_read;
        char *recv_data;
        socklen_t addr_len;
        struct sockaddr_in server_addr, client_addr;
    
        /* 分配接收用的数据缓冲 */
        recv_data = rt_malloc(BUFSZ);
        if (recv_data == RT_NULL)
        {
            /* 分配内存失败,返回 */
            rt_kprintf("No memory
    ");
            return;
        }
    
        /* 创建一个socket,类型是SOCK_DGRAM,UDP类型 */
        if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
        {
            rt_kprintf("Socket error
    ");
    
            /* 释放接收用的数据缓冲 */
            rt_free(recv_data);
            return;
        }
    
        /* 初始化服务端地址 */
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(5000);
        server_addr.sin_addr.s_addr = INADDR_ANY;
        rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
    
        /* 绑定socket到服务端地址 */
        if (bind(sock, (struct sockaddr *)&server_addr,
                 sizeof(struct sockaddr)) == -1)
        {
            /* 绑定地址失败 */
            rt_kprintf("Bind error
    ");
    
            /* 释放接收用的数据缓冲 */
            rt_free(recv_data);
            return;
        }
    
        addr_len = sizeof(struct sockaddr);
        rt_kprintf("UDPServer Waiting for client on port 5000...
    ");
    
        while (1)
        {
            /* 从sock中收取最大BUFSZ - 1字节数据 */
            bytes_read = recvfrom(sock, recv_data, BUFSZ - 1, 0,
                                  (struct sockaddr *)&client_addr, &addr_len);
            /* UDP不同于TCP,它基本不会出现收取的数据失败的情况,除非设置了超时等待 */
    
            recv_data[bytes_read] = ''; /* 把末端清零 */
    
            /* 输出接收的数据 */
            rt_kprintf("
    (%s , %d) said : ", inet_ntoa(client_addr.sin_addr),
                       ntohs(client_addr.sin_port));
            rt_kprintf("%s", recv_data);
    
            /* 如果接收数据是exit,退出 */
            if (strcmp(recv_data, "exit") == 0)
            {
                closesocket(sock);
    
                /* 释放接收用的数据缓冲 */
                rt_free(recv_data);
                break;
            }
        }
    
        return;
    }
    

    参考文献

    1. RT-Thread视频中心内核入门
    2. RT-Thread文档中心

    本文作者: CrazyCatJack

    本文链接: https://www.cnblogs.com/CrazyCatJack/p/14408893.html

    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

    关注博主:如果您觉得该文章对您有帮助,可以点击文章右下角推荐一下,您的支持将成为我最大的动力!


  • 相关阅读:
    将博客搬至CSDN
    defender 月考总结
    生日祝福@陈俊翰
    个性签名
    你这是virus吧?
    (CPSCA's)CPOJC+VIJOS
    Sentence by defender
    工作制一览
    最长上升子序列
    mysql约束
  • 原文地址:https://www.cnblogs.com/CrazyCatJack/p/14408893.html
Copyright © 2020-2023  润新知