python实现利用Socket进行图片、文件的传输

        近期测试方面有需求,利用一台主机进行图像数据获取,然后传输至另一台主机上,我的方法是利用socket实现,开发语言是python,能够在客户端实现图像的获取,然后发送至服务器,服务器端可以选择保存图片至指定文件夹,也可以不保存,实时查看。

一、视频转化为图片的传输:

        客户端打开笔记本摄像头,将图片格式进行转换后,发送至服务器,服务器格式转换后显示保存。

服务器程序:

# -*- coding: utf-8 -*-
#功能:
# 1.一台电脑/两台电脑间,客户端自动截屏后传输图像至服务器
# 2.一台电脑/两台电脑间,客户端自动打开摄像头传输图像至服务器
#服务器程序
import socketserver, struct, gzip, time
import PIL.ImageShow
from PIL import ImageFile, Image
from io import BytesIO
import matplotlib.pyplot as plt
import cv2
import numpy
def Save(imgBytes):
	imgIO = BytesIO(imgBytes)
	img = Image.open(imgIO)
	img = img.convert('YCbCr') # 转换成YCbCr格式
	# img.save('.\screenshot\{:.2f}.jpeg'.format(time.time()))#保存图片,注意图片会一直存储,要及时清理

class DATA(socketserver.BaseRequestHandler):

	def handle(self):

		ImageFile.LOAD_TRUNCATED_IMAGES = True
		s = self.request

		while True:
			t1 = time.time()
			headSize = struct.calcsize('l') # 计算文件头长度
			head = s.recv(headSize) # 接收文件头

			if head:
				imgSize = struct.unpack('l', head)[0] # 获取图像长度,类型:int
				recvSize = 0 # 接收到的数据长度
				imgBytesZ = b'' # 接收到的数据
				print("imgSize,recvSize,imgBytesZ",imgSize,recvSize,imgBytesZ)

				while True:
					if imgSize - recvSize > 0: # 不断接收数据直至没有数据
						imgBuf = s.recv(SERVER().bufSize)
						recvSize += len(imgBuf)
					else:
						break
					imgBytesZ += imgBuf
				imgBytes = gzip.decompress(imgBytesZ) # 解压数据
				t2 = time.time()
				T = str(str((t2 - t1) * 1000) + "ms")
				print("接受图片所需时间",T)
				Save(imgBytes)#保存图像
				#图像显示
				bytes_stream = BytesIO(imgBytes)#图像由Byte格式转换回PCI.Image格式
				image = Image.open(bytes_stream)
				PIL.ImageShow.show(image)
				# img = cv2.cvtColor(numpy.asarray(image), cv2.COLOR_RGB2BGR)#显示图片,每次都会重新打开一个界面显示传输的图片

class SERVER():

	def __init__(self):
		self.bufSize = 8388608 # 每一次接受的字节流长度

	def Server(self):
		server = socketserver.ThreadingTCPServer(('192.168.1.12', 8000), DATA) # 自定义端口,不同主机的话,服务器写自己的主机端口,客户机写服务器主机端口
        #server = socketserver.ThreadingTCPServer(('127.0.0.1', 8000), DATA) #同一主机
		server.serve_forever()

if __name__ == "__main__":
	SERVER().Server()

客户端程序:

# -*- coding: utf-8 -*-
#功能:
# 1.一台电脑/两台电脑间,客户端自动截屏后传输图像至服务器
# 2.一台电脑/两台电脑间,客户端自动打开摄像头传输图像至服务器
#客户端程序
import socket, struct, pyscreenshot, gzip
from PIL import Image
from io import BytesIO
import cv2
import time
import numpy


