【网络编程】深入了解UDP协议:快速数据传输的利器

  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,请在评论区指正,感谢🙏


        在计算机网络中,UDP(User Datagram Protocol)是一种重要的传输层协议,与TCP(Transmission Control Protocol)一样位于 OSI 模型的传输层。但与TCP不同,UDP提供了一种无连接、轻量级的数据传输方式,适用于需要快速传输数据的应用场景。本文将深入探讨UDP协议的特点、用途以及与TCP的对比。

一、UDP的特点

  1. 无连接性:UDP不需要在通信前建立连接,也不维护连接状态,因此它更加轻量级。这使得UDP适用于实时数据传输,如音频和视频流。

  2. 不可靠性:UDP不提供数据包的可靠性传输。这意味着数据包可能会丢失、重复或无序到达目标。因此,UDP通常用于那些可以容忍一些数据丢失的应用,如实时多媒体流。

  3. 高性能:由于不需要建立连接和维护状态信息,UDP的开销较低,具有较高的性能。这使得它成为一种适用于高吞吐量、低延迟的协议。

  4. 头部小:UDP的头部相对较小,只包含源端口、目标端口、长度和校验和等字段,这有助于减少数据传输时的开销。

  5. 多播和广播:UDP支持多播和广播,可以同时向多个接收方发送数据包,适用于一对多或多对多通信。

二、UDP的用途

        UDP在许多应用中发挥着重要作用,包括但不限于:

  • 实时音视频传输:VoIP电话、视频会议和直播流都使用UDP来传输实时音视频数据,因为它具有低延迟和快速传输的特性。

  • DNS查询:域名系统(DNS)使用UDP来快速查询域名解析。

  • 游戏数据传输:在线游戏经常使用UDP来传输游戏数据,以确保低延迟和实时性。

  • 网络广播:UDP用于发送广播消息,例如局域网内的设备发现和服务广告。

三、UDP实现服务器与客户端相互通信

        在此之前我们已经学习掌握了TCP/IP实现服务器与客户端通信,链接我放这里:

【网络编程】TCP传输控制协议(Transmission Control Protocol)_祐言QAQ的博客-CSDN博客

        那么现在让我们一起来学习一下UDP如何实现通信。

1.UDP服务器与客户端设计思路

        不难看出其中与TCP通信有几个不同点:

①套接字类型:

        TCP使用 SOCK_STREAM 表示流式套接字,这意味着它提供面向连接的、可靠的、基于字节流的通信;
        UDP使用 SOCK_DGRAM 表示数据报套接字,这表明它提供无连接的、不可靠的、基于数据报的通信。


②通信流程:

        在TCP中,通信需要经过 listen、accept 和 connect 过程,其中建立连接是必要的;
        在UDP中,通信是无连接的,不需要建立连接,因此不需要进行 listen、accept 和 connect 步骤。


③收发数据的函数:

        在TCP中,通常使用 recv 和 send 函数来进行数据的接收和发送;
        在UDP中,通常使用 recvfrom 和 sendto 函数来进行数据的接收和发送。这些函数需要指定目标地址,因为UDP是无连接的,每个数据包都需要包含目标地址信息。

2.具体实现代码

