使用UDP套接字编程详解【C语言】

UDP(User Datagram Protocol,用户数据报协议)是一种面向无连接的传输层协议,用于在计算机网络上发送数据。它与 TCP(Transmission Control Protocol,传输控制协议)相比具有轻量、高效的特点,但牺牲了可靠性和顺序传输的保证。udp的通信过程默认也是阻塞的。

  • 无连接:UDP 是无连接的,不会维护连接状态,每次发送数据都是独立的。
  • 不可靠性:UDP 不保证数据的可靠传输,数据报文可能丢失、重复或顺序错乱。
  • 数据包大小:UDP 数据报文大小受限制,超过网络的最大传输单元(MTU)会被分片,可能导致性能下降。
  • 顺序保证:UDP 不保证数据报文的顺序交付,接收方收到数据的顺序可能与发送方不同。
  • 适用场景:适合实时数据传输,如实时音视频流、游戏数据更新等,对实时性要求高而对数据完整性要求较低的场景。

1.UDP通信流程

  • 创建套接字

    • 首先,服务器端和客户端分别创建 UDP 套接字。套接字是应用程序与网络之间的接口,用于发送和接收数据。
  • 绑定地址和端口(可选):

    • 在服务器端,可以选择将套接字绑定到特定的 IP 地址和端口上,以便客户端能够发送数据到指定的地址。客户端通常不需要绑定,而是直接向服务器发送数据。
  • 发送数据报文

    • 客户端通过 sendto 函数将数据报文发送到服务器端的指定地址和端口。UDP 不需要建立连接,因此每个数据报文都是独立发送的,不会进行握手或状态管理。
  • 接收数据报文

    • 服务器端使用 recvfrom 函数接收从客户端发送过来的数据报文。这个函数能够获取发送方的地址信息,以便服务器端可以知道从哪个客户端接收到数据。
  • 处理数据

    • 服务器端和客户端在接收到数据后,可以根据协议和应用程序的需求对数据进行处理,例如解析、验证、响应等操作。
  • 关闭套接字

    • 当通信结束或者程序不再需要使用套接字时,服务器端和客户端分别使用 close 函数关闭套接字,释放资源。

2.相关操作函数

2.1 socket 函数

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建一个套接字。
参数:domain:指定协议族,常用的有 AF_INET(IPv4 地址)和 AF_INET6(IPv6 地址)。type:指定套接字类型,常用的有 SOCK_DGRAM(数据报套接字,UDP)和 SOCK_STREAM(流式套接字,TCP)。protocol:通常设置为 0,表示使用默认协议。
返回值:返回一个非负的文件描述符(套接字描述符),表示套接字创建成功;返回 -1 表示创建失败。

2.2 bind 函数

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:将一个本地地址绑定到套接字。
参数:sockfd:套接字描述符,即 socket 函数返回的文件描述符。addr:指向 sockaddr 结构体的指针,包含要绑定的 IP 地址和端口信息。addrlen:addr 结构体的长度。
返回值:成功返回 0,失败返回 -1。
注意事项:
服务器端通常需要绑定一个固定的 IP 地址和端口,以便客户端能够发送数据到指定的地址。
客户端一般不需要绑定,而是直接发送数据到服务器端。

2.3 sendto 函数

#include <sys/socket.h>
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:指向 sockaddr 结构体的指针,包含目标地址(IP 地址和端口)信息。IP和端口都存储在这里边, 是大端存储的addrlen:dest_addr 结构体的长度。
返回值:成功返回发送的字节数,失败返回 -1。
注意事项:
UDP 是无连接的,每次发送数据时都要指定目标地址。
数据报文的大小应小于网络的最大传输单元(MTU),以免发生分片,影响性能。

2.4 recvfrom 函数

#include <sys/socket.h>
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 结构体的指针,用于存放发送方的地址信息。IP和端口都存储在这里边, 是大端存储的addrlen:src_addr 结构体的长度,在调用时需传入 src_addr 的实际长度。
返回值:成功返回接收到的字节数,失败返回 -1。
注意事项:
recvfrom 函数会阻塞直到接收到数据或发生错误。
可以通过 src_addr 参数获取发送方的 IP 地址和端口信息,用于处理接收到的数据。

2.5 close 函数

#include <unistd.h>
int close(int sockfd);
功能:关闭套接字。
参数:sockfd:要关闭的套接字描述符。
返回值:成功返回 0,失败返回 -1。
注意事项:
在不再需要使用套接字时应及时关闭,释放系统资源。
关闭套接字后,相关的文件描述符将不再可用,不能再进行数据发送和接收操作。