class CLIENT():

    def __init__(self):
        self.bufSize = 8388608  # 每一次传输的字节流长度

    #自动截屏功能,需要可以开启,使用的话注意注释掉下面的调用摄像头代码
    '''
    # def Data(self, s):
    #     while True:
    #         img = pyscreenshot.grab()  # 截取屏幕图像,类型:PIL.PngImagePlugin.PngImageFile
    #
    #         imgW = img.size[0]  # 图像宽度
    #         imgH = img.size[1]  # 图像长度
    #         if imgW > 1920:  # 图像宽度不超过1920
    #             zoom = 1920 / imgW
    #         else:
    #             zoom = 1
    #         imgResize = img.resize((int(imgW * zoom), int(imgH * zoom)), Image.Resampling.LANCZOS)  # 调整后的图像
    #         print(type(imgResize))
    #         imgIO = BytesIO()  # 创建文件对象,类型:io.BytesIO
    #         imgResize.save(imgIO, 'JPEG')  # 以JPEG格式存储,减少数据大小
    #         print(type(imgIO))
    #         imgIOZ = BytesIO()  # 创建文件对象,类型:io.BytesIO
    #         imgIOZ.write(gzip.compress(imgIO.getvalue()))  # 压缩原图并存入文件对象
    #         print(type(imgIOZ))
    #         imgBytes = imgIOZ.getvalue()  # 图像的字节流,类型:bytes
    #         print(len(imgBytes))  # 显示字节流长度
    #
    #         imgSize = len(imgBytes)  # 图像大小(字节流长度),类型:int
    #         head = struct.pack('l', imgSize)  # 构造文件头信息,内容是图像大小(字节流长度),类型:bytes
    #         s.send(head)  # 发送文件头

            # imgIOZ.seek(0, 0)  # 从开头读取图片
            # while True:
            #     imgBuf = imgIOZ.read(self.bufSize)  # self.bufSize大小的图片,类型:bytes
            #     if not imgBuf:  # 传输完成退出循环
            #         break
            #     s.send(imgBuf)  # 发送self.bufSize大小的图片
    '''

    #调用笔记本摄像头程序,使用时注意注释掉上方截屏部分的代码
    def Data(self, s):
        # screen = cv2.VideoCapture(0)
        while True:
            # img即为
            t1 = time.time()
            sucess, img = screen.read()
            # 转为灰度图片,转不转都可以
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            img_encode = cv2.imencode('.jpg', img)[1]
            data_encode = numpy.array(img_encode)
            # str_encode = data_encode.tostring()
            str_encode = data_encode.tobytes()
            encode_len = str(len(str_encode))
            print(encode_len)  # 输出看一下encode码的大小,可有可无
            # 显示摄像头
            cv2.imshow("321img", gray)
            # 保持画面的持续。
            k = cv2.waitKey(3) #这里如果为0的话,就是将你目前所在的画面定定格,为其他数字比如1的时候,表示1秒后程序结束。但是由于是死循环,所以结束后马上开启,就为连续图像,
            if k == 27:
                # 通过esc键退出摄像
                cv2.destroyAllWindows()
                break
            elif k == ord("s"):  # 关闭摄像头
                # 通过s键保存图片,并退出。
                cv2.imwrite("image2.jpg", img)
                cv2.destroyAllWindows()
                break

            imgResize = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))#将opencv格式图像,转化为PIC.Image格式
            print(type(imgResize))
            imgIO = BytesIO()  # 创建文件对象,类型:io.BytesIO
            imgResize.save(imgIO, 'JPEG')  # 以JPEG格式存储,减少数据大小
            print(type(imgIO))
            imgIOZ = BytesIO()  # 创建文件对象,类型:io.BytesIO
            imgIOZ.write(gzip.compress(imgIO.getvalue()))  # 压缩原图并存入文件对象
            print(type(imgIOZ))
            imgBytes = imgIOZ.getvalue()  # 图像的字节流,类型:bytes
            print(len(imgBytes))  # 显示字节流长度
            imgSize = len(imgBytes)  # 图像大小(字节流长度),类型:int
            head = struct.pack('l', imgSize)  # 构造文件头信息,内容是图像大小(字节流长度),类型:bytes
            s.send(head)  # 发送文件头
            imgIOZ.seek(0, 0)  # 从开头读取图片
            while True:
                imgBuf = imgIOZ.read(self.bufSize)  # self.bufSize大小的图片,类型:bytes
                if not imgBuf:  # 传输完成退出循环
                    break
                s.send(imgBuf)  # 发送self.bufSize大小的图片
                print("send successed")
            t2 = time.time()
            T = str(str((t2 - t1)*1000) + "ms")
            print("发送图片所需时间:", T)


    def Client(self):
        while True:  # 死循环加上try语句可以不用考虑先运行服务器端
            try:  # 连接不上重试
                s = socket.socket()
                s.connect(('192.168.1.12', 8000))  # 自定义服务器IP和端口
                #s.connect(('127.0.0.1', 8000))# 同一台主机下服务器IP和端口
            except Exception as e:
                print('Connection error')
                print(e)
                s.close()
                continue

            try:  # 传输数据发生错误重试
                self.Data(s)  # 处理数据并传输
            except Exception as e:
                print('Data error')
                print(e)
            finally:
                s.close()


