UDP传输协议Linux C语言实战

news2024/10/23 8:43:17

文章目录

  • 1.UDP简介
    • 1.1特点
    • 1.2 UDP协议头部格式
      • 1.2.1 **UDP头部**:
      • 1.2.2 **头部意义**:
      • 1.2.3 **头部参数**:
    • 1.3 UDP数据长度控制
    • 1.4 UDP协议建立框架
  • 2. 函数介绍
    • 2.1 sendto函数
    • 2.2 recvform函数
    • 2.3 其他函数
  • 3.实例
    • 3.1 通用结构体、IPV4结构体、IPV6结构体
    • 3.2 框架搭建
      • 3.2.1 client
      • 3.2.2 server
    • 3.3 服务端和客户端编写
      • 3.3.1 client_udp.c
      • 3.3.2 sever_udp.c
      • 3.3.3 终端结果
  • 4.总结

摘要:这篇文章是对UDP传输协议的讲解,包括基础知识、主要函数,最后通过在linux上用C语言建立客户端和服务端实例对UDP的认识更加深刻。
关键词:UDP、sendto、recvfrom。

1.UDP简介

1.UDP(User Datagram protocol)传输协议是TCP/IP网络协议下,传输层的一中面向报文传输方式。

1.1特点

1.无连接特性:传输时不用建立连接,服务端也不用做出响应。
2. 不可靠传输:没有TCP那样复杂,发送端只管发送数据,不会管接收端的感受,真是个无情的man。
3. 快速传输:由于 UDP 没有连接建立和可靠传输机制带来的开销,它可以非常快速地发送数据。
4.面向数据报:应用层发送多少,UDP传输层就给网络层发送多少数据。

1.2 UDP协议头部格式

在这里插入图片描述

1.2.1 UDP头部

UDP头部没有TCP头部那么复杂,只占了8个字节,而TCP头部最小20各字节,根据可选项参数还能扩展到60个字节,TCP头部长度标识只占4位,但是TCP头部的每一个值表示4字节数目,所以最大长度2^4*4=60。

1.2.2 头部意义

UDP头部和TCP头部表示的意思不同,UDP头部是整个数据报的长度,因为UDP头部固定8个字节,所以不用标识多长。
更多TCP信息请查看
TCP三次握手,四次挥手,通俗易懂!!!
Linux C语言TCP协议实战

1.2.3 头部参数

源端口号:这个可有可无,数据报是无连接的,只需给数据报指定发送方就行了(sendto函数后面会介绍)。由于不需要消耗额外的资源消耗在连接、同步、拥塞控制、流量控制等等机制上,这也是UDP传输快速的原因。
目的端口号:数据报的的接收端必须指定,网络层和链路层通过路由、链路等方法,将数据迅速的发送到指定端口。
UDP长度:指定了数据报的长度,UDP头部固定长度无需指定。
校验和:确认数据是否错误。

1.3 UDP数据长度控制

下面这两张图的意思是设置了IP_MTU_DESCOVER套接字选项,发送数据端会追踪目标IP的最大传输单元(MTU)的大小,而不是SOCK_STREAM数据形式的(也就是不是TCP协议)传输协议会默认使用MTU追踪,默认选项可以在/proc/sys/net/ipv4/ip_no_pmtu_disc文件下设置,从最后一张图中默认为0,默认使用IP_PMTUDISC_WANT选项,这个选项会根据追踪到的目的端MTU的大小来控制数据报的尺寸,关闭这个选项数据超过MTU(加协议头部)UDP会进行数据分片,但是不建议关闭,这样会影响到UDP数据的发送速度,这些是IP层的知识,不多讲,我也懂的不多。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4 UDP协议建立框架

UDP协议没有一对一建立连接,只需要确定服务器的IP和端口就可以了,所以不用多线程和和多进程进行处理。
在这里插入图片描述

2. 函数介绍

2.1 sendto函数

向指定端口发送数据,在函数中,出现sockaddr结构体,发送端可以不用绑定端口,直接发送数据。

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr*dest_addr, socklen_t addrlen);
/*
sockfd: socket函数返回的文件描述符
buf:  数据
len: 数据长度
flags: 可选参数一般填0,更多选项可在man sendto中查看
dest_addr:  网络通用结构体
addrlen:  结构体长度
*/

2.2 recvform函数

阻塞等待接收数据,接收端可以不用绑定端口直接接收数据。

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sockfd: socket函数返回的文件描述符
buf:  数据
len: 数据长度
flags: 可选参数一般填0,更多选项可在man sendto中查看
src_addr:  网络通用结构体
addrlen:结构体长度

2.3 其他函数