3.示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define PORT 8888
#define MAXLINE 1024int main() {int sockfd;struct sockaddr_in server_addr, client_addr;char buffer[MAXLINE];socklen_t client_len;ssize_t n;// 创建UDP套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&server_addr, 0, sizeof(server_addr));memset(&client_addr, 0, sizeof(client_addr));// 设置服务器地址结构server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(PORT);// 绑定服务器地址到套接字if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}printf("Server listening on port %d...\n", PORT);while (1) {client_len = sizeof(client_addr);// 接收来自客户端的消息n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&client_addr, &client_len);buffer[n] = '\0';// 打印客户端信息char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);printf("Received message from %s:%d - %s\n", client_ip, ntohs(client_addr.sin_port), buffer);// 发送响应消息给客户端strcpy(buffer, "Hello from server");sendto(sockfd, (const char *)buffer, strlen(buffer), MSG_CONFIRM, (const struct sockaddr *)&client_addr, client_len);printf("Server : Hello message sent\n");}close(sockfd);return 0;
}

作为数据接收端,服务器端通过bind()函数绑定了固定的端口,然后基于这个固定的端口通过recvfrom()函数接收客户端发送的数据,同时通过这个函数也得到了数据发送端的地址信息(recvfrom的第三个参数),这样就可以通过得到的地址信息通过sendto()函数给客户端回复数据了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define PORT 8888
#define MAXLINE 1024int main() {int sockfd;struct sockaddr_in server_addr;char buffer[MAXLINE];socklen_t server_len;ssize_t n;// 创建UDP套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&server_addr, 0, sizeof(server_addr));// 设置服务器地址结构server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");while (1) {printf("Enter message to send : ");fgets(buffer, MAXLINE, stdin);// 发送消息给服务器sendto(sockfd, (const char *)buffer, strlen(buffer), MSG_CONFIRM, (const struct sockaddr *)&server_addr, sizeof(server_addr));printf("Message sent to server.\n");// 接收服务器的响应消息n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&server_addr, &server_len);buffer[n] = '\0';printf("Server : %s\n", buffer);}close(sockfd);return 0;
}

作为数据发送端,客户端不需要绑定固定端口,客户端使用的端口是随机绑定的(也可以调用bind()函数手动进行绑定)。客户端在接收服务器端回复的数据的时候需要调用recvfrom()函数,因为客户端在发送数据之前就已经知道服务器绑定的固定的IP和端口信息了,所以接收服务器数据的时候就可以不保存服务器端的地址信息,直接将函数的最后两个参数指定为NULL即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/49349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

英语(二)-我的学习方式

章节章节汇总我的学习方式历年真题作文&范文 目录 1、背单词 2、学语法 3、做真题 4、胶囊助学计划 写在最前&#xff1a;我是零基础&#xff0c;初二就听天书的那种。 本专栏持续更新学习资料 1、背单词 单词是基础&#xff0c;一定要背单词&#xff01;考纲要求要…

云动态摘要 2024-07-23

