UDP套接字编程详解

news2024/11/28 22:50:48

    UDP 是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议。

    UDP协议与TCP协议一样用于处理数据包。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但即使在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。

    当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。在数据传输时间很短,以至于此前的连接过程成为整个流量主体的情况下,UDP也是一个好的选择。 [3] 

UDP的一般实现流程图:

UDP服务端 

初始化套接字库

//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;

wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
//判断执行结果
if (err != 0) {
	return err;
}
//判断初始化结果是否与选择的版本库一致
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
	//清理套接字库
	WSACleanup();
	return -1;
}

    在开之前需要先初始化套接字库,告诉操作系统我们选择的套接字库的版本号。这里我们选择的是 1.1版本的套接字库 。

    初始化完成后需要判断程序的执行结果,一个是函数本身的运行情况,判断返回结果是否为0;另一个是我们初始化的套接字库的版本号是否和我们指定的相一致。

创建Socket 

//创建Socket
//指定协议族为IPV4,socket通信类型为支持UDP连接
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);

//准本一个处理网络通信的地址
SOCKADDR_IN addrSrv;
//指定要绑定的IP地址为本机上的任意IP地址
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//协议族要与创建给的Socket一致
addrSrv.sin_family = AF_INET;
//指明绑定的端口号
addrSrv.sin_port = htons(6001);

创建一个服务器的Socket, 并指明协议族为IPV4,通信类型为支持UDP协议。

还需要准本一个用于处理网络通信的结构体,并指绑定的IP地址、端口号和协议族(协议族要与创建Socket时的协议族保持一致)。

绑定到本机

//绑定套接字
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

bind函数会将端口和IP地址绑定到我们的socket描述符上,返回0时表示绑定成功 。

等待发送与接收数据

//保存发送数据的地址
SOCKADDR_IN addrCli;

int len = sizeof(SOCKADDR_IN);
char recvBuf[100];
char sendBuf[100];
while (true) {
	//接收数据
	recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
	//将接收数据打印下来
	std::cout << recvBuf << std::endl;
	sprintf_s(sendBuf, 100, "ACK%s", recvBuf);
    //发送数据
	sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
}

UDP没有TCP的监听和请求步骤,而是直接去接收客户端发来的信息,并将客户端的socket信息保存下来,在发送数据的时候使用。 

recvfrom函数用于从(已连接)套接口上接收数据,并捕获数据发送源的地址。

recvfrom函数原型为

int recvfrom(
	SOCKET   s,
	char* buf,
	int      len,
	int      flags,
	sockaddr * from,
	int* fromlen
);
  •  s:标识绑定套接字的描述符
  • buf:传入数据的缓冲区
  • len:指向缓冲区的长度
  • flags:可一般默认为0
  • from:保存数据的发送地址
  • fromlen:发送数据的源地址长度指针

sendto是一个向指定目的地发送数据的函数,将recvfrom捕获的客户端地址传入sendto里,就可以向该客户端发送数据 

sendto函数原型

int sendto(
    int s, 
    const void * msg, 
    int len, 
    unsigned int flags,
    const struct sockaddr * to, 
    int tolen
);

s:标识绑定的套接字描述符

msg:发送数据的缓冲区

len:指向发送缓冲区的长度

flags:可一般默认为0

to:向指定地址发送数据的地址长度

完整代码

#include<iostream>
#include<WinSock2.h>

int main() {


	//初始化套接字库
	WORD wVersion;
	WSADATA wsaData;
	int err;

	wVersion = MAKEWORD(1, 1);
	err = WSAStartup(wVersion, &wsaData);
	//判断执行结果
	if (err != 0) {
		return err;
	}
	//判断初始化结果是否与选择的版本库一致
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		//清理套接字库
		WSACleanup();
		return -1;
	}

	//创建Socket
	//指定协议族为IPV4,socket通信类型为支持UDP连接
	SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
	//准本一个处理网络通信的地址
	SOCKADDR_IN addrSrv;
	//指定要绑定的IP地址为本机上的任意IP地址
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	//协议族要与创建给的Socket一致
	addrSrv.sin_family = AF_INET;
	//指明绑定的端口号
	addrSrv.sin_port = htons(6001);
	//绑定套接字
	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	//保存发送数据的地址
	SOCKADDR_IN addrCli;

	int len = sizeof(SOCKADDR_IN);

	char recvBuf[100];
	char sendBuf[100];
	while (true) {
		//接收数据
		recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
		//将接收数据打印下来
		std::cout << recvBuf << std::endl;
		sprintf_s(sendBuf, 100, "ACK%s", recvBuf);
        //发送数据
		sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
	}


	//关闭套接字
	closesocket(sockSrv);
	//清理套接字库
	WSACleanup();

	system("pause");
	return 0;
}

UDP客户端 

初始化套接字库

//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;

wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
//判断执行结果
if (err != 0) {
	return err;
}
//判断初始化结果是否与选择的版本库一致
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
	//清理套接字库
	WSACleanup();
	return -1;
}