udp_server.c

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>// 定义一个结构体用于存储客户端信息
struct Client
{struct sockaddr_in addr; // 客户端地址结构体int ser_socket;         // 服务器套接字
};// 线程函数,用于发送数据
void *send_data(void *arg)
{struct Client *cli = (struct Client *)arg;char buf[1024];while (1){scanf("%[^\n]", buf); // 从用户输入读取数据while (getchar() != '\n');// 发送数据到客户端sendto(cli->ser_socket, buf, strlen(buf), 0, (struct sockaddr *)&(cli->addr), sizeof(cli->addr));}
}int main(int argc, char const *argv[])
{if (argc != 2){printf("./server <port>\n");return -1;}// 创建套接字socketint ser_socket = socket(AF_INET, SOCK_DGRAM, 0);if (ser_socket == -1){perror("socket");return -1;}// 设置套接字属性,SO_REUSEADDR 允许地址端口重用int on = 1;if (setsockopt(ser_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){perror("setsockopt");return -1;}// 初始化地址结构体struct sockaddr_in addr;addr.sin_family = AF_INET;   							// 地址簇addr.sin_port = atoi(argv[1]);							// 端口(一般以传参的传进来)// addr.sin_addr.s_addr = inet_addr("192.168.1.128");	// IP地址addr.sin_addr.s_addr = htonl(INADDR_ANY);				// 用特殊的"0.0.0.0"这个IP来绑定本机IP地址// bind 绑定IP跟PORTint b = bind(ser_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));if(b == -1){perror("bind");return -1;}printf("绑定成功\n");struct sockaddr_in c_addr;		// IPV4地址结构体int addrlen = sizeof(c_addr);char buf[1024];// 接收客户端的第一条消息recvfrom(ser_socket, buf, sizeof(buf), 0, (struct sockaddr *)&c_addr, &addrlen);printf("[%s] [%d]:%s\n", inet_ntoa(c_addr.sin_addr), c_addr.sin_port, buf);struct Client cli;cli.addr = c_addr;cli.ser_socket = ser_socket;// 创建一个线程用来发送数据pthread_t pid;pthread_create(&pid, NULL, send_data, &cli);// 接收/发送数据 recvfrom/sendtowhile(1){bzero(buf, sizeof(buf));// 每接收一条客户端发送的信息,保存一次客户端的IP+PORTrecvfrom(ser_socket, buf, sizeof(buf), 0, (struct sockaddr *)&c_addr, &addrlen);// 第一次接收,创建线程发送数据,将套接字,对方的IP地址传递给线程任务函数printf("[%s] [%d]:%s\n", inet_ntoa(c_addr.sin_addr), c_addr.sin_port, buf);}// 关闭套接字closeclose(ser_socket);return 0;
}

udp_client.c

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>// 线程函数,用于接收数据
void *recv_data(void *arg)
{int cli_socket = *(int *)arg;struct sockaddr_in c_addr;int addrlen = sizeof(c_addr);char buf[1024];while(1){bzero(buf, sizeof(buf));// 每接收一条客户端发送的信息,保存一次客户端的IP+PORTrecvfrom(cli_socket, buf, sizeof(buf), 0, (struct sockaddr *)&c_addr, &addrlen);printf("[%s] [%d]:%s\n", inet_ntoa(c_addr.sin_addr), c_addr.sin_port, buf);}
}int main(int argc, char const *argv[])
{if (argc != 3){printf("./client <ip> <port>\n");return -1;}// (1) 创建套接字socketint cli_socket = socket(AF_INET, SOCK_DGRAM, 0);if (cli_socket == -1){perror("socket");return -1;}// (2) 初始化地址结构体(服务器的)struct sockaddr_in addr;addr.sin_family = AF_INET;   				// 地址簇addr.sin_port = atoi(argv[2]);				// 服务器端的端口(一般以传参的传进来)addr.sin_addr.s_addr = inet_addr(argv[1]);	// 服务器端的IP地址(一般以传参的传进来)// 先发一条上线的消息给serverchar buf[1024] = "on line";sendto(cli_socket, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));// 创建线程用来接收数据pthread_t pid;pthread_create(&pid, NULL, recv_data, &cli_socket);// (3) 发送数据while(1){scanf("%[^\n]", buf); // 从用户输入读取数据while(getchar()!='\n');// 发送数据bufsendto(cli_socket, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));}// (5) 关闭套接字closeclose(cli_socket);return 0;
}

四、UDP套接字获取和设置

        在UDP套接字编程中,你可以使用 getsockoptsetsockopt 函数来获取和设置套接字的属性。以下是一些常见的UDP套接字属性以及如何使用这两个函数来处理它们:

获取套接字属性(使用 getsockopt 函数):

  1. SO_RCVBUF 和 SO_SNDBUF

    • 功能:获取套接字的接收缓冲区和发送缓冲区的大小。
    • 示例代码:
      int buffer_size;
      socklen_t optlen = sizeof(buffer_size);
      getsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, &optlen);
      // 现在 buffer_size 中包含接收缓冲区的大小
      
  2. SO_RCVTIMEO 和 SO_SNDTIMEO

    • 功能:获取套接字的接收超时时间和发送超时时间。
    • 示例代码:
      struct timeval timeout;
      socklen_t optlen = sizeof(timeout);
      getsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, &optlen);
      // 现在 timeout 中包含接收超时时间
      

设置套接字属性(使用 setsockopt 函数):

  1. SO_RCVBUF 和 SO_SNDBUF

    • 功能:设置套接字的接收缓冲区和发送缓冲区的大小。
    • 示例代码:
      int buffer_size = 8192; // 设置缓冲区大小
      setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size));
      

  2. SO_RCVTIMEO 和 SO_SNDTIMEO

    • 功能:设置套接字的接收超时时间和发送超时时间。
    • 示例代码:
      struct timeval timeout;
      timeout.tv_sec = 5; // 设置超时时间为5秒
      timeout.tv_usec = 0;
      setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
      

        这些是一些常见的UDP套接字属性设置选项。可以根据需要使用 getsockoptsetsockopt 函数来获取和设置套接字的其他属性,具体选项取决于你的应用程序的需求。套接字属性设置允许你自定义套接字的行为,以满足不同的网络通信需求。在上面的服务端与客户端通信中也有用到一些示例。

五、与TCP的对比

UDP和TCP是两种不同的传输协议,它们在以下方面有所不同:

  • 连接性:UDP无连接,TCP面向连接。

  • 可靠性:UDP不提供可靠性传输,TCP提供可靠性传输。

  • 开销:UDP开销较低,TCP开销较高。

  • 适用场景:UDP适用于需要快速传输但可以容忍一些数据丢失的应用,而TCP适用于需要确保数据完整性和可靠性的应用。

