python实现udp传输图片功能


Posted in Python onMarch 20, 2020

本文实例为大家分享了python实现udp传输图片的具体代码,供大家参考,具体内容如下

首先要了解UDP的工作模式

python实现udp传输图片功能

对于服务器,首先绑定IP和端口,本机测试的时候可以使用127.0.0.1是本机的专有IP,端口号 大于1024的是自定义的,所以用大于1024的端口号,然后接收客户端数据,处理,返回
对于客户端,UDP不用建立连接,只管发送不管接收到没有,所以可以直接对服务器的IP地址和端口号发送信息,然后等待应答。

注意传输的数据是二进制流数据,所以要找方法把需要传输的数据编码成二进制码流,传过去之后再解码即可,这里我用到了opencv读取图片成numpy的array格式,然后编码,传输,最后接到之后再解码。

先说一次性传输整个图片,这个思路就是接受的参数设置很大,而且图片比较小的情况,实现比较简单

首先是服务器脚本,实现了接收、显示、应答

udp_sever.py

# -*- coding: utf-8 -*-

import socket
import cv2
import numpy as np

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定端口:
s.bind(('127.0.0.1', 9999))

print('Bind UDP on 9999...')

while True:
 # 接收数据:
 data, addr = s.recvfrom(400000)
 print('Received from %s:%s.' % addr)
 #解码
 nparr = np.fromstring(data, np.uint8)
 #解码成图片numpy
 img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
 cv2.imshow('result',img_decode)
 cv2.waitKey()
 reply = "get message!!!"
 s.sendto(reply.encode('utf-8'), addr)
 cv2.destroyAllWindows()

客户端脚本,实现了发送图片,接收应答

udp_client.py

# -*- coding: utf-8 -*-
import socket
import cv2
import numpy as np

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

img = cv2.imread('/home/xbw/jupyter_notebook/0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()

# 发送数据:
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))

s.close()

为了方便理解放一下图片转到二进制再转回图片的代码