客户端的实现也需要先初始化套接字库,注意库的版本和服务器的版本相一致。

创建套接字 

//创建Socket
//指定协议族为IPV4,socket通信类型为支持UDP连接
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);

//准本一个处理网络通信的地址
SOCKADDR_IN addrSrv;
//指定要连接的IP地址
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//协议族要与创建给的Socket一致
addrSrv.sin_family = AF_INET;
//指明绑定的端口号
addrSrv.sin_port = htons(6001);

创建一个套接字,套接字的协议族与通信类型要和服务器的保持一致。之后准备一个处理网络通信的地址,也就是需要连接服务器的地址,并指明连接的IP地址和端口号。 

发送与接收数据

//保存发送数据的地址
SOCKADDR_IN addrCli;

int len = sizeof(SOCKADDR_IN);
char recvBuf[100];
char sendBuf[100] = "UDP Cli come to connect";
//发送数据
sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, len);
//接收数据,这里也可也不保存地址
recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
//将发送数据打印下来
std::cout << recvBuf << std::endl;

其实这里我们可以不写保存地址的SOCKADDR_IN结构体,因为我们已经知道要发送到的地址了

完整代码 

#include<iostream>
#include<WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

int main()
{
	//初始化套接字库
	WORD wVersion;
	WSADATA wsaData;
	int err;

	wVersion = MAKEWORD(1, 1);
	err = WSAStartup(wVersion, &wsaData);
	//判断执行结果
	if (err != 0) {
		return err;
	}
	//判断初始化结果是否与选择的版本库一致
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		//清理套接字库
		WSACleanup();
		return -1;
	}

	//创建Socket
	//指定协议族为IPV4,socket通信类型为支持UDP连接
	SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
	//准本一个处理网络通信的地址
	SOCKADDR_IN addrSrv;
	//指定要连接的IP地址
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	//协议族要与创建给的Socket一致
	addrSrv.sin_family = AF_INET;
	//指明绑定的端口号
	addrSrv.sin_port = htons(6001);

	//保存发送数据的地址
	SOCKADDR_IN addrCli;
	int len = sizeof(SOCKADDR_IN);
	char recvBuf[100];
	char sendBuf[100] = "UDP Cli come to connect";
	//发送数据
	sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, len);
	//接收数据
	recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
	//将发送数据打印下来
	std::cout << recvBuf << std::endl;

	system("pause");
	return 0;
}

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

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

相关文章

【状语从句练习题】综合训练

1. 改正错误 1.第二个 either 改为or 2.后面的 he plays 去掉&#xff0c;不必要的重复 3.but 去掉 4.cold 后 and 5.I went out. 加 个 I. 6.第一个 will 去掉&#xff0c;get 变为 ges 7.so 去掉 8.去掉第二个 either 9.去掉后面的 I need 10.Although/but 去掉一个 11.后…

【JavaSE】类和对象——上

文章目录1. 类的定义1.1 什么是类1.2 如何定义类2. 类的实例化3. this关键字3.1 this访问成员变量和成员方法3.2 构造方法及this()我们给类初始化的方法有&#xff1a;3.2.1 就地初始化3.2.2 使用 set 方法赋值3.2.3 使用构造方法4. 如何便捷的打印对象中属性1. 类的定义 1.1 什…

【Pytorch Lighting】第 1 章:PyTorch Lightning adventure

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

数据中心通识

文章目录一 数据中心定义特点二 机房定义内部物品物理环境三 服务器定义外型分类与PC相比的优点超线程技术固态硬盘和机械硬盘四 网络定义OSI模型局域网的拓扑结构TCP/IP协议常见端口号IP地址介绍、格式、分类、子网掩码DNS三层交换机五 存储格式化缓存RAID技术存储体系架构IP-…

二叉树的前中后序遍历(递归与迭代)

作者&#xff1a;~小明学编程 文章专栏&#xff1a;Java数据结构 格言&#xff1a;目之所及皆为回忆&#xff0c;心之所想皆为过往 目录 简介 前序遍历 递归法 迭代法 中序遍历 递归法 迭代法 后序遍历 递归法 迭代法 简介 前面学习二叉树的时候&#xff0c;已经学过…

渗透测试 | 域名信息收集

0x00 前言 信息收集可以说是在渗透测试中最重要的一部分&#xff0c;上文对 IP 信息收集做了一个简要的叙述&#xff0c;认识了 CDN 技术和网络空间搜索引擎。但是很多网站的主站因为访问流量过大的原因通常会使用 CDN 技术&#xff0c;同时也可以有效防止 DDOS 攻击。在域名信…

前端面经 强缓存与协商缓存

前端面经 强缓存与协商缓存 图片多来自第三方平台 文章目录前端面经 强缓存与协商缓存适用场合浏览器缓存的过程缓存规则强缓存&#xff08;本地缓存&#xff09;协商缓存缓存分为两种&#xff1a;强缓存和协商缓存&#xff0c;根据响应的header内容来决定 获取资源形式状态码…

