Linux应用 UDP网络编程

1、前言

1.1 定义

UDP 是一种无连接的传输协议,它不需要在通信之前建立连接,也不保证数据传输的可靠性。UDP 将数据打包成数据报进行传输,不进行数据分段和重组,因此传输效率较高。

1.2 应用场景

  • 实时性要求高的应用:由于 UDP 不保证数据传输的可靠性,适用于实时性要求高的应用,如音频/视频流的传输、在线游戏等。
  • 广播和多播通信:UDP 支持广播和多播通信,适用于需要向多个主机发送相同数据的场景。
  • 简单的数据传输:对于一些简单的数据传输场景,如 DNS 查询、SNMP 等,UDP 是一个较为合适的选择。

1.3 网络中的作用

在网络分层模型中,UDP 位于传输层,与 TCP 一起为应用层提供数据传输服务。

  • 为应用层提供无连接的数据传输服务:UDP 不需要建立连接,适用于一些实时性要求高、数据传输较简单的应用场景。
  • 提供数据报服务:UDP 将数据打包成数据报进行传输,不进行数据分段和重组,传输效率较高。
  • 支持广播和多播通信:UDP 支持向多个主机发送相同数据,适用于广播和多播通信场景。

2、常用接口

2.1 socket函数

创建一个套接字,用于后续的数据传输。

复制代码
int socket(int domain, int type, int protocol);

入参:

  • domain:指定协议族,通常为 AF_INET(IPv4)或 AF_INET6(IPv6)。
  • type:指定套接字类型,通常为 SOCK_DGRAM 表示 UDP 套接字。
  • protocol:指定协议,通常为 0 表示使用默认协议。

返回值:

  • 成功时返回套接字描述符,失败时返回 -1

2.2 blind函数

将套接字绑定到特定的IP地址和端口号。

复制代码
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

入参:

  • sockfd:套接字描述符,由socket()函数返回
  • addr:指向sockaddr结构体的指针,包含要绑定的IP地址和端口号信息
  • addrlensockaddr结构体的长度

返回值:

  • 成功时返回0,失败时返回-1

2.3 sendto函数

向指定地址发送数据。

复制代码
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

入参:

  • sockfd:套接字描述符。
  • buf:指向要发送数据的缓冲区。
  • len:要发送数据的长度。
  • flags:发送标志,通常为 0
  • dest_addr:目标地址的结构体指针,可以是 struct sockaddr_instruct sockaddr_in6
  • addrlen:目标地址结构体的长度。

返回值:

  • 成功时返回发送的字节数,失败时返回 -1

2.4 recvfrom 函数

从指定的目标地址接收数据。

复制代码
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

入参:

  • sockfd:套接字描述符
  • buf:指向接收数据的缓冲区
  • len:缓冲区长度
  • flags:接收控制标志,一般设为0
  • src_addr:指向发送方地址信息的sockaddr结构体指针
  • addrlen:发送方地址结构体的长度

返回值:

  • 成功时返回接收的字节数,失败时返回-1

3、编程测试

3.1 一般编程步骤

UDP 服务器端编程流程:

  • 创建套接字:调用socket()函数创建一个UDP套接字。

  • 绑定地址:调用bind()函数将服务器端地址绑定到套接字上。

  • 接收数据:循环调用recvfrom()函数接收客户端发送的数据。

  • 处理数据:对接收到的数据进行处理,可以根据需求进行相应的逻辑操作。

  • 发送响应:根据处理结果,调用sendto()函数向客户端发送响应数据。

  • 关闭套接字:通信结束后,调用close()函数关闭套接字。

UDP 客户端编程流程:

  • 创建套接字:调用socket()函数创建一个UDP套接字。

  • 准备数据:准备要发送的数据,并将目标服务器地址信息填入sockaddr结构体中。

  • 发送数据:调用sendto()函数向服务器发送数据。

  • 接收响应:调用recvfrom()函数接收服务器端的响应数据。

  • 处理响应:对接收到的响应数据进行处理,可以根据需求进行相应的逻辑操作。

  • 关闭套接字:通信结束后,调用close()函数关闭套接字。

3.2 服务器编程

编写服务器代码如下:

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8888
#define BUF_SIZE 1024

int main() 
{
    int sockfd;
    struct sockaddr_in server_addr, client_addr;
    char buffer[BUF_SIZE];

    // 创建 UDP 套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    // 绑定服务器地址和端口
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);
    bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    printf("UDP Server listening on port %d...\n", PORT);

    socklen_t len = sizeof(client_addr);
    
    // 接收数据
    recvfrom(sockfd, buffer, BUF_SIZE, 0, (struct sockaddr*)&client_addr, &len);
    printf("Received from client: %s\n", buffer);

    // 回复客户端数据
    sendto(sockfd, "Message received", strlen("Message received"), 0, (struct sockaddr*)&client_addr, len);

    close(sockfd);
    return 0;
}

3.3 客户端编程

编写客户端代码如下:

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8888
#define SERVER_IP "127.0.0.1"
#define BUF_SIZE 1024

int main() 
{
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[BUF_SIZE] = "msg from client";

    // 创建 UDP 套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    // 设置服务器地址和端口
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    server_addr.sin_port = htons(PORT);

    // 发送数据到服务器
    sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));

    // 接收服务器回复的数据
    recvfrom(sockfd, buffer, BUF_SIZE, 0, NULL, NULL);
    printf("Server replied: %s\n", buffer);

    close(sockfd);
    return 0;
}

3.4 测试

开启两个终端进行测试,先开启服务器,再开启客户端,测试结果如下:

4、总结

本文讲解了UDP的定义以及应用场景,列出了编程中常用的接口,最后编写程序进行测试。

相关推荐
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展