六、总结

        UDP协议在网络通信中扮演着重要的角色,尤其是在需要实时性和低延迟的应用中。虽然它不提供可靠性传输,但在正确的应用场景下,UDP是一个强大的工具,能够满足快速数据传输的需求。了解UDP的特点和用途有助于网络工程师更好地选择合适的协议来满足应用程序的需求。

        更多C/C++语言Linux系统数据结构ARM板实战相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            系统、网络编程

                                     探索C++

                                             6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉🎉🎉感谢关注🎉🎉🎉

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

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

相关文章

mysql UUID 作为主键的问题

UUID 在MySQL中&#xff0c;可以使用UUID()函数来生成一个新的UUID值。该函数的返回值是一个字符串类型&#xff0c;表示一个32位的十六进制数字&#xff0c;其中包含4个连字符“-”&#xff0c;例如&#xff1a;“6ccd780c-baba-1026-9564-0040f4311e29”。 varchar(32) 32*4…

JAVA毕业设计096—基于Java+Springboot+Vue的在线教育系统(源码+数据库+18000字论文)

基于JavaSpringbootVue的在线教育系统(源码数据库18000字论文)096 一、系统介绍 本系统前后端分离 本系统分为管理员、用户两种角色(管理员角色权限可自行分配) 用户功能&#xff1a; 注册、登录、课程预告、在线课程观看、学习资料下载、学习文章预览、个人信息管理、消息…

Midjourney学习(四)光源类型prompt

序号类别光线名称英文名称描述用途示例1光线质地硬光Hard Light直接照射在主题上&#xff0c;产生明显的阴影和高对比度。强调轮廓&#xff0c;增加照片的戏剧性2光线质地软光/柔光Soft Light光线经过散射或扩散&#xff0c;产生柔和的阴影和低对比度。平滑细节&#xff0c;适合…

将符号分隔的文本文件txt转换为excel的实现

文本文件如下&#xff1a; 现在不好处理&#xff0c;打算将其转换为excel&#xff0c;其中通过冒号分割&#xff1a;line.split(":") main方法如下&#xff1a; public static void main(String[] args) {String textFilePath "D:\\zoom\\期刊\\J_Medline\\J_…

vulhub-tomcat弱口令

1.启动靶场 进入文件 进入目录 进入到靶场 启动靶场 docker-compose up -d 2.查看 ip地址 3.使用nmap对ip进行 扫描 发现存在8080的端口&#xff0c;并且端口是开放的状态&#xff0c;apache&#xff0c;tomcat搭建的 4.访问ip地址的端口 点击Manager app 6.开启BP进行抓包 随…

【PHP】手术麻醉系统源码

手术麻醉信息管理系统覆盖了与麻醉相关的各个临床工作环节&#xff0c;可详细记录病人从进入手术室、手术中、到手术结束的全部数据&#xff0c;包括各类仪器的监测数据、麻药、用药、事件、输氧、插管、拔管、输液、出液、输血、呼吸、电子病例、检验信息、检查结果、医嘱、病…

Java- 虚拟机学习总结

Java文件编译&#xff0c;加载过程 写好java文件&#xff0c;jdk会通过javac编译class文件&#xff0c;classLaoder通过classpath将字节码文件加载进入jre jvm数据区 包含栈&#xff0c;堆&#xff0c;程序计数器&#xff0c;方法区&#xff0c;本地方法栈 JAVA里的常量&…

Docker Desktop 设置镜像环境变量

点击run 展开Optional settings container name &#xff1a;容器名称 Ports&#xff1a;根据你需要的端口进行输入&#xff0c;不输入则默认 后面这个 比如我这个 5432 Volumes&#xff1a;卷&#xff0c;也就是做持久化 需要docker 数据保存的地方 Environment variables…

Qt实现图书管理系统(C++)

文章目录 数据库表的实现创建表将powerDesigner里面的表导出成xxx.sql脚本将SQL文件导入数据库创建表 图书管理系统思维导图创建工程开发阶段创建Dlg_login登录页面login页面样式主页页面布局主函数测试login设置logo打包程序子页面的样子将子页面放到StackedWidget里面按钮直接…

物联网智慧种植农业大棚系统

一、项目背景 智慧农业是是将物联网技术和农业生产箱管理的新型农业&#xff0c;依托部署在农业生产现场的各种传感节点&#xff0c;以物联网网关为通道形成数据传输网络&#xff0c;可以实现控制柜、环境监测传感器、气象监测机器等设备的远程监控&#xff0c;达到及时高校的…

Lesson5-1:OpenCV视频操作---视频读写

学习目标 掌握读取视频文件&#xff0c;显示视频&#xff0c;保存视频文件的方法 1 从文件中读取视频并播放 在OpenCV中我们要获取一个视频&#xff0c;需要创建一个VideoCapture对象&#xff0c;指定你要读取的视频文件&#xff1a; 创建读取视频的对象 cap cv.VideoCapt…

IIS WebDAV配置,https绑定及asp设置

IIS支持标准CGI&#xff0c;因此可以用程序语言针对STDIN和STDOUT开发。 IIS CGI配置和CGI程序FreeBasic, VB6, VC 简单样例_Mongnewer的博客-CSDN博客 IIS支持脚本解释CGI&#xff0c;因此可以用脚本语言针对STDIN和STDOUT开发。 IIS perl python cbrother php脚本语言配置…