【C++】二叉搜索树

前言 hi~大家好呀&#xff0c;欢迎点进我的C学习笔记~ 我的前一篇C笔记链接~ 【C】多态_柒海啦的博客-CSDN博客 本篇需要用到的基础二叉树C语言实现链接~ 用c语言实现一个简单的链表二叉树_柒海啦的博客-CSDN博客_c语言建立二叉树链表 我们知道&#xff0c;查找一个数据会有很多…

数据库基本操作

目录 数据库操作 创建数据库 查看数据库 选择数据库 删除数据库 注释 数据表操作 创建数据表 查看数据表 查看数据表 查看数据表的相关信息 修改数据表 修改数据表名称 修改表选项 查看表结构 查看数据表的字段信息 查看数据表的创建信息 查看数据表结构 修…

linux进程间通信之共享内存

目录 一&#xff0c;共享内存原理 二&#xff0c;创建共享内存 1&#xff0c;shmget创建共享内存 2&#xff0c;shmat挂接共享内存 3&#xff0c;shmdt取消挂接共享内存 4&#xff0c;shmctl删除共享内存 三&#xff0c;代码使用 1,com.hpp 2&#xff0c;ipc_client.c…

Allegro基本规则设置指导书之Physical Region

Allegro基本规则设置指导书之Physical Region 下面介绍基本规则设置指导书之Physical Region 空白的地方创建一个Region 给新建的Region匹配一个规则,所有区域里面的Physical相关的都按照Region的规则来 当部分网络想按照本身的规则来匹配,可以创建region-Class 然后匹配…

目标检测算法——医学图像开源数据集汇总(附下载链接)

关注”PandaCVer“公众号 深度学习Tricks&#xff0c;第一时间送达 目录 1.血细胞图像数据 2.眼病深度学习数据集 3.皮肤病数据集 4.膝关节 X 射线图像数据集 小海带整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff01;&#xff01; >>>一起交流…

VisualSVN 是 Visual Studio 的专业级 Subversion 集成插件

用于 Visual Studio 的 VisualSVN 专业且无缝的 Subversion 集成。 专业级 Subversion 集成 VisualSVN 是 Visual Studio 的专业级 Subversion 集成插件。 VisualSVN 的主要优点是&#xff1a; 无与伦比的可靠性&#xff1a; Visual Studio 永远不会因为 VisualSVN 而崩溃或挂…

保护鲸鱼动物网页设计作业 静态HTML宠物主题网页作业 DW鲸鱼网站模板下载 大学生简单动物网页作品代码 个人网页制作 学生个人网页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

2022阿里云栖大会,顶尖科技趋势峰会和全链路元宇宙体验

2022年的11月3日-5日是阿里巴巴云栖大会的日子&#xff0c;地点在云栖小镇&#xff0c;本人有幸报名参加了5日那场&#xff0c;因为5日是周六。秉着打工人工作日需要搬砖&#xff0c;因为“公司离不开我”&#xff0c;哈哈哈&#xff0c;实际上是每天满满的工作量。所以只能选择…

一文彻底搞懂协程(coroutine)是什么,值得收藏

什么是协程 我们可以简单的认为&#xff1a;协程就是用户态的线程&#xff0c;但是上下文切换的时机是靠调用方&#xff08;写代码的开发人员&#xff09;自身去控制的。 同时&#xff0c;协程和用户态线程非常接近&#xff0c;用户态线程之间的切换不需要陷入内核&#xff0…

NYIST(计科ACMTC)第三次招新赛题解

A题 原文, 原比赛B题 牛客练习赛104【出题人题解】 - 知乎 直接输出 输入的数 就可以了 B题 C题 找到分别处理"无留陀的化身"坐标轴的x轴和y轴, 组合成无留陀的坐标, 再遍历求纳西妲的坐标, 相减即可 /* ⣿⣿⣿⣿⣿⣿⡷⣯⢿⣿⣷⣻⢯⣿⡽⣻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿…

ROS小工具学习与使用

ROS小工具学习与使用 rqt的使用 rqt_bag工具 rqt_bag <your bagfile> #使用rqt_bag查看你的rosbag例如&#xff1a;可以查看第一帧GPS的rawdata信息&#xff0c;如下图&#xff1a; 参考文献&#xff1a; 1、http://wiki.ros.org/rqt_bag 2、rosbag与rqt_bag的常用 rq…

Nacos学习笔记

视频学习指路&#xff1a; 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 Nacos nacos注册中心的搭建 1.下载nacos的安装包&#xff0c;github地址&#xff1a;https://github.com/alibaba/nacos&…

Tkinter保姆级教程(上)

目录 什么是GUI Tkinter用法详解 第一个Tkinter程序 常用控件和属性 主窗口 Label标签控件 Button按钮控件 Entry输入控件 基本属性 Text 文本控件 列表框(ListBox)和组合框(Combobox) 单选框(Radiobutton)和多选框按钮(Checkbutton) 什么是GUI 图形用户界面&#x…