if __name__ == '__main__':
    screen = cv2.VideoCapture(0)
    CLIENT().Client()


二、图片、文件的传输,都是一样利用的socket,代码如下:

客户端代码:

# 服务器端server.py
# 能实现主机自身或不同主机间传图片或者文件,不过目前只能是客户端发,服务器收,
# 互相发送的功能还未实现,如果是单纯不想利用U盘传图片,笨方法是互相调换身份,
# 那就用客户端运行服务器程序,注意更换ip
import socket
import os
import sys
import struct


def socket_service_image():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(('127.0.0.1', 8000))
        s.listen(10)

    except socket.error as msg:
        print(msg)
        sys.exit(1)

    print("Wait for Connection.....................")

    while True:
        sock, addr = s.accept()  # addr是一个元组(ip,port)
        deal_image(sock, addr)


def deal_image(sock, addr):
    print("Accept connection from {0}".format(addr))  # 查看发送端的ip和端口

    while True:
        fileinfo_size = struct.calcsize('128sq')
        buf = sock.recv(fileinfo_size)  # 接收图片名
        print(buf)
        if buf:
            filename, filesize = struct.unpack('128sq', buf)
            fn = filename.decode().strip('\x00')
            print(type(fn))
            new_filename = os.path.join(r'E:\\reseive_images\\' + fn)  
            recvd_size = 0
            fp = open(new_filename, 'wb')
            while not recvd_size == filesize:
                if filesize - recvd_size > 1024:
                    data = sock.recv(1024)
                    recvd_size += len(data)
                else:
                    data = sock.recv(1024)
                    recvd_size = filesize
                fp.write(data)  # 写入图片数据
            fp.close()
        sock.close()
        break
if __name__ == '__main__':
    socket_service_image()

客户端程序:

# 客户端client.py
# 图片或者文件都可以
import socket
import os
import sys
import struct


def sock_client_image():
    while True:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # s.connect(('改成服务器ip地址', 8001))  # 服务器和客户端在不同的系统或不同的主机下时使用的ip和端口,首先要查看服务器所在的系统网卡的ip
            # s.connect(('127.0.0.1', 8000))  #服务器和客户端都在一个系统下时使用的ip和端口
            s.connect(('192.168.1.13', 8000))  # 服务器和客户端是两个主机时,服务器的ip和端口
        except socket.error as msg:

            print(msg)
            print(sys.exit(1))

         # 输入当前目录下的图片名 xxx.jpg
        filepath = r'F:\pythonProject\Pyserial-Demo-master\Pyserial-Demo-master\test1.jpg'

        fhead = struct.pack(b'128sq', bytes(os.path.basename(filepath), encoding='utf-8'),os.stat(filepath).st_size)  # 将xxx.jpg以128sq的格式打包
        s.send(fhead)
        fp = open(filepath, 'rb')  # 打开要传输的图片
        while True:
            data = fp.read(1024)  # 读入图片数据
            if not data:
                print('{0} send over...'.format(filepath))
                break
            s.send(data)  # 以二进制格式发送图片数据
        s.close()
        break    #循环发送

if __name__ == '__main__':
    sock_client_image()

三、大文件的传输,其实原理和上面是一样的,结构也差不多,代码如下:

服务器端:


#快速读取快速存储,大文件也可以
import time
import cv2
import os
import json
import socket
import threading


