UDP网络通信(发送端+接收端)实例 —— Python

简介
在网络通信编程中,用的最多的就是UDP和TCP通信了,原理这里就不分析了,网上介绍也很多,这里简单列举一下各自的优缺点和使用场景

通信方式优点缺点适用场景
UDP及时性好,快速视网络情况,存在丢包

与嵌入式设备通信,实时控制

场景

TCP丢包会自动重发,理论上不用担心丢包问题延时相对大一些

通信可靠性场景,比如IoT设备

控制,状态同步

一、socket
我们要进行网络通信,那么就要用到socket,socket即网络套接字,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。
在 Python 中,使用socket 模块的函数 socket 就可以创建一个socket对象,socket()函数的参数分别有family, type, proto。

1.其中family参数是指协议域,又称为协议族(family),常用的协议族有,AF_INET、AF_INET6、...等等,AF_INET指ipv4,AF_INET6即为ipv6;
2.然后是type,type指定socket类型,有SOCK_STREAM(流式套接字,主要用于 TCP 协议)和SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)等等;
3.proto就是指定的协议,常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议,但是type和proto不可以随意组合,当proto参数为0或者不填时,会自动选择type类型对应的默认协议。

二、UDP发送数据
首先我们要导入socket包

import socket

创建一个udp套接字,ipv4协议,使用SOCK_DGRAM参数,不填proto,就会默认自动选择udp协议;

# 1、创建一个UDP套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

然后我们把要接收数据的那一端的ip地址和端口号放在一个元组里准备好

​​​​# 2. 准备接收方的地址和端口,'127.0.0.1:12341'表示目的ip地址,12341表示目的端口号
dest_addr = ('127.0.0.1', 12341)  # 注意这是一个元组,其中ip地址是字符串,端口号是数字​

  准备好后就可以使用sendto函数进行发送了,要注意,需要对字符串进行编码才可以发送

# 3. 发送数据到指定的ip和端口
udp_socket.sendto("Hello,I am a UDP socket.".encode('utf-8'), dest_addr)

发送完就可以关闭套接字了

# 4. 关闭套接字
udp_socket.close()

例程一:UDP server端,UDP数据接收