关于端口和IP地址的主机字节序和网络字节序可以在这篇文章查找:TCP/IP传输协议,易懂!!!
实战代码中用到的其他网络相关函数可以在这篇文章中查找:Linux C语言TCP协议实战

3.实例

3.1 通用结构体、IPV4结构体、IPV6结构体

struct sockaddr {
    unsigned short sa_family;  // 地址族,用于指定地址类型,例如 AF_INET(IPv4)、AF_INET6(IPv6)等
    char sa_data[14];          // 地址数据,具体格式和长度取决于地址族的不同
};
 IPV6结构体
struct sockaddr_in6 {
   sa_family_t sin6_family;   /* 地址族,这里是 AF_INET6,表示 IPv6 地址 */
   in_port_t sin6_port;       /* 端口号,使用网络字节序 */
   uint32_t sin6_flowinfo;    /* IPv6 流信息 */
   struct in6_addr sin6_addr;  /* IPv6 地址 */
   uint32_t sin6_scope_id;    /* IPv6 范围 ID */
};
IPV4结构体
struct sockaddr_in {
    sa_family_t sin_family;   // 地址族(address family),通常是 AF_INET,表示使用 IPv4 地址
    in_port_t sin_port;       // 16 位的端口号,需要使用 `htons()` 函数转换为网络字节序
    struct in_addr sin_addr;  // 存储 IPv4 地址的结构体
    char sin_zero[8];         // 填充字节,为保持与 `struct sockaddr` 一致,一般设置为 0
};

 struct in_addr{
 uint32_t s_addr;
 }

3.2 框架搭建

3.2.1 client

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h>    //POSIX.1不需要包含,一些旧版本(BSD)需要包含
#include <sys/socket.h>    
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>
#include <strings.h>

int main(int argc, char const *argv[])
{
	int sockfd;
	//初始化网络结构体
	struct sockaddr_in XXX; 
	......
	
	//1.创建套接字
	socket()
	...
	
	//客户端绑定端口,客户端在sendto发送数据的时候,操作系统会随机配置一个端口给客户端,bind一般不用
	#if COND_COMPILLE  
		...
		bind()	
		...
	#endif

	while(1)
	{
		//2.通信
		...
		sendto()   //发送数据
		...
		recvfrom()   //阻塞接收
		...
		
	}
	//3.关闭套接字
	close(sockfd);

	return 0;
}


3.2.2 server

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>

int main(int argc, char const *argv[])
{
	int sockfd; 
	
	//初始化网络结构体
	struct sockaddr_in XXX;
	......
	
	//1.创建套接字
	socket()
	...
	//2.绑定端口,用于接收数据
	bind()
	...
	char readBuf[BUF_SIZE] = {0};
	while(1)
	{
		//3.数据处理
		...
		recvfrom()   //阻塞接收数据
		...
		sendto()     //做出响应
		...
		
	}
	//4.关闭套接字
	close(sockfd);

	return 0;
}

3.3 服务端和客户端编写

3.3.1 client_udp.c

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>
#include <strings.h>

#define BUF_SIZE 256
#define COND_COMPILE 0	
#define QUIT_FLAG "quit"

int main(int argc, char const *argv[])
{
	if(argc !=3 )
	{
		printf("input para is incorrent\n");
		exit(1);
	}
	//1.创建套接字
	int sockfd;
	//初始化网络结构体
	struct sockaddr_in serveraddr; 
	socklen_t addrlen = sizeof(serveraddr);
	bzero(&serveraddr, addrlen);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	
	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(1);
	}
//客户端绑定端口,一般不用
	#if COND_COMPILLE  
		struct sockaddr_in clientaddr;
		clientaddr.sin_family = AF_INET;
		clientaddr.sin_addr.s_addr = inet_addr(argv[3]); 
		clientaddr.sin_port = htons(atoi(argv[4])); 
		if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) < 0)
		{
			perror("fail to bind");
			exit(1);
		}
	#endif


	char readBuf[BUF_SIZE] = {0};
	char writeBuf[BUF_SIZE] = {0};
	while(1)
	{
		//3.通信
		fgets(writeBuf, BUF_SIZE-1, stdin);
		size_t len = strlen(writeBuf);
		writeBuf[len-1] = '\0';
		if(sendto(sockfd, writeBuf, (len-1), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr))==-1)
		{
			perror("sendto");
			exit(1);
		}
		printf("send complete\n");
		if(!strncasecmp(writeBuf, QUIT_FLAG, strlen(QUIT_FLAG)))
                {
                        printf("the client exit\n");
                        break;
                }
		if(recvfrom(sockfd, readBuf, BUF_SIZE, 0,(struct sockaddr*)&serveraddr, &addrlen)==-1)
		{
			perror("recvfrom");
			exit(1);
		}
		printf("%s\n",readBuf);
		bzero(readBuf, BUF_SIZE);
		bzero(writeBuf, BUF_SIZE);

	}
	//关闭套接字
	close(sockfd);

	return 0;
}