#读取文件的最大数
max_len=8388608  #也可以提前写一个大数字,这样传输的会快一些,或者用于,如4294967296

#端口号和IP地址
remote_PORT=5555
remote_IP='127.0.0.1'
remote_addr=(remote_IP,remote_PORT)

#绑定IP地址和端口号PORT
socket_Server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_Server.bind(remote_addr)

#监听
socket_Server.listen()
print('正在监听来自客户端的消息......')

def Server_Recv_File(socket):
    """
    :param socket: 服务端套接字
    :param root: 主窗口
    :return:
    """

    t1 = time.time()
    #获取客户端发送的消息头
    msg_header=socket.recv(max_len)
    header=json.loads(msg_header.decode('utf-8'))
    #输出客户端发送的消息头信息
    print(header)
    #保存接收文件的地方

    # curr_path=os.getcwd()
    curr_path = os.path.join(r'E:\reseive_images_1')
    # time = datetime.now()
    # time = datetime.strftime(datetime.now(), '%Y%m%d-%H%M%S')
    filename = curr_path + '\\recv_' + header['filename'] + header['msg_type']
    get_file_Size=header['msg_len']
    file_size=0
    #输出文件名和文件大小
    print('文件名: {}'.format(filename))
    print(type(filename))
    print('file_size: {}'.format(get_file_Size))
    recv_count = 0
    #如果文件不存在则创建
    if os.path.exists(filename)==False:
        with open(filename,'wb') as fp:
            while file_size!=get_file_Size:
                message=socket.recv(max_len)
                fp.write(message)
                file_size+=len(message)
                print(file_size)
                recv_count+=1
    else:
        with open(filename, 'wb') as fp:
            while file_size != get_file_Size:
                message = socket.recv(max_len)
                fp.write(message)
                file_size += len(message)
                print(file_size)
                recv_count += 1
    print('接收次数: {}'.format(recv_count))
    socket.close()
    print('接收完成...')
    t2 = time.time()
    t = t2-t1
    print(t)


if __name__=='__main__':
    print('Pycharm')
    new_socket,addr=socket_Server.accept()
    Server_Recv_File(new_socket)

 客户端:


#快速读取快速存储,大文件也可以
import os
import json
import socket
import threading
import time
#读取文件的最大数,可以修改,提前写一个大数字,这样传输的会快一些,或者用于,如4294967296
max_len=8388608

#端口号和IP地址
remote_PORT=5555
remote_IP='127.0.0.1'
remote_addr=(remote_IP,remote_PORT)

#绑定端口号和IP地址
socket_Client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_Client.connect(remote_addr)

def Client_Send_File(socket_Client,filename):
    """
    :param socket: 客户端套接字
    :param filename: 要传输的文件
    :param root:主窗口
    :return:
    """

    t1 = time.time()
    #首先将消息头发送至服务端
    file,class_file=os.path.splitext(filename)
    #获取文件大小
    # file_size=os.path.getsize(filename)
    file_Size=os.stat(filename).st_size
    msg_header={'filename':file,'msg_type':class_file,'msg_len':file_Size}
    msg_header_bytes=bytes(json.dumps(msg_header),encoding='utf-8')
    #当消息头的长度不满1024时,使用空格填充
    msg_header_bytes+=b''*(max_len-len(msg_header_bytes))
    socket_Client.send(msg_header_bytes)
    file_len=0
    recv_count=0
    #发送的文件头大小
    print('msg_header_bytes: {}'.format(len(msg_header_bytes)))
    #发送的文件大小
    print('file_size: {}'.format(file_Size))
    with open(filename,'rb') as fp:
        while file_len!=file_Size:
            message=fp.read(max_len)
            socket_Client.send(message)
            file_len+=len(message)
            print(file_len)
            recv_count+=1
    print('发送次数: {}'.format(recv_count))

    # socket_Client.close()
    print('发送完成...')
    t2 =time.time()
    t=t2-t1
    print(t)

