• 网络编程:UDP网路编程


    参考:盛延敏:网络编程实战

    一、UDP和TCP的不同

    • UDP 是一种“数据报”协议,而 TCP 是一种面向连接的“数据流”协议。
    • TCP 是一个面向连接的协议,TCP 在 IP 报文的基础上,增加了诸如重传、确认、有序传输、拥塞控制等能力,通信的双方是在一个确定的上下文中工作的。而 UDP 则不同,UDP 没有这样一个确定的上下文,它是一个不可靠的通信协议,没有重传和确认,没有有序控制,也没有拥塞控制。可以简单地理解为,在 IP 报文的基础上,UDP 增加的能力有限。
      UDP 不保证报文的有效传递,不保证报文的有序,也就是说使用 UDP 的时候,我们需要做好丢包、重传、报文组装等工作。

    TCP 的发送和接收每次都是在一个上下文中,类似这样的过程:

    A 连接上: 接收→发送→接收→发送→…
    B 连接上: 接收→发送→接收→发送→…

    而 UDP 的每次接收和发送都是一个独立的上下文,类似这样:

    接收 A→发送 A→接收 B→发送 B →接收 C→发送 C→ …

    二、UDP网络编程

    服务器端创建 UDP 套接字之后,绑定到本地端口,调用 recvfrom 函数等待客户端的报文发送;客户端创建套接字之后,调用 sendto 函数往目标地址和端口发送 UDP 报文,然后客户端和服务器端进入互相应答过程。

    recvfrom 和 sendto 是 UDP 用来接收和发送报文的两个主要函数:

    
    #include <sys/socket.h>
    
    ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, 
              struct sockaddr *from, socklen_t *addrlen); 
    
    • sockfd 是本地创建的套接字描述符
    • buff 指向本地的缓存
    • nbytes 表示最大接收数据字节。
    • flags 是和 I/O 相关的参数,这里还用不到,设置为 0。
    • 后面两个参数 from 和 addrlen,实际上是返回对端发送方的地址和端口等信息,和 TCP 非常不一样,TCP 是通过 accept 函数拿到的描述字信息来决定对端的信息。另外 UDP 报文每次接收都会获取对端的信息,也就是说报文和报文之间是没有上下文的。
    • 返回值告诉实际接受的字节数。
    ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
                    const struct sockaddr *to, socklen_t addrlen); 
    
    • sockfd 是本地创建的套接字描述符
    • buff 指向发送的缓存
    • nbytes 表示发送字节数
    • 第四个参数 flags 依旧设置为 0。
    • 后面两个参数 to 和 addrlen,表示发送的对端地址和端口等信息。
    • 函数的返回值告诉我们实际发送的字节数。

    三、编程实践
    udp服务端

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    #define    SERV_PORT      43211
    #define    MAXLINE        4096
    
    static int count;
    
    static void recvfrom_int(int signo)
    {
        printf("\nrecevied %d datagrams\n",count);
        exit(0);
    }
    
    int main(int argc,char*argv[])
    {
        int socket_fd;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET,SOCK_DGRAM,0);
    
        bzero(&server_addr,sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        server_addr.sin_port = htons(SERV_PORT);
        bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    
        socklen_t client_len;
        char message[MAXLINE];
        count = 0;
    
        signal(SIGINT,recvfrom_int);
    
        struct sockaddr_in client_addr;
        client_len = sizeof(client_addr);
        for(;;)
        {
            int n = recvfrom(socket_fd,message,MAXLINE,0,(struct sockaddr *) &client_addr, &client_len);
            message[n] = 0;
            printf("received %d bytes: %s\n",n,message);
    
            char send_line[MAXLINE];
            sprintf(send_line,"Hi,%s\n",message);
    
            sendto(socket_fd,send_line,strlen(send_line),0, (struct sockaddr *) &client_addr, client_len);
    
            count++;
        }
    }
    

    udp客户端

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    #define    SERV_PORT      43211
    #define    MAXLINE        4096
    
    int main(int argc,char* argv[])
    {
        if(argc != 2)
        {
            perror("Usage:udpclient <IPaddress>");
            return -1;
        }
    
        int socket_fd;
        socket_fd = socket(AF_INET,SOCK_DGRAM,0);
    
        struct sockaddr_in server_addr;
        bzero(&server_addr,sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERV_PORT);
        inet_pton(AF_INET,argv[1],&server_addr.sin_addr);
    
        socklen_t server_len = sizeof(server_addr);
    
        struct sockaddr *reply_addr;
        reply_addr = malloc(server_len);
    
        char send_line[MAXLINE],recv_line[MAXLINE+1];
        socklen_t len;
        int n;
    
        while(fgets(send_line,MAXLINE,stdin) != NULL)
        {
            int i = strlen(send_line);
            if(send_line[i-1] == '\n')
            {
                send_line[i-1] = 0;
            }
    
            printf("now sending %s \n",send_line);
            size_t rt = sendto(socket_fd,send_line,strlen(send_line),0,(struct sockaddr *)&server_addr,server_len);
            if(rt < 0)
            {
                perror("send faild");
            }
            printf("send bytes %zu \n",rt);
    
            len = 0;
            n = recvfrom(socket_fd,recv_line,MAXLINE,0,reply_addr,&len);
            if(n < 0)
            {
                perror("recvfrom failed");
            }
            recv_line[n] = 0;
            fputs(recv_line,stdout);
            fputs("\n",stdout);
        }
    
        exit(0);
        
    }
    

    效果:
    1)若只运行客户端,程序一直会阻塞在recvfrom上

    2)先开服务端,再开客户端


    3) 开服务端,开两个客户端
    服务端:

    客户端1:

    客户端2:

    两个客户端发送的报文,依次都被服务端收到,并且客户端也可以收到服务端处理之后的报文。如果我们此时把服务器端进程杀死,就可以看到信号函数在进程退出之前,打印出服务器端接收到的报文个数。
    再重启服务器端进程,并使用客户端 1 和客户端 2 继续发送新的报文,这也体现了UDP 报文的“无连接”的特点,可以在 UDP 服务器重启之后,继续进行报文的发送,这就是 UDP 报文“无上下文”的最好说明。(但在服务端进程被杀死后,此时就是1)状态了,若此时在任意客户端发送数据,程序便会一直阻塞在recvfrom上)

    注:
    详解inet_pton()和inet_ntop()函数

  • 相关阅读:
    DRF__序列化(1)serializers.Serializer 基本的序列化、反序列化概念
    小整数池以及字符串驻留机制
    Paginator实现分页
    xml代码 解决eclipse乱码问题
    css,解决文字与图片对齐的问题
    鼠标悬停,背景颜色变化问题
    解决html代码文本复制问题,js技术
    解决 点击超链接后,周围出现的虚线框的问题
    将一个html文件引入另一个html文件的div中
    WebStorm出现中文乱码解决代码
  • 原文地址:https://www.cnblogs.com/whiteBear/p/15948318.html
Copyright © 2020-2023  润新知