UDP编程详解[通俗易懂]

UDP编程详解[通俗易懂]UDP与TCP的不同之处是:他的通信不需要建立连接的过程。中文名称用户数据报协议。时OSI参考模型中一种无连接的传输层协议。提供面向事务的简单不可靠信息传送服务,UDP在IP报文中的协议号是17.与T

大家好,又见面了,我是你们的朋友全栈君。


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]\n");
        rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
        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\n");
        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\n");
        return;
    }

    /* 创建一个socket,类型是SOCK_DGRAM,UDP类型 */
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        rt_kprintf("Socket error\n");

        /* 释放接收用的数据缓冲 */
        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\n");

        /* 释放接收用的数据缓冲 */
        rt_free(recv_data);
        return;
    }

    addr_len = sizeof(struct sockaddr);
    rt_kprintf("UDPServer Waiting for client on port 5000...\n");

    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] = '\0'; /* 把末端清零 */

        /* 输出接收的数据 */
        rt_kprintf("\n(%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 许可协议。转载请注明出处!

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


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

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/155103.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 微信公众号中ip白名单用谁的ip

    微信公众号中ip白名单用谁的ip

    2021年10月12日
  • 如何利用eclipse创建一个java web项目?「建议收藏」

    如何利用eclipse创建一个java web项目?「建议收藏」1.在左侧空白处,右键选择New->DynamicWebProject2.若New的选择列表中没有DynamicWebProject,则选择Others,然后输入dynamic(一般输入dy就可以),选择DynamicWebProject3.输入项目名称,选择运行时环境(中间件环境),选择web相应的版本。4.点击Finish

  • PHP pathinfo() 函数

    PHP pathinfo() 函数

  • 如何利用python读excel数据_python在excel应用实例

    如何利用python读excel数据_python在excel应用实例文章目录python读取excel表数据的方法:完整的程序代码python读取excel表数据的方法:首先安装Excel读取数据的库xlrd;然后获取Excel文件的位置并且读取进来;接着读取指定的行和列的内容,并将内容存储在列表中;最后运行程序即可。python读取excel表数据的方法:安装Excel读取数据的库—–xlrd直接pipinstallxlrd安装xlrd库#引入Excel库的xlrdimportxlrd获取Excel文件的位置并且读取进来#导入需要读取Exc

  • mysql左连接查询

    mysql左连接查询mysql左连接查询左连接查询:以左表为主表,右表为从表,查询符合条件的数据1.当右表中数据匹配不到时展示为空例:左表两条数据,按条件匹配到右表一条数据且匹配左表第一条,结果展示两条数据,且第二条数据右表中的字段全部为null2.当匹配到右表的数据为多条时,左表数据会重复展示,不会自动合并例:左表数据一条,按条件匹配到右表数据三条,结果展示三条数据,左表数据均相同,右表数据不同…

  • redis过期删除机制(redis过期策略和删除策略)

    在Redis中,内存的大小是有限的,所以为了防止内存饱和,需要实现某种键淘汰策略。主要有两种方法,一种是当Redis内存不足时所采用的内存释放策略。另一种是对过期键进行删除的策略,也可以在某种程度上释放内存。1、内存释放的策略Redis中有专门释放内存的函数:freeMmoryIfNeeded。每当执行一个命令的时候,就会调用该函数来检测内存是否够用。如果已用内存大于最大内存限制,它就会进行内存释…

发表回复

您的电子邮箱地址不会被公开。

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