import numpy as np
import cv2
img = cv2.imread('0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
str_encode = data_encode.tostring()
#print(str_encode)
nparr = np.fromstring(str_encode, np.uint8)
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('result',img_decode)
cv2.waitKey()
cv2.destroyAllWindows()

分批传输图片

搞了好久终于知道怎么分批传输图片了,首先要知道需要传的图片需要多长的内存,不然不知道什么时候停止接收,这样就要考虑加一个文件头,告诉服务器要接受多长的码流。

实现思路是,首先客户端要先发送一个文件头,包含了码流的长度,用一个long int型的数,先用struct.pack打包,发过去,然后循环发送图片的码流即可

接着服务器先接到文件头,确认图片码流的长度,然后循环接收确定长度的码流,最后再解码成图片即可

实现代码如下:

首先是客户端脚本

udp_client.py

# -*- coding: utf-8 -*-
import socket
import cv2
import numpy as np
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#读取图片,编码成二进制 bytes格式
img = cv2.imread('/home/xbw/jupyter_notebook/0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
#定义文件头,打包成结构体
fhead = struct.pack('l',len(data))
# 发送文件头:
s.sendto(fhead,('127.0.0.1', 9999))
#循环发送图片码流
for i in range(len(data)//1024+1):
 if 1024*(i+1)>len(data):
 s.sendto(data[1024*i:], ('127.0.0.1', 9999))
 else:
 s.sendto(data[1024*i:1024*(i+1)], ('127.0.0.1', 9999))
# 接收应答数据:
print(s.recv(1024).decode('utf-8'))
#关闭
s.close()

然后是服务器接收

udp_sever.py

# -*- coding: utf-8 -*-

import socket
import cv2
import numpy as np
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定端口:
s.bind(('127.0.0.1', 9999))

print('Bind UDP on 9999...')

while True:
 # 接收文件头,文件头的长度由calcsize函数确定,注意这里recvfrom是接收UDP消息,recv是接收TCP消息
 fhead_size = struct.calcsize('l')
 buf,addr = s.recvfrom(fhead_size)
 if buf:
 #这里结果是一个元组,所以把值取出来
 data_size = struct.unpack('l',buf)[0]
 #接收图片码流长度的码流
 recvd_size = 0
 data_total = b''
 while not recvd_size == data_size:
 if data_size -recvd_size >1024:
  data,addr = s.recvfrom(1024)
  recvd_size += len(data)
 else:
  data,addr = s.recvfrom(1024)
  recvd_size = data_size 
 data_total += data
# data, addr = s.recvfrom(400000)
 print('Received')
# reply = 'Hello, %s!' % data.decode('utf-8')
# s.sendto(reply.encode('utf-8'), addr)
 #把接到的码流解码成numpy数组,显示图像
 nparr = np.fromstring(data_total, np.uint8)
 img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
 cv2.imshow('result',img_decode)
 cv2.waitKey()
 #应答
 reply = "get message!!!"
 s.sendto(reply.encode('utf-8'), addr)
 cv2.destroyAllWindows()

-------------------分割线----------------

上面是基本的实现,经过一番学习我终于掌握了UDP传输的精髓

首先是确定客户端和服务器的运行机制

客户端:先定义一个socket对象,不用绑定,然后指定IP地址和端口发送消息,然后如果用了recvfrom就会一直阻塞等待应答(这个很有用,作用就是保证对方确实收到,再发新的消息,不用在考虑发送频率的问题了),前面加一个while True就可以循环发送了,如果涉及到很大的消息,可以拆分发送,技巧是先发送一个文件头高速服务器要发的内容有多大(文件头这里建议使用stuct库,看前面例程),然后随后发送文件内容,保证要循环发送,因为每次发送,对面就当发了一次,假如发了2048字节的内容,对面设置的每次收1024,那么剩下的1024就被丢掉了,而不是等待下次继续接收。还有就是发送的是二进制的码流,目前我用到的转换成码流的方法有:图片用opencv,先imencode 转成二进制,然后再转成numpy,然后再tostring。文件头这种,需要确切知道占多大内存,使得服务器好接收的,用了stuct库,里面的pack,unpack,calcsize三个函数非常好用,发送的时候把数据pack一下就能发送了。列表、字典等等,作为文件内容,用到了json,有点万能,先json.dumps转换成json类型,然后再encode编码成二进制即可拿去发送了。

服务器:先定义一个socket对象,绑定IP地址和端口,让客户端可以找到,然后等待接收消息,收到消息之后处理消息,应答,配合客户端的recvfrom,保证接收频率一致,服务器为了保证始终接收消息,一定会有一个while True,接收到的消息是二进制码流,因此要进行解码。针对上面讲的编码方式解码,其实就是编码方式的反向操作:图片,用opencv解码,先是np.fromstring,然后再cv2.imdecode(data, cv2.IMREAD_COLOR)。对于接收文件头,这里有点技巧,用struct.calcsize确定文件头长度,然后只接收这个长度的码流,再unpack出来即可,这里unpack是个元组。对于json,解码就是先decode,再json.loads即可,是上面编码的反向操作。

然后再高端一点的操作,同一个脚本多进程工作,这就要用到了threading.Thread创建多个进程,思路就是新建多个服务器,然后分配给不同的进程,他们的IP地址可以一样,端口号不一样就行,然后就可以在同一个脚本里并行工作了,这里不同于TCP,因为UDP不需要建立连接

然后附上我实现的源码,服务器脚本里有两个进程,一个接收客户端1的图片,另一个接收客户端2的列表

服务器

udp_server.py

# -*- coding: utf-8 -*-
import socket
import cv2
import numpy as np
import struct
import threading
import json
#设置IP地址、两个服务器端口号
dest_ip = '127.0.0.1'
img_port = 9999
msg_port = 6666

#服务器1的处理、应答函数,接收图片、显示、应答
def receive_img(rec_img):
 while True:
 # 接收数据:
 fhead_size = struct.calcsize('l')
 buf,addr = rec_img.recvfrom(fhead_size)
 if buf:
  data_size = struct.unpack('l',buf)[0]
  print(data_size)
 recvd_size = 0
 data_total = b''
 while not recvd_size == data_size:
  if data_size -recvd_size >1024:
  data,addr = rec_img.recvfrom(1024)
  recvd_size += len(data)
  else:
  data,addr = rec_img.recvfrom(1024)
  recvd_size = data_size 
  data_total += data
# data, addr = rec_img.recvfrom(400000)
 print('Received')
# reply = 'Hello, %s!' % data.decode('utf-8')
# rec_img.sendto(reply.encode('utf-8'), addr)

 nparr = np.fromstring(data_total, np.uint8)
 img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
 cv2.imshow('result',img_decode)
 cv2.waitKey(100)
 reply = "get message!!!"
 rec_img.sendto(reply.encode('utf-8'), addr)
# cv2.destroyAllWindows()

#服务器2函数,接收消息、输出、应答
def receive_msg(rec_msg):
 while True:
 msg_data ,msg_addr = rec_msg.recvfrom(1024)
 msg_str = msg_data.decode('utf-8')
 msg = json.loads(msg_str)
 print(msg)
 reply = 'get the msg'
 rec_msg.sendto(reply.encode('utf-8'),msg_addr)
 rec_msg.close()

#主函数 创建服务器、绑定端口、创建运行两个进程、调用上面两个函数
def main():
 #创建套接字
 rec_img = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 rec_msg = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 #绑定本地地址端口
 rec_img.bind((dest_ip, img_port))
 rec_msg.bind((dest_ip, msg_port))
 #创建进程
 t_recimg = threading.Thread(target=receive_img, args=(rec_img,))
 t_recmsg = threading.Thread(target=receive_msg, args=(rec_msg,))
 #开始进程
 t_recimg.start()
 t_recmsg.start()
 print('程序正常运行!!!')
 


if __name__ == '__main__':
 main()

客户端1

udp_client_1.py

# -*- coding: utf-8 -*-
import socket
import cv2
import numpy as np
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cap = cv2.VideoCapture(0)
#cap.set(3,320)
#cap.set(4,240)
while True:
 
 if cap.isOpened():
 flag, img = cap.read()
# img = cv2.imread('/home/xbw/jupyter_notebook/0.jpg')
 img_encode = cv2.imencode('.jpg', img)[1]
 data_encode = np.array(img_encode)
 data = data_encode.tostring()
 #定义文件头
 fhead = struct.pack('l',len(data))
 # 发送文件头、数据:
 s.sendto(fhead,('127.0.0.1', 9999))
 for i in range(len(data)//1024+1):
  if 1024*(i+1)>len(data):
  s.sendto(data[1024*i:], ('127.0.0.1', 9999))
  else:
  s.sendto(data[1024*i:1024*(i+1)], ('127.0.0.1', 9999))
 # 接收应答:
 cv2.waitKey(1)
 print(s.recv(1024).decode('utf-8'))

s.close()

客户端2

udp_client_2.py

import socket
import cv2
import numpy as np
import struct
import json
import time
#定义套接字
send_msg = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#设置目标IP地址、端口号
target_ip = '127.0.0.1'
target_port = 6666
#发送数据,等待应答
while True:
 data = [0,0,0,1]
 data_str = json.dumps(data)
 send_msg.sendto(data_str.encode(),(target_ip,target_port))
 time.sleep(0.01)
 print(send_msg.recv(1024).decode('utf-8'))

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python 实现简单的shell sed替换功能(实例讲解)
Sep 29 Python
pandas.DataFrame.to_json按行转json的方法
Jun 05 Python
python数据批量写入ScrolledText的优化方法
Oct 11 Python
Python开启线程,在函数中开线程的实例
Feb 22 Python
Python 通过requests实现腾讯新闻抓取爬虫的方法
Feb 22 Python
Python自动化之数据驱动让你的脚本简洁10倍【推荐】
Jun 04 Python
Python Django 添加首页尾页上一页下一页代码实例
Aug 21 Python
python shutil文件操作工具使用实例分析
Dec 25 Python
使用Python爬虫库BeautifulSoup遍历文档树并对标签进行操作详解
Jan 25 Python
python线程池如何使用
May 28 Python
Python 中如何使用 virtualenv 管理虚拟环境
Jan 21 Python
聊聊基于pytorch实现Resnet对本地数据集的训练问题
Mar 25 Python
python实现UDP协议下的文件传输
Mar 20 #Python
python实现TCP文件传输
Mar 20 #Python
python实现FTP循环上传文件
Mar 20 #Python
python实现ftp文件传输功能
Mar 20 #Python
Python开发之身份证验证库id_validator验证身份证号合法性及根据身份证号返回住址年龄等信息
Mar 20 #Python
python实现FTP文件传输的方法(服务器端和客户端)
Mar 20 #Python
python实现ftp文件传输系统(案例分析)
Mar 20 #Python
You might like
完美解决dedecms中的[html][/html]和[code][/code]问题
2007/03/20 PHP
PHP实现把MySQL数据库导出为.sql文件实例(仿PHPMyadmin导出功能)
2014/05/10 PHP
PHP数组与对象之间使用递归实现转换的方法
2015/06/24 PHP
php实现图片缩略图的方法
2016/03/29 PHP
PHP实现字符串大小写转函数的功能实例
2019/02/06 PHP
Ext 表单布局实例代码
2009/04/30 Javascript
Javascript中的window.event.keyCode使用介绍
2011/04/26 Javascript
uploadify在Firefox下丢失session问题的解决方法
2013/08/07 Javascript
jQuery实现仿QQ头像闪烁效果的文字闪动提示代码
2015/11/03 Javascript
JavaScript的设计模式经典之建造者模式
2016/02/24 Javascript
javascript DOM的详解及实例代码
2017/03/06 Javascript
jquery mobile实现可折叠的导航按钮
2017/03/11 Javascript
Angular4实现动态添加删除表单输入框功能
2017/08/11 Javascript
Vue.set() this.$set()引发的视图更新思考及注意事项
2018/08/30 Javascript
python内存管理分析
2015/04/08 Python
python爬虫入门教程--HTML文本的解析库BeautifulSoup(四)
2017/05/25 Python
Python实现根据日期获取当天凌晨时间戳的方法示例
2019/04/09 Python
pytorch多进程加速及代码优化方法
2019/08/19 Python
PyQt5实现画布小程序
2020/05/30 Python
美国女士泳装店:Swimsuits For All
2017/03/02 全球购物
美国女孩洋娃娃店:American Girl
2017/10/24 全球购物
自我鉴定思想方面
2013/10/07 职场文书
高中生学习生活的自我评价
2013/10/09 职场文书
人事科岗位职责范本
2014/03/02 职场文书
六一儿童节主持词
2014/03/21 职场文书
档案工作汇报材料
2014/08/21 职场文书
关于晚自习早退的检讨书
2014/09/13 职场文书
社区党员群众路线教育实践活动心得体会
2014/11/03 职场文书
2014年公路养护工作总结
2014/12/04 职场文书
年度考核登记表个人总结
2015/03/06 职场文书
八年级历史教学反思
2016/02/19 职场文书
工作建议书范文
2019/07/08 职场文书
使用css样式设计一个简单的html登陆界面的实现
2021/03/30 HTML / CSS
mysq启动失败问题及场景分析
2021/07/15 MySQL
全面盘点MySQL中的那些重要日志文件
2021/11/27 MySQL
Python编程中内置的NotImplemented类型的用法
2022/03/23 Python