• Linux UDP编程流程


    UDP编程流程

    UDP 提供的是无连接、不可靠的、数据报服务。服务器端和客户端没有什么本质上的区别。编程流程如下:

    在这里插入图片描述

    socket()用来创建套接字,使用 udp 协议时,选择数据报服务 SOCK_DGRAM。sendto()用来发送数据,由于 UDP 是无连接的,每次发送数据都需要指定对端的地址(IP 和端口)。recvfrom()接收数据,每次都需要传给该方法一个地址结构来存放发送端的地址。recvfrom()可以接收所有客户端发送给当前应用程序的数据,并不是只能接收某一个客户端的数据。

    服务器端代码ser.c如下:

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int  main()
    {   //1.创建套接字
        int sockfd=socket(AF_INET,SOCK_DGRAM,0);
        if(sockfd==-1)
        {
            printf("创建失败");
            exit(1);
        }
    
        struct sockaddr_in saddr,caddr;
        memset(&saddr,0,sizeof(saddr));
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(6000);
        saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
        
        //2.指定套接字的地址,绑定ip和端口
        int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
        if(res==-1)
        {
            printf("绑定失败");
            exit(1);
        }
        int len=sizeof(caddr);
        while(1)
        {
            char buff[128]={0};
        //3.接收客户端的消息,谁发来数据就接收谁的数据,并没有和任何客户端建立连接
            recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
            //客户端的信息(ip和端口)存放到caddr,caddr指定数据的来源,中记录着是谁发的数据
            printf("buff(%d)=%s\n",ntohs(caddr.sin_port),buff);
        //4.向客户端回复数据
            sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    客户端代码cli.c如下:

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
        //1.创建套接字
        int sockfd=socket(AF_INET,SOCK_DGRAM,0);
        if(sockfd==-1)
        {
            printf("创建失败");
            exit(1);
        }
    
        struct sockaddr_in saddr;//服务器的地址,ip和端口
        memset(&saddr,0,sizeof(saddr));
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(6000);
        saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    
        int len =sizeof(saddr);
        while(1)
        {
            char buff[128]={0};
            printf("输入数据:");
            fgets(buff,128,stdin);
    
            if(strncmp(buff,"end",3)==0)
            {
                break;
            }
    
            //2.向服务器端发送数据
            sendto(sockfd,buff,sizeof(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
    
            memset(buff,0,sizeof(buff));
    
            //3.接收服务器端回复的数据
            recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
    
            printf("buff=%s\n",buff);
        }
    
        //4.关闭套接字
        close(sockfd);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    运行结果:

    在这里插入图片描述

    从结果可以看出,当服务器端启动之后,可以同时启动多个客户端与客户端通信,但是两个客户端的端口号是不同的。

    UDP协议无连接的特点

    将服务器端关闭再重启后,客户端依旧可以与服务器端通信,如下图所示:

    在这里插入图片描述
    这是因为服务器端和客户端是没有建立连接的,当服务器端启动之后,收的到底是哪个客户端的消息我们并不知道,哪个客户端发消息,服务器端就收哪个客户端发来的消息。当服务器端关闭之后是不会像TCP编程那样有底层的协议去进行四次挥手通知客户端它要关闭的,客户端不会收到任何信息并且不知道服务器端是否关闭,所以当我们服务器端关闭之后再重新启动不会影响客户端向服务器端发送数据。同理,客户端关闭也不会给服务器端发送任何信息,客户端的关闭也不会影响服务器端。

    如果服务器端关闭后,客户端向服务器端发送数据就会发生阻塞,阻塞在客户端的recvfrom这个地方,因为服务器没启动,端口没有人使用,那么数据就会被丢掉,所以没有服务器端给客户端回复数据,所以就会阻塞在recvfrom这个地方。如下图:

    在这里插入图片描述

    此时客户端代码cli.c执行完了sendto,接下来执行recvfrom的时候就被阻塞住了,因为没有服务器端给客户端回复消息,此时发送的aaaaaa发给本主机的6000端口,但是本主机的6000端口没有被占用,也就是6000端口没有被使用,所以所发送的数据aaaaaa就直接被丢掉了,所以也不会有服务器端给客户端回复消息,客户端就会在recvfrom这个地方阻塞住。

    UDP协议数据报的特点

    将服务器端代码ser.c中的recvfrom那一行的代码修改为如下情况,让服务器端一次只收一个字符:

    recvfrom(sockfd,buff,1,0,(struct sockaddr*)&caddr,&len);
    
    • 1

    此时的运行结果:

    在这里插入图片描述

    出现这样的结果是因为,第一次客户端所发送的"hello",服务器端只收到了一个字符"h",剩下的"ello"被丢掉了,后续的结果也是一样,这是因为UDP协议是数据报服务,不像流式服务一样没有读完会继续读,每次recvfrom就拆一个包,拆一个包要保证把所有的数据都读走,不然剩余的数据就会被丢掉。所以在使用UDP协议的时候必须要保证一次把数据接收完。不用担心两个包合到一起导致收不完数据,因为每次调sendto的时候都要指定对方的地址,这个地址可以一样也可以不一样,所以两个sendto不可能合到一起,因为sendto的目的端可以不一样。不像TCP协议,send一下,再send一下,两次send的内容都会放到同一个缓冲区中,UDP协议是sendto直接打一个包过去,不会出现粘包的现象。数据报服务的特点如下图所示:

    在这里插入图片描述

  • 相关阅读:
    联想M7216NWA一体机连接WiFi及手机添加打印机方法
    LeetCode 热题 HOT 100:二叉树专题
    手把手教你做主成分分析
    深入浅出Spring 框架,原来以前白学了!
    国产自主可控C++工业软件可视化图形架构源码
    动态内存管理
    进阶:编写符合Python风格的对象
    运用线程的一些问题,基础篇
    利用C#实现Pdf转图片
    聊聊设计模式——迭代器模式
  • 原文地址:https://blog.csdn.net/NuYoaH502329/article/details/132643296