3.3.2 sever_udp.c

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>

#define HOST_PORT 5001
#define BUF_SIZE 256
#define QUIT_FLAG "QUIT"
#define RESP ",RESP"
int main(int argc, char const *argv[])
{
	int sockfd; 
	//初始化网络结构体
	struct sockaddr_in serveraddr,cin;
	socklen_t addrlen = sizeof(cin);
	bzero(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = INADDR_ANY;
	serveraddr.sin_port = htons(HOST_PORT);
	//1.创建套接字
	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(1);
	}
	//2.绑定端口,用于接收数据
	if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
	{
		perror("fail to bind");
		exit(1);
	}
	char readBuf[BUF_SIZE] = {0};
	while(1)
	{
		//3.数据处理
		if(recvfrom(sockfd, readBuf, BUF_SIZE, 0, (struct sockaddr*)&cin, &addrlen)==-1)
		{
			perror("recvform");
			exit(0);
		}
		printf("the client port: %d, IP: %s\n", 
				ntohs(cin.sin_port), inet_ntoa(cin.sin_addr));
		printf("readBuf: %s\n", readBuf);
		if(!strncasecmp(readBuf, QUIT_FLAG, strlen(QUIT_FLAG)))
                {
                        printf("the client exit\n");
                        break;
                }
		strncat(readBuf, RESP, strlen(RESP));
		if(sendto(sockfd, readBuf, BUF_SIZE, 0, (struct sockaddr*)&cin, addrlen)==-1)
		{
			perror("sendto");
			exit(1);
		}
		printf("send complete\n");
	}

	close(sockfd);

	return 0;
}


3.3.3 终端结果

在这里插入图片描述

4.总结

这篇文章着重在UDP实操上,概念上涉及的不是很多。
文章有错误请指出。

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

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

相关文章

算法的学习笔记—(牛客JZ50)

&#x1f600;前言 在处理字符串时&#xff0c;寻找第一个只出现一次的字符是一项常见的任务。本文将探讨几种有效的解法&#xff0c;包括使用 HashMap 和位集&#xff08;BitSet&#xff09;。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f970;第一个只出现…

软件分享丨豆包电脑端 AI 助手

豆包电脑端 AI 助手是由字节跳动推出&#xff0c;旨在为用户提供高效便捷的工作和学习体验。它能在工作、学习等场景中发挥重要作用&#xff0c;为用户提供智能辅助&#xff0c;下面简单介绍它的特点&#xff1a; 高效搜索&#xff1a;像优化后的百度&#xff0c;直接提问就能…

【本科毕业设计】基于单片机的智能家居防火防盗报警系统

基于单片机的智能家居防火防盗报警系统 相关资料链接下载摘要Abstract第1章 绪论1.1课题的背景1.2 研究的目的和意义 第2章 系统总体方案设计2.1 设计要求2.2 方案选择和论证2.2.1 单片机的选择2.2.2 显示方案的选择 第3章 系统硬件设计3.1 整体方案设计3.1.1 系统概述3.1.2 系…

C#通过异或(^)运算符制作二进制加密(C#实现加密)

快速了解异或运算符&#xff1a; 异或运算符在C#中用 “^” 来表示 口诀&#xff1a;相同取0&#xff0c;相异取1 简单加密解密winform示例&#xff1a; /// <summary>/// 异或运算符加密实现/// </summary>/// <param name"p_int_Num">初始值<…

生成式 AI 与向量搜索如何扩大零售运营:巨大潜力尚待挖掘

在竞争日益激烈的零售领域&#xff0c;行业领导者始终在探索革新客户体验和优化运营的新途径&#xff0c;而生成式 AI 和向量搜索在这方面将大有可为。从个性化营销到高效库存管理&#xff0c;二者在零售领域的诸多应用场景中都展现出变革性潜力&#xff0c;已成为保持行业领先…

云电脑的真实使用体验

最近这几年&#xff0c;关于云电脑的宣传越来越多。 小枣君之前曾经给大家介绍过云电脑&#xff08;链接&#xff09;。简单来说&#xff0c;它属于云计算的一个应用。通过在云端虚拟出一些虚拟电脑&#xff0c;然后让用户可以远程使用&#xff08;仍然需要借助本地电脑&#x…

使用爬虫爬取Python中文开发者社区基础教程的数据

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

CANoe_C#调用CDD和CAPL调用CDD方法对比

引言 在汽车电子系统的开发和测试中,CANoe作为一款强大的网络仿真工具,广泛应用于各种通信协议的模拟和验证。为了实现复杂的测试场景,开发者可以使用不同的编程语言和方法来调用CANoe的功能。其中,C#和CAPL(CANoe Programming Language)是两种常用的编程方式。本文将对…