if __name__=='__main__':
    print('Pycharm')
    #第二个参数为图片或者其他文件的路径
    Client_Send_File(socket_Client,'yolov3.weights')

  • 4
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python利用socket传输视频可以通过以下步骤实现: 1. 创建一个服务器端和一个客户端,使用socket模块中的socket函数创建套接字对象。 2. 在服务器端,使用bind函数将套接字绑定到一个IP地址和端口号上。 3. 在服务器端,使用listen函数监听客户端的连接请求。 4. 在客户端,使用connect函数连接到服务器端。 5. 在服务器端,使用accept函数接受客户端的连接请求,并返回一个新的套接字对象。 6. 在服务器端,使用recv函数接收客户端发送的视频数据,并将其写入到一个文件中。 7. 在客户端,使用send函数发送视频数据给服务器端。 8. 在客户端和服务器端,使用close函数关闭套接字对象。 需要注意的是,视频数据的传输需要进行分包处理,以避免数据丢失和传输效率低下。同时,还需要对视频数据进行压缩和解压缩处理,以减小数据传输量。 ### 回答2: Python是一种非常流行的编程语言,它具有易学易用、开发效率高等优点,因此,越来越多的开发者选择使用Python进行各种编程任务,其中使用Python进行网络编程是一个具有广泛应用的领域。 在Python中,使用socket库可以实现网络编程,其中,socket是一种网络编程的基础组件,它提供了一种通用的网络通信模式,让开发者可以轻松实现基于TCP或UDP协议的网络应用程序。 在实践中,Python可以使用socket库来实现视频传输,通过在服务端和客户端之间建立socket连接,进行视频数据的传输。在这个过程中,服务端将视频数据流发送给客户端,而客户端则接收到视频数据流后进行播放。 具体来说,实现视频传输的过程如下: 首先,在服务端,需要先创建一个socket对象,并指定socket类型为SOCK_STREAM(TCP类型),并绑定IP地址和端口号。然后,通过调用listen()方法开始监听客户端的连接请求,并在连接成功后接受客户端发送的数据流。 接着,在客户端,需要创建一个socket对象,并指定socket类型为SOCK_STREAM(TCP类型),同时连接服务端的IP地址和端口号。然后从服务端接收数据流,并进行播放。 在这个过程中,需要注意一些问题,比如数据帧的分割、传输协议的选择、编码格式的处理等等。但总的来说,通过socket实现视频传输是一种简单、高效的方法。 总之,Python利用socket传输视频是一项常用的网络编程技术,在日常工作中被广泛应用。对于想要深入了解Python网络编程相关技术的开发者,这是一项必须要掌握的技能。 ### 回答3: Python 是一种使用非常广泛的编程语言,提供了许多标准库和第三方库,可以方便地实现网络编程。其中,socketPython 中一种用于网络编程的标准库,支持不同层次的网络协议和多种 I/O 模型。 视频传输是一个常见的应用场景,在 Python 中可以利用 socket 实现。视频传输需要解决以下几个问题: 1. 视频的编码和解码; 2. 视频的分片和组装; 3. 视频数据的传输和处理。 首先,让我们考虑视频的编码和解码。Python 中可以使用 ffmpeg 库进行视频编解码处理。它是一个开源的跨平台视频和音频处理软件,支持多种编解码器和容器格式,并且可以通过 Python 的 subprocess 模块进行调用。 其次,视频的分片和组装。为了实现视频的实时传输,通常需要将视频分割成多个数据包进行传输。在 Python 中可以利用 socket 的 send() 方法将分片后的视频数据发送给接收方。同时,需要定义一个协议来规定每个数据包的大小和格式,以便接收方正确地解析和组装视频数据。 最后,为了提高视频传输的效率,可以采用一些传输优化方式,如使用 UDP 协议进行传输,或者利用多线程和异步编程等方式提高处理和传输效率。 总的来说,Python 利用 socket 进行视频传输,并不是一件容易的任务,需要综合考虑编解码、分片组装、传输协议和传输优化等多个方面。但是,随着 Python 技术的不断发展,越来越多的第三方库和工具可以帮助我们轻松地完成这些任务,提高视频传输的效率和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值