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的定义以及应用场景,列出了编程中常用的接口,最后编写程序进行测试。

相关推荐
hellojackjiang201139 分钟前
开源轻量级IM框架MobileIMSDK的鸿蒙NEXT客户端库已发布
网络·即时通讯·im开发·mobileimsdk-鸿蒙端
F-2H1 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
aherhuo1 小时前
基于openEuler22.09部署OpenStack Yoga云平台(一)
linux·运维·服务器·openstack
WebDeveloper20011 小时前
如何使用美国域名中心US Domain Center和WordPress创建商业网站
运维·服务器·css·网络·html
檀越剑指大厂2 小时前
【Linux系列】Shell 脚本中的条件判断:`[ ]`与`[[ ]]`的比较
linux·运维·服务器
车载诊断技术3 小时前
电子电气架构 --- 什么是EPS?
网络·人工智能·安全·架构·汽车·需求分析
KevinRay_3 小时前
Python超能力:高级技巧让你的代码飞起来
网络·人工智能·python·lambda表达式·列表推导式·python高级技巧
2301_819287124 小时前
ce第六次作业
linux·运维·服务器·网络
CIb0la4 小时前
GitLab 停止为中国区用户提供 GitLab.com 账号服务
运维·网络·程序人生
武汉联从信息4 小时前
如何使用linux日志管理工具来管理oracle osb服务器日志文件?
linux·运维·服务器