Golang | Leetcode Golang题解之第498题对角线遍历

题目&#xff1a; 题解&#xff1a; func findDiagonalOrder(mat [][]int) []int {m, n : len(mat), len(mat[0])ans : make([]int, 0, m*n)for i : 0; i < mn-1; i {if i%2 1 {x : max(i-n1, 0)y : min(i, n-1)for x < m && y > 0 {ans append(ans, mat[x…

学习笔记——交换——STP(生成树)工作原理

三、工作原理 STP的基本原理是在一个有二层环路的网络中&#xff0c;交换机通过运行STP&#xff0c;自动生成一个没有环路的网络拓扑。这个无环网络拓扑也叫做STP树(STP Tree)&#xff0c;树节点为某些交换机&#xff0c;树枝为某些链路。当网络拓扑发生变化时&#xff0c;STP…

《汇编语言》第15章——实验15安装新的 int 9 中断例程

安装新的 int9 中断例程 安装一个新的 int 9 中断例程&#xff0c;功能:在 DOS 下&#xff0c;按下A键后&#xff0c;除非不再松开如果松开&#xff0c;就显示满屏幕的A&#xff0c;其他的键照常处理。 提示&#xff0c;按下一个键时产生的扫描码称为通码&#xff0c;松开一个…

云计算作业一hadoop:问题解决备忘

教程地址&#xff1a;https://blog.csdn.net/qq_53877854/article/details/142412784 修改网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33在root用户下编辑 静态ip地址配置后查看ip与配置不符 注意&#xff1a;确保在这之前已经在VMware的编辑>虚拟网络编…

OpenCV中的图像通道合并

在计算机视觉和图像处理领域&#xff0c;OpenCV是一个强大的工具库&#xff0c;它提供了从基本操作到复杂算法的广泛功能。今天&#xff0c;我们将通过一个简单的示例来探索OpenCV中的图像通道处理&#xff0c;特别是如何操作和理解BGR与RGB颜色空间的差异。 Lena图像&#xf…

WSL迁移到D盘

迁移WSL 下的 ubuntu 到D盘 使用工具LxRunOffline 如果出现下面的错误 使用其他版本 [ERROR] Couldnt set the case sensitive attribute of the directory "\\?\C:\Users\admin\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04LTS_79rhkp1fndgsc\LocalState…

TwinCAT3运动路径生成与执行

右键MAIN&#xff0c;点击Add添加Action&#xff0c;添加如下俩个名称的Action 在MAIN中添加如下代码&#xff1a; 在俩个Action中分别添加如下代码&#xff1a; 在MAIN程序中输入如下代码&#xff1a; 选择激活配置 弹出的对话框选择OK 弹出的对话框选择确定&…

Web前端高级工程师培训:使用 Node.js 构建一个 Web 服务端程序(3)

11、HTTP 协议 11-1、协议的定义 HTTP 是一种能够获取如 HTML 这样的网络资源的 protocol(通讯协议)。它是在 Web 上进行数据交换的基础&#xff0c;是一种 client-server 协议&#xff0c;也就是说&#xff0c;请求通常是由像浏览器这样的接受方发起的。一个完整的Web文档通…

【解决】使用Hypermark将Markdown文件转化为HTML文件

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 文章目录 一、文件准备&#xff08;一&#xff09;HTML模板文件&#xff08;二&#xff09;MD文件夹和储存文件夹 二、文件转…

【C++贪心】1536. 排布二进制网格的最少交换次数|1880

本文涉及知识点 C贪心 决策包容性 LeetCode1536. 排布二进制网格的最少交换次数 给你一个 n x n 的二进制网格 grid&#xff0c;每一次操作中&#xff0c;你可以选择网格的 相邻两行 进行交换。 一个符合要求的网格需要满足主对角线以上的格子全部都是 0 。 请你返回使网格满…

QUIC 启动!

掘金地址&#xff1a;https://juejin.cn/post/7428200842229006377 引言 QUIC是什么&#xff1f;明明你每天都在用&#xff0c;明明每天都在timing&#xff0c;难道你不知道吗&#xff1f;啊&#xff1f;不会吧&#xff0c;不会吧。 那就让本文来让你全方位的了解这个协议。 …

word表格跨页后自动生成的顶部横线【去除方法】

Hello World! Its been a long time. 这一年重心放在了科研、做事、追寻新的经历上&#xff0c;事有正事、琐事、幸事、哀事&#xff0c;内心与认知成长了一些&#xff0c;思想成熟了几分&#xff0c;技艺也有若干收获。不管怎样&#xff0c;来打个卡吧&#xff0c;纪念一下&…