• Linux c编程之UDP通信


    一、说明

      UDP(User Datagram Protocol),由RFC 768规范定义,中文名为用户数据报协议。UDP 为应用程序提供了一种无需建立连接就可以发送网络数据包的方法。
      UDP是常用的网络传输协议之一,该协议是无连接、不可靠、面向数据报的协议。在Linux C网络程序中广泛使用,如音、视频媒体数据传输、DNS协议、SIP协议等。
      UDP通信分为客户端和服务端,其中服务端在指定的网络端口上读取数据,客户端将数据发给服务端绑定的网络端口,无需建立连接即可通信,反过来,服务端向客户端发送数据也是一样。

    二、常用API介绍

    2.1 socket()

       #include 
       #include 
       int socket(int domain, int type, int protocol);
    
    • 1
    • 2
    • 3

    作用:创建一个通信的终端
    参数说明:
      domain: 协议族,常用AF_INET表示IPv4
      type: 传输方式,常用的有以下两种:
        SOCK_STREAM: TCP
        SOCK_DGRAM: UDP
      protol: 特殊协议,实际应用中都是写为0
    返回值:
      成功时返回一个socket文件描述符,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.2 bind()

    #include 
    #include 
    int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
    
    • 1
    • 2
    • 3
    • 4

    作用:
      绑定网络地址(IP/PORT)到socket
    参数说明:
      sockfd: socket()返回的描述符
      addr: 绑定的地址
      addrlen: 绑定的地址结构长度
    返回值:
      成功时返回0,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.3 sendto()

    #include 
    #include 
    ssize_t sendto(int sockfd, const void *buf, size_t len,
                 int flags, const struct sockaddr *dest_addr,
                  socklen_t addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    作用:
      发送消息
    参数说明:
      sockfd: socket()创建的socket描述符
      buf: 发送的内容(起始地址)
      len:发送内容的长度
      flags: 发送选项,一般为0
      dest_addr:目的地址
      addrlen:目的地址长度
    返回值:
      成功返回发送的字符个数,失败返回-1,errno会被设置

    2.4 recvfrom()

    #include 
    #include 
    ssize_t recvfrom(int sockfd, void *buf, size_t len,
                     int flags, struct sockaddr *src_addr,
                     socklen_t *addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    作用:
      接收消息
    参数说明:
      sockfd: socket()创建的socket描述符
      buf: 接收的内容(起始地址)
      len:接收内容的长度
      flags: 接收选项,一般为0
      dest_addr:源地址
      addrlen:源地址长度
    返回值:
      成功时返回接收的字节数,错误时返回-1,并设置errno值。如果对端关闭时,返回0

    2.5 close()

    #include 
    int close(int fd);
    
    • 1
    • 2

    作用:
      关闭一个(文件/socket)描述符
    参数说明:
      fd: 描述符

    三、UDP实例分析

    3.1 基本UDP通信示例

    server_udp.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_PORT 9999
    
    int server_udp()
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        addr_len = sizeof(server_addr);
        ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
        while (1) {
            memset(buf, 0, sizeof(buf));
            addr_len = sizeof(client_addr);
            ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
                             (struct sockaddr *)&client_addr, &addr_len);
            if (ret > 0) {
                printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
            } else if (0 == ret) {
                printf("ret:%d\n", ret);
            } else {
                printf("ret:%d\n", ret);
            }
        }
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        server_udp();
    
        return 0;
    }
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    client_udp.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_IP "127.0.0.1"
    #define SERVER_PORT 9999
    
    int client_udp(char *data)
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    
        addr_len = sizeof(server_addr);
        ret = sendto(socket_fd, data, strlen(data), 0,
                         (struct sockaddr *)&server_addr, addr_len);
        if (ret > 0) {
            printf("send data: [%s] to %s:%d\n", data, inet_ntoa(server_addr.sin_addr),
                            ntohs(server_addr.sin_port));
        } else {
            printf("ret:%d\n", ret);
        }
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc < 2) {
            client_udp("hello world");
        } else {
            client_udp(argv[1]);
        }
    
        return 0;
    }
    
    • 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
    • 52
    • 53
    • 54

    Makefile:

    all:
    	gcc -o server server_udp.c
    	gcc -o client client_udp.c
    clean:
    	-@rm server client
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编译方式:

    make
    
    • 1

    测试运行:

    $ ./client
    send data: [hello world] to 127.0.0.1:9999
    $ ./server
    recv len:11, data:[hello world] from 127.0.0.1:44180
    
    $ ./client "I can do it"
    send data: [I can do it] to 127.0.0.1:9999
    $ ./server
    recv len:11, data:[I can do it] from 127.0.0.1:43607
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.2 客户端端口变化问题

      在3.1的例子中,客户端重复调用时,服务端收到的数据是从客户端不同的端口发来的,如下:

    $ ./server
    recv len:1, data:[1] from 127.0.0.1:43113
    recv len:1, data:[2] from 127.0.0.1:46748
    recv len:1, data:[3] from 127.0.0.1:43452
    
    • 1
    • 2
    • 3
    • 4

      客户端端口变化是由于客户端的socket没有绑定固定的端口,系统每次随机分配了一个端口。可以使用bind函数绑定固定端口,如下代码所示:

    memset(&client_addr, 0, sizeof(client_addr));                                                                                        
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = htons(CLIENT_PORT);
        client_addr.sin_addr.s_addr = inet_addr(CLIENT_IP);
        addr_len = sizeof(client_addr);
    
        ret = bind(socket_fd, (const struct sockaddr *)&client_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    客户端绑定端口后,再次测试验证:可以看到客户端的端口一直是绑定的端口10000

    $ ./server
    recv len:1, data:[1] from 127.0.0.1:10000
    recv len:1, data:[2] from 127.0.0.1:10000
    recv len:1, data:[3] from 127.0.0.1:10000
    
    • 1
    • 2
    • 3
    • 4

    3.3 服务器向客户端回写消息

    server.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_PORT 9999
    
    int server_udp()
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        addr_len = sizeof(server_addr);
        ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
        while (1) {
            memset(buf, 0, sizeof(buf));
            addr_len = sizeof(client_addr);
            ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
                             (struct sockaddr *)&client_addr, &addr_len);
            if (ret > 0) {
                printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                addr_len = sizeof(client_addr);
                ret = sendto(socket_fd, buf, ret, 0,
                        (struct sockaddr *)&client_addr, addr_len);
                if (ret > 0) {
                    printf("send data: [%s] to %s:%d\n", buf, inet_ntoa(client_addr.sin_addr),
                            ntohs(client_addr.sin_port));
                } else {
                    printf("ret:%d\n", ret);
                }
    
            } else if (0 == ret) {
                printf("ret:%d\n", ret);
            } else {
                printf("ret:%d\n", ret);
            }
        }
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        server_udp();
    
        return 0;
    }
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    client.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_IP "127.0.0.1"
    #define SERVER_PORT 9999
    #define CLIENT_IP "127.0.0.1"
    #define CLIENT_PORT 10000
    
    int client_udp(char *data)
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = htons(CLIENT_PORT);
        client_addr.sin_addr.s_addr = inet_addr(CLIENT_IP);
        addr_len = sizeof(client_addr);
    
        ret = bind(socket_fd, (const struct sockaddr *)&client_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
        addr_len = sizeof(server_addr);
        ret = sendto(socket_fd, data, strlen(data), 0,
                         (struct sockaddr *)&server_addr, addr_len);
        if (ret > 0) {
            printf("send data: [%s] to %s:%d\n", data, inet_ntoa(server_addr.sin_addr),
                            ntohs(server_addr.sin_port));
        } else {
            printf("ret:%d\n", ret);
        }
    
        memset(buf, 0, sizeof(buf));
        addr_len = sizeof(server_addr);
        ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
                (struct sockaddr *)&server_addr, &addr_len);
        if (ret > 0) {
            printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
                    inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
        } else if (0 == ret) {
            printf("ret:%d\n", ret);
        } else {
            printf("ret:%d\n", ret);
        }
    
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc < 2) {
            client_udp("hello world");
        } else {
            client_udp(argv[1]);
        }
    
        return 0;
    }
    
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    测试:

    $ ./client "hello world"
    send data: [hello world] to 127.0.0.1:9999
    recv len:11, data:[hello world] from 127.0.0.1:9999
    
    $ ./server
    recv len:11, data:[hello world] from 127.0.0.1:10000
    send data: [hello world] to 127.0.0.1:10000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.4 服务端状态和行为

    服务器不启动时,客户端发送消息时,会出现icmp的端口不可达报文,如下:
    在这里插入图片描述
    服务端启动时,可以通过netstat命令查看服务端接收消息的网络端口

    $ ./server
    
    $ netstat -anp | grep 9999
    udp        0      0 0.0.0.0:9999            0.0.0.0:*                           21845/server 
    
    • 1
    • 2
    • 3
    • 4

    3.5 非阻塞模式

      socket默认是阻塞模式,因此调用recvfrom()函数时,如果没有客户端发送数据,则recvfrom会一直阻塞,导致程序挂住,不能处理其它事情。因此,在实际应用场景中,会将socket设置为非阻塞。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_PORT 9999
    
    int server_udp()
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        int flag = 0;
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        flag = fcntl(socket_fd, F_GETFL, 0);
        flag = flag | O_NONBLOCK;
        fcntl(socket_fd, F_SETFL, flag);
    
        addr_len = sizeof(server_addr);
        ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
        while (1) {
            memset(buf, 0, sizeof(buf));
            addr_len = sizeof(client_addr);
            ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
                             (struct sockaddr *)&client_addr, &addr_len);
            if (ret > 0) {
                printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
            } else if (0 == ret) {
                printf("ret:%d\n", ret);
            } else {
                if (EAGAIN == errno) {
                    printf("ret:%d, errno:%d, %s\n", ret, errno, strerror(errno));
                } else {
                    printf("ret:%d, errno:%d, %s\n", ret, errno, strerror(errno));
                }
            }
        }
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        server_udp();
    
        return 0;
    }
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    测试:

    $ ./server
    ret:-1, errno:11, Resource temporarily unavailable                                                                                       
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ret:-1, errno:11, Resource temporarily unavailable
    ................................................................
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过轮询的方式调用recvfrom接口时,会出现循环打印日志的情况。
    为了解决这个问题,使用IO复用函数epoll来处理socket,只在有数据的时候才调用,示例如下:
    server.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_PORT 9999
    
    int server_udp()
    {
        int i = 0;
        int num = 0;
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        int flag = 0;
        int epoll_fd = -1;
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
        struct epoll_event event;
        struct epoll_event events_array[10];
    
        epoll_fd = epoll_create(10);
        if (epoll_fd < 0) {
            printf("epoll_create failure:%s\n", strerror(errno));
            return -1;
        }
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        flag = fcntl(socket_fd, F_GETFL, 0);
        flag = flag | O_NONBLOCK;
        fcntl(socket_fd, F_SETFL, flag);
    
        addr_len = sizeof(server_addr);
        ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
        event.events =  EPOLLIN;
        event.data.fd = socket_fd;
        ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);
        if(ret < 0) {
            printf("epoll_trl failure:%s\n", strerror(errno));
            close(epoll_fd);
            return -1;
        }
    
        while (1) {
            num = epoll_wait(epoll_fd, events_array, 10, 10000);
            if(num < 0) {
                printf("epoll_wait failure:%s\n", strerror(errno));
                close(epoll_fd);
                break;
            } else if(num == 0) {
                printf("eopll_wait timeout!\n");
                continue;
            }
    
            for (i = 0; i < num; i++) {
                if(events_array[i].events == EPOLLIN) {
                    ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
                            (struct sockaddr *)&client_addr, &addr_len);
                    if (ret > 0) {
                        printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
                                inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                    } else if (0 == ret) {
                        printf("ret:%d\n", ret);
                    } else {
                        if (EAGAIN == errno) {
                            printf("ret:%d, errno:%d, %s\n", ret, errno, strerror(errno));
                        } else {
                            printf("ret:%d, errno:%d, %s\n", ret, errno, strerror(errno));
                            epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events_array[i].data.fd, NULL);
                        }
                    }
                }
            }
        }
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        server_udp();
    
        return 0;
    }
    
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    client.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_IP "127.0.0.1"
    #define SERVER_PORT 9999
    #define CLIENT_IP "127.0.0.1"
    #define CLIENT_PORT 10000
    
    int client_udp(char *data)
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        struct sockaddr_in client_addr;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = htons(CLIENT_PORT);
        client_addr.sin_addr.s_addr = inet_addr(CLIENT_IP);
        addr_len = sizeof(client_addr);
    
        ret = bind(socket_fd, (const struct sockaddr *)&client_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
        addr_len = sizeof(server_addr);
        ret = sendto(socket_fd, data, strlen(data), 0,
                         (struct sockaddr *)&server_addr, addr_len);
        if (ret > 0) {
            printf("send data: [%s] to %s:%d\n", data, inet_ntoa(server_addr.sin_addr),
                            ntohs(server_addr.sin_port));
        } else {
            printf("ret:%d\n", ret);
        }
    
        close(socket_fd);
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc < 2) {
            client_udp("hello world");
        } else {
            client_udp(argv[1]);
        }
    
        return 0;
    }
    
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    测试 :

    $ ./client 1
    send data: [1] to 127.0.0.1:9999
    $ ./client 2
    send data: [2] to 127.0.0.1:9999
    $ ./client 3
    send data: [3] to 127.0.0.1:9999
    $ ./client 
    send data: [hello world] to 127.0.0.1:9999
    
    $ ./server
    recv len:1, data:[1] from 127.0.0.1:10000
    recv len:1, data:[2] from 127.0.0.1:10000
    recv len:1, data:[3] from 127.0.0.1:10000
    recv len:11, data:[hello world] from 127.0.0.1:10000
    eopll_wait timeout!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    四、关键说明

    • UDP 是无连接的,即发送数据之前不需要像TCP那样建立连接,因此减少了建立连接带来的开销和发送数据的时延
    • UDP 数据是面向报文传输的。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界,因此不涉及到粘包问题。不过应用程序必须选择合适大小的报文。
    • UDP 没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。
    • UDP适用于实时性要求很高,并且几乎不能容忍重传的应用场景,比如直播
    • UDP适用于应用程序对传输的可靠性要求不高,但是对传输速度和延迟要求较高的场景。UDP适合于实时数据传输,如VoIP中的语音和视频通信,因为这种场景在偶尔丢失一两个或少量数据包时,对接收方也不会造成太大的影响
  • 相关阅读:
    【C++ Primer Plus】第6章 分支语句和逻辑运算符
    每日练习------实现双色球的彩票功能。规则:从36个红球中随机选择不重复的6个数,从15个篮球中随机选择1个组成一注彩票。可以选择买多注。
    Tomcat选择不同配置文件启动
    linux centos7 安装nginx
    【adb】adb相关命令行及adb传输文件权限问题 remote couldn‘t create file: Read-only file system
    【数据结构】堆综合的运用
    gdb调试中设置监控点watch ,rwatch ,awatch 的区别
    JDBC和数据库连接池
    如何在Win系统搭建Oracle数据库并实现远程访问【内网穿透】
    MaxEnt模型融合技术的物种分布模拟、参数优化方法、结果分析制图与论文写作
  • 原文地址:https://blog.csdn.net/szkbsgy/article/details/127949147