给您带来云厂商的最新动态,最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起! [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造,可通过对话方式生成可视化…

中文分词库 jieba 详细使用方法与案例演示

1 前言 jieba 是一个非常流行的中文分词库&#xff0c;具有高效、准确分词的效果。 它支持3种分词模式&#xff1a; 精确模式全模式搜索引擎模式 jieba0.42.1测试环境&#xff1a;python3.10.9 2 三种模式 2.1 精确模式 适应场景&#xff1a;文本分析。 功能&#xff1…

【Zotero插件】Zotero Tag为文献设置阅读状态 win11下相关设置

【Zotero插件设置】Zotero Tag为文献设置阅读状态 win11下相关设置 1.安装Zotero Tag1.1安装1.2配置1.3 win11的相关设置1.3.1 字体安装 参考教程 2.支持排序的标注参考教程 1.安装Zotero Tag 1.1安装 Zotero Tag插件下载链接安装方法&#xff1a;Zotero–》工具–》附加组件…

googleTest 源码主线框架性分析——TDD 01

TDD&#xff0c;测试驱动开发&#xff0c;英文全称Test-Driven Development&#xff0c;简称TDD&#xff0c;是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码&#xff0c;然后只编写使测试通过的功能代码&#xff0c;通过测试来推…

苹果和乔布斯的传奇故事,从车库创业到万亿市值巨头

苹果公司的品牌故事&#xff0c;就像一部充满创新、挑战与辉煌的科幻大片&#xff0c;让人目不暇接。 故事始于1976年&#xff0c;那时&#xff0c;年轻的史蒂夫乔布斯与斯蒂夫沃兹尼亚克在加州的一个简陋车库里&#xff0c;用他们的热情和智慧&#xff0c;捣鼓出了世界上第一…

python学习之闭包与装饰器

一、闭包 闭包允许一个函数访问并操作函数外部的变量&#xff08;即父级作用域中的变量&#xff09;&#xff0c;即使在该函数外部执行。 特性&#xff1a; (1)外部函数嵌套内部函数。 (2)外部函数可以返回内部函数。 (3)内部函数可以访问外部函数的局部变量。 def out()…

linux中使用docker安装mongodb

随着容器的普及&#xff0c;越来越多服务都喜欢跑在容器中&#xff0c;并且安装也很方便快捷&#xff0c;接下来一起看下linux中使用docker来安装mongodb吧&#xff01; 1.首先安装docker&#xff1b; 使用Yum 进行安装&#xff0c;我安装docker比较喜欢参考阿里云中的安装步骤…

通过泛型+函数式编程封装成通用解决方案|缓存穿透、缓存击穿,缓存雪崩

缓存更新方法封装 用到了泛型、函数式编程。 使用函数式编程是因为我们这个是一个通用的工具&#xff0c;使用泛型&#xff08;泛型&#xff08;Generics&#xff09; 允许我们定义类、接口和方法&#xff0c;可以使用不同类型的参数进行操作&#xff09;可以实现数据类型的通…

Mem0 - 个人 AI 的内存层

文章目录 一、关于 Mem0核心功能&#x1f511;路线图 &#x1f5fa;️常见用例Mem0与RAG有何不同&#xff1f; 二、快速入门 &#x1f680;1、安装2、基本用法&#xff08;开源&#xff09;3、高级用法&#x1f527;4、大模型支持 三、MultiOn1、概览2、设置和配置4、将记忆添加…

鸿蒙仓颉语言【模块module】

module 模块 模块配置文件&#xff0c;这里指项目的modules.json 文件&#xff0c;用于描述代码项目的基础元属性。 {"name": "file name", //当前项目的名称"description": "项目描述", //项目描述"version": "1.0…

视频汇聚平台EasyCVR启动出现报错“cannot open shared object file”的原因排查与解决

安防视频监控EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、fmp4等&#xf…

kafka基础介绍

一、为什么使用消息队列 1.使用同步的通信方式来解决多个服务之间的通信 同步的通信方式会存在性能和稳定性的问题。 2.使用异步的通信方式 针对于同步的通信方式来说,异步的方式,可以让上游快速成功,极大提高了系统的吞吐量。而且在分布式系统中,通过下游多个服务的 分布式事…

怎么拼接几张图片为一张?拼接几张图片为一张的四种方法推荐

怎么拼接几张图片为一张&#xff1f;要将几张图片拼接成一张完整的图像&#xff0c;你可以利用现代软件和工具来实现这一操作。这种技术可以帮助你创建更大、更复杂的图像&#xff0c;无论是为了美学上的需要还是为了展示更完整的视觉信息。通过合并多张图片&#xff0c;你可以…

Windows上让Qt支持https请求

一.前言 Qt默认其实支持https的&#xff0c;但需要openssl的支持。所以有时候你代码中写了支持https的请求连接&#xff0c;发现程序可以运行&#xff0c;但到了https请求时会报错&#xff0c;如下&#xff1a; 这就是没有openssl的支持&#xff0c;导致QSslSocket无法进行ht…

关于c#的简单应用三题

#region 输入一个正整数&#xff0c;求1~这个数的阶乘 public static void Factorial(int a) { int result 1; for (int i 1; i < a; i) { result result * i; } Console.WriteLine(result); } #endregion #region 一个游戏&#…

【Vue3】从零开始编写项目

【Vue3】从零开始编写项目 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的…

阶梯-度小满春招算法方向第1批

问题的题面是典型的最长上升子序列问题。求方案数属动态规划问题&#xff0c;可推出以a[i]为最大节点的上升子序列方案数公式 dp[i]{dp[j] , 1<j<i-1&&f[j]1f[i]} &#xff08;f为最大上升子序列&#xff09;。 并且这个方案总数不会超过n&#xff0c;因此也…

C2W1.Assignment.Autocorrect.Part2

理论课&#xff1a;C2W1.Auto-correct 文章目录 3. Combining the edits3.1 Exercise 8.Edit one letter3.2 Exercise 9.Edit two letters3.3 Exercise 10.suggest spelling suggestions 4. Minimum Edit Distance4.1 Dynamic ProgrammingExercise 11Test All-in-one 5. Backt…

javaScrip的学习(一)

目录 引言 一、java和JavaScript的联系 二、js中的弹出框 1.alert弹出框 2.confirm带确认取消的按钮弹框 3.prompt带有提示信息且带有输入框的弹框 4.输出到网页中 ​三、js引入方式 1. 放在script标签中 2.放在外部js文件中 四、执行顺序 五、书写规范 1. 语句结…