#!/usr/bin/python3
# -*- coding: utf-8 -*-"""
udp通信例程:udp server端,修改udp_addr元组里面的ip地址,即可实现与目标机器的通信,
此处以单机通信示例,ip为127.0.0.1,实际多机通信,此处应设置为目标客户端ip地址
"""from time import sleep
import socketdef main():# udp 通信地址,IP+端口号udp_addr = ('127.0.0.1', 9999)udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定端口udp_socket.bind(udp_addr)# 等待接收对方发送的数据while True:recv_data = udp_socket.recvfrom(1024)  # 1024表示本次接收的最大字节数# 打印接收到的数据print("[From %s:%d]:%s" % (recv_data[1][0], recv_data[1][1], recv_data[0].decode("utf-8")))if __name__ == '__main__':print("当前版本: ", __version__)print("udp server ")main()

代码解析 

1.socket函数中第二个参数就是通信类型,此处SOCK_DGRAM 就是指定使用UDP通信
2.服务端需要使用bind函数绑定端口,客户端不需要,因为客户端发送的时候已经带了端口参数

例程二:UDP client端,UDP数据发送

#!/usr/bin/python3
# -*- coding: utf-8 -*-"""
udp通信例程:udp client端,修改udp_addr元组里面的ip地址,即可实现与目标机器的通信,
此处以单机通信示例,ip为127.0.0.1,实际多机通信,此处应设置为目标服务端ip地址
"""from time import sleep
import socketdef main():# udp 通信地址,IP+端口号udp_addr = ('127.0.0.1', 9999)udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 发送数据到指定的ip和端口,每隔1s发送一次,发送10次for i in range(10):udp_socket.sendto(("Hello,I am a UDP socket for: " + str(i)) .encode('utf-8'), udp_addr)print("send %d message" % i)sleep(1)# 5. 关闭套接字udp_socket.close()if __name__ == '__main__':print("当前版本: ", __version__)print("udp client ")main()


例程三:多线程实现UDP数据收发

#!/usr/bin/python3
# -*- coding: utf-8 -*-"""
python多线程通信
"""from time import sleep
import socket
import threading# 定义全局变量
t1_count = 0
t2_count = 0def udp_received_hundle(s):global t1_countprint("this is thread 1 running")while True:t1_count += 1print("thread 1 第 %s 次运行" % t1_count)recv_data = s.recvfrom(1024)  # 1024表示本次接收的最大字节数# 打印接收到的数据print("[From %s:%d]:%s" % (recv_data[1][0], recv_data[1][1], recv_data[0].decode("utf-8")))def udp_send_hundle(s):global t2_countprint("this is thread 2 running")while True:t2_count += 1print("")print("thread 2 第 %s 次运行" % t2_count)s.sendto(("Hello,I am a UDP socket for: " + str(t2_count)).encode('utf-8'), udp_addr)print("send %d message" % t2_count)print("")sleep(1)if __name__ == '__main__':print("当前版本: ", __version__)# 初始化# udp 通信地址,IP+端口号udp_addr = ('127.0.0.1', 9999)udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定端口:udp_socket.bind(udp_addr)# 定义线程thread_list = []t1 = threading.Thread(target=udp_received_hundle, args=(udp_socket, ))thread_list.append(t1)t2 = threading.Thread(target=udp_send_hundle, args=(udp_socket, ))thread_list.append(t2)for t in thread_list:t.setDaemon(True)t.start()for t in thread_list:t.join()print("exit all task.")print('all process end.')

代码解析
这里用到了多线程,虽然python中的多线程是假的多线程,实际上是一个线程分时复用,这里我们不深究,如果平常用到也就几个小任务跑一跑,抄我这个作业就ok。
多线程实际上是从t.join()后才开始正式运行的,这里一定要注意,不能漏了这个函数。
udp的收发与上面的例程几乎是一样的。
代码运行效果如下

当前版本:  1.0.0
this is thread 1 running
thread 1 第 1 次运行
this is thread 2 runningthread 2 第 1 次运行
send 1 message[From 127.0.0.1:9999]:Hello,I am a UDP socket for: 1
thread 1 第 2 次运行thread 2 第 2 次运行
send 2 message[From 127.0.0.1:9999]:Hello,I am a UDP socket for: 2
thread 1 第 3 次运行thread 2 第 3 次运行
send 3 message[From 127.0.0.1:9999]:Hello,I am a UDP socket for: 3
thread 1 第 4 次运行thread 2 第 4 次运行
send 4 message[From 127.0.0.1:9999]:Hello,I am a UDP socket for: 4
thread 1 第 5 次运行thread 2 第 5 次运行
send 5 message

 使用网络调试助手,测试程序
注意,如果不是在本机windows系统上运行python程序,在Ubuntu虚拟机或者其他局域网内的机器上运行,要把windows的防火墙关了!!!
然后我们让其每隔一秒发送一次,发送10次,发送成功

完整代码:

​#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Williamimport socket,timedef main():# 1、创建一个UDP套接字udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 2. 准备接收方的地址和端口,'192.168.0.107'表示目的ip地址,8080表示目的端口号dest_addr = ('192.168.8.226', 12341)  # 注意这是一个元组,其中ip地址是字符串,端口号是数字# 3. 发送数据到指定的ip和端口for i in range(10):udp_socket.sendto("Hello,I am a UDP socket.".encode('utf-8'), dest_addr)time.sleep(1)# 4. 关闭套接字udp_socket.close()if __name__ == '__main__':main()​

三、UDP接收数据
在之前发送数据的时候,我们可以看到,其端口号是一直在变得,那么我们要接收数据,就需要知道其端口号是什么,所以我们要先固定一个端口号,使用bind函数

# 2. 绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号
local_addr = ('', 12344)  # ip地址和端口号,ip一般不用写,表示本机的任何一个ip
udp_socket.bind(local_addr)

接收数据使用recvfrom函数,其参数为接收的最大数据长度

# 3. 等待接收对方发送的数据
recv_data = udp_socket.recvfrom(1024)  # 1024表示本次接收的最大字节数

接收完后将其打印出来:

# 4、打印接收到的数据
print(recv_data)

运行,通过网络调试助手发送数据

 可以看到,打印出来的信息是一个元组,第一项接收到的字符串,第二项也是一个元组,包含对方的IP地址和端口号
完整代码:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Williamimport socket,timedef main():# 1、创建一个UDP套接字udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 2. 绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号local_addr = ('', 12344)  # ip地址和端口号,ip一般不用写,表示本机的任何一个ipudp_socket.bind(local_addr)# 3. 等待接收对方发送的数据recv_data = udp_socket.recvfrom(1024)  # 1024表示本次接收的最大字节数# 4、打印接收到的数据print(recv_data)# 5. 关闭套接字udp_socket.close()if __name__ == '__main__':main()


四、UDP收发数据
实现这样一个功能,通过UDP发送10次消息,然后等待接收,将接收的数据及其来源打印出来:

完成代码:

​
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Williamimport socket,timedef main():# 1、创建一个UDP套接字udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 2. 绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号udp_socket.bind(('', 12344))# 3. 发送数据到指定的ip和端口,每隔1s发送一次,发送10次for i in range(10):udp_socket.sendto("Hello,I am a UDP socket.".encode('utf-8'), ('192.168.8.226', 12341))time.sleep(1)# 4. 等待接收对方发送的数据while(True):recv_data = udp_socket.recvfrom(1024)# 打印接收到的数据print("[From %s:%d]:%s"%(recv_data[1][0],recv_data[1][1],recv_data[0].decode("utf-8")))# 5. 关闭套接字udp_socket.close()if __name__ == '__main__':main()​


五、同时收发数据
现在实现这样一个功能,即运行程序,然后在控制台输入字符串发送出去,同时,还可以接收数据,我使用多线程来实现这个程序,不过要实现方便接收,我们在程序的开始,将IP地址和端口号打印出来,实现效果如下:

实现代码:

​
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Williamimport socket,time,threadingdef recv_thread(socket):# 等待接收对方发送的数据while(True):try:recv_data = socket.recvfrom(1024)# 打印接收到的数据print("[From %s:%d]:%s"%(recv_data[1][0],recv_data[1][1],recv_data[0].decode("utf-8")))except Exception:breakdef main():# 1、创建一个UDP套接字udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 2、绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号udp_socket.bind(('', 12344))# 3、打印本机ip地址和端口号print("local ipaddr and port->",socket.gethostbyname(socket.gethostname())+":12344")# 4、创建一个线程,用来接收数据t = threading.Thread(target=recv_thread, args=(udp_socket,))t.start()# 5、等待输入数据,然后发送出去,直到输入的数据为'quit'while(True):print("please input a string.input 'quit' to quit.")send_data = input()if send_data == "quit":breakelse:udp_socket.sendto(send_data.encode('utf-8'), ("192.168.8.226",12341))# 6、关闭套接字udp_socket.close()def main1():# 1、创建一个UDP套接字udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 2. 准备接收方的地址和端口,'192.168.0.107'表示目的ip地址,8080表示目的端口号dest_addr = ('192.168.8.226', 12341)  # 注意这是一个元组,其中ip地址是字符串,端口号是数字# 3. 发送数据到指定的ip和端口for i in range(1):udp_socket.sendto("Hello,I am a UDP socket.".encode('utf-8'), dest_addr)time.sleep(1)# 4. 等待接收对方发送的数据recv_data = udp_socket.recvfrom(1024)  # 1024表示本次接收的最大字节数# 5、打印接收到的数据print(recv_data)# 4. 关闭套接字udp_socket.close()if __name__ == '__main__':main()​

结语
这里只是UDP的简单使用,给大家一个示例参考,在实际应用过程中,涉及到复杂数据通信,还需要使用通信协议,协议收发,解包等函数,另外数据缓存也很关键,尤其是大数据量的情况下,通常会用到队列相关知识,这一部分就留给大家自行研究吧,如果这篇文章对你有用,不妨点赞关注,你的支持是我最大的动力。

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

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

相关文章

【JavaEE】网络编程——TCP

🤡🤡🤡个人主页🤡🤡🤡 🤡🤡🤡JavaEE专栏🤡🤡🤡 文章目录 前言1.网络编程套接字1.1流式套接字(TCP)1.1.1特点1.1.2编码1.1.2.1ServerSo…

华为USG6000V防火墙v1

目录 一、实验拓扑图 二、要求 三、IP地址规划 四、实验配置 1🤣防火墙FW1web服务配置 2.网络配置 要求1:DMZ区内的服务器,办公区仅能在办公时间内(9:00-18:00)可以访问,生产区的设备全天可以访问 要求2:生产区不…

电影《头脑特工队2》观后感

上周看了电影《头脑特工队2》,整体是非常不错的,一个大脑中,想象的世界。 (1)人格-多元政体理论 记得前几年是看过《头脑特工队1》的,当时电影是非常出名的,当时有很多研究理论,都…

【练习】分治--归并排序

🎥 个人主页:Dikz12🔥个人专栏:算法(Java)📕格言:吾愚多不敏,而愿加学欢迎大家👍点赞✍评论⭐收藏 目录 归并排序 代码实现 交易逆序对的总数 题目描述 ​编辑 题解 代码实…

JAVA从入门到精通之入门初阶(二)

1. 自动类型转换 自动类型转换:类型范围小的变量可以赋值给类型范围大的变量 byte->int public class java_7_10 {public static void main(String[] args) {//自动类型转换//类型范围小的变量可以赋值给类型范围大的变量 byte->intbyte a 12;int b a;//自动…

C语言 ——— 输入两个正整数,求出最小公倍数

目录 何为最小公倍数 题目要求 代码实现 方法一:暴力求解法(不推荐) 方法二:递乘试摸法(推荐) 何为最小公倍数 最小公倍数是指两个或者多个正整数(除了0以外)的最小的公共倍数…

【LeetCode】205. 同构字符串

认真地分类讨论,评判复杂度,再决定是否要写代码执行。整套流程干净利落,不存在主观臆想,也不会有对事实结果计算的巨大偏差。 1. 题目 2. 分析 这里提供一版可以解题的思路。 定义两个词典,这两个词典分别记录s字符串…

DHC2-2多时段电子式时间继电器 带底座 约瑟JOSEF

DHC2多时段电子式时间继电器 DHC2-H多时段电子式时间继电器 DHC2-1多时段电子式时间继电器 DHC2-2多时段电子式时间继电器 DHC2-3多时段电子式时间继电器 一、特点 超小型面板尺寸DIN 36X36mm(DHC1)、DIN 48X48m(DHC2) 有延时吸合、延时释放、等周期循环三种规格 可替代…

kibana连接elasticsearch(版本8.11.3)

前言 elasticsearch在8版本之后就出现了很大变化,由于kibana版本需要需elasticsearch进行版本对象,kibana连接方式也出现了很大变化。我在这里记录下自己的踩坑记录。 服务部署 本文中的服务都是在docker环境中部署的。其中elasticsearch版本和kibana版…

5G-A通感融合赋能低空经济-RedCap芯片在无人机中的应用

1. 引言 随着低空经济的迅速崛起,无人机在物流、巡检、农业等多个领域的应用日益广泛。低空飞行器的高效、安全通信成为制约低空经济发展的关键技术瓶颈。5G-A通感一体化技术通过整合通信与感知功能,为低空网络提供了强大的技术支持。本文探讨了5G-A通感…

OpenCV 寻找棋盘格角点及绘制

目录 一、概念 二、代码 2.1实现步骤 2.2完整代码 三、实现效果 一、概念 寻找棋盘格角点(Checkerboard Corners)是计算机视觉中相机标定(Camera Calibration)过程的重要步骤。 OpenCV 提供了函数 cv2.findChessboardCorners…

什么? CSS 将支持 if() 函数了?

CSS Working Group 简称 CSSWG, 在近期的会议中决定将 if() 添加到 CSS Values Module Level 5 中。 详情可见:css-meeting-bot 、[css-values] if() function 当我看到这个消息的时候,心中直呼这很逆天了,我们知道像 less 这些 css 这些预…

用Speedtest-Tracker跟踪上网速度(续)

什么是 Speedtest Tracker ? Speedtest Tracker 是一款自托管互联网性能跟踪应用程序,可针对 Ookla 的 Speedtest 服务运行速度测试检查。 之前老苏介绍的另一个 https://github.com/henrywhitaker3/Speedtest-Tracker 已被放弃。现在这个是积极维护的替代品&#…

STM32编写代码之嵌入式常用位操作

在单片机编程的过程中&#xff0c;经常会遇到位操作进行赋值&#xff0c;例如 //程序1 int a 0,b 0x5b,c 0; //1 for (i 0; i < 8; i ) { //2a b & (0x80 >> i)); //3 c | (0x80 >> i); //4 } 这些位操作是什么意思呢&#xff1f…

GuLi商城-商品服务-API-品牌管理-JSR303自定义校验注解

自定义注解规则: 可以参考@NotNull注解 package com.nanjing.common.valid;import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target;i…

跨域解决方案

跨域 当发起请求的协议号、域名、端口号中有一个不一样时就会导致跨域 跨域解决方案 分为两个方面&#xff0c;是否可以修改服务器端。 可以修改服务器端&#xff1a;cors方案、jsonp方案 不可以修改服务器端&#xff1a; 使用代理&#xff1a; 因为跨域主要是针对浏览器…

T113-i系统启动速度优化方案

背景: 硬件:T113-i + emmc 软件:uboot2018 + linux5.4 + QT应用 分支:longan 问题: 全志T113-i的官方系统软件编译出的固件,开机启动时间10多秒,启动时间太长,远远超过行业内linux系统的开机速度,需要进一步优化。 T113-i 优化后启动速度实测数据 启动阶段启动时间(…

Fastgpt本地使用Docker Compose 快速部署

使用 Docker Compose 快速部署 FastGPT 部署架构图 MongoDB:用于存储除了向量外的各类数据 PostgreSQL/Milvus:存储向量数据 OneAPI: 聚合各类 AI API,支持多模型调用 (任何模型问题,先自行通过 OneAPI 测试校验) 推荐配置 PgVector版本 体验测试首选 环境最低配置(单…

MySql性能调优04-[MySql事务与锁机制原理]

MySql事务与锁机制原理 从undo与redo日志&#xff0c;理解事务底层ACID底层原理事务四大隔离级别事务底层锁机制和MVCC并发优化机制串行化底层实现机制读已提交和可重复读底层实现MVCC机制详解脏写问题(重要)读已提交&#xff1f;实现机制 BufferPool缓存与redo日志是如何提升事…

海康相机GrabImage

#include <stdio.h> #include <Windows.h> #include <process.h> #include <conio.h> #include "MvCameraControl.h"bool g_bExit false;// ch:等待按键输入 | en:Wait for key press void WaitForKeyPress(void) {while(!_kbhit()){Sleep(…