处于不同局域网下的Socket通信(发送文件)

2022-08-14 09:05:49

学习笔记主要来源:python之socket编程


此例程可以完成文件(图片、txt文本文件……)通过socket来完成传输

然后对于程序中的几个代码进行相关知识点的解读

代码实现

<只需将文件的地址或者是文件进行修改即可>

service端:(接受文件以及保存文件)

'''
file:03_accept_save_service.py
Fuction:accept and save file from client as service
'''

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', 22))
        s.listen(10)
    except socket.error as msg:
        print(msg)
        sys.exit(1)
    print("Wait for Connection.....................")
    while True:
        sock, address = s.accept()  # addr是一个元组(ip,port)
        deal_image(sock, address)


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

    while True:
        fileinfo_size = struct.calcsize('128sq')# 返回格式字符串fmt描述的结构的字节大小
        print('fileinfo_size is', fileinfo_size)
        buf = sock.recv(fileinfo_size)  # 接收图片名
        print('buf is ', buf)
        if buf:#如果接收到了数据就开始接下来的操作
            filename, filesize = struct.unpack('128sq', buf)#按照客户端打包的格式进行解包,得到图片的名字和图片大小
            print('filename :',filename.decode(),'filesize :', filesize)
            fn = filename.decode().strip('\x00')#其实,这里的filename已经是解码完毕去掉字节符号的样子了,我感觉这一步是多此一举
            print('fn is ', fn)
            new_filename = os.path.join('C:/Users/86183/Desktop/' + fn)
            # 在服务器端新建图片名(可以不用新建的,直接用原来的也行,只要客户端和服务器不是同一个系统或接收到的图片和原图片不在一个文件夹下)

            recvd_size = 0
            fp = open(new_filename, 'wb')#二进制打开文件

            while not recvd_size == filesize:#如果收到的直接总数不等于这个文件的直接总数,那么就继续接受数据
                if filesize - recvd_size > 1024:#这个最后一次如果数据比
                    data = sock.recv(1024)#每次从客户端接受1024个字节
                    recvd_size += len(data)#每次都记录好收到的字节数,然后叠加上去
                else:#这个最后一次数据如果比1024少的话那么这一次读完就OK了,而且这次结束后循环也就结束了
                    data = sock.recv(1024)
                    recvd_size = filesize
                print('data is', data)#输出每一次收到的数据
                fp.write(data)  # 写入图片数据,因为每次都是读取到1024个字节,所以每一次读取都需要把得到的字节进行拼凑
            fp.close()
        sock.close()
        break

if __name__ == '__main__':
    socket_service_image()

client端:(发送文件)

'''
file:03_send_file_client.py
Fuction:send file as client
'''

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地址', 6000))
        except socket.error as msg:
            print(msg)
            print(sys.exit(1))
        file_path = 'C:/Users/86183/Desktop/test_dir/test.txt'  # 输入当前目录下的图片名
        #pack按照图片格式、图片名字、图片大小
        fhead = struct.pack(b'128sq', # 图片打包的格式为128sq
                            bytes(os.path.basename(file_path), encoding='utf-8'),#返回图片文件名,编码字节化
                            os.stat(file_path).st_size)  # 返回读取文件的相关属性,然后得到其中st_size的数据(记录文件大小)
        s.send(fhead)

        fp = open(file_path, 'rb')  # 打开要传输的图片
        while True:
            data = fp.read(1024)  # 读入图片数据,data是图片的数据字节流(由于图片字节很多,这里一次只读取1024个字节)
            if not data:#如果读到字节的数据没有了
                print('{0} send over...'.format(file_path))
                break
            s.send(data)  # 以二进制格式发送图片数据(每次发1024个字节)
        s.close()
        break#如果需要一直发送图片则需要在这里进行修改

if __name__ == '__main__':
    sock_client_image()

知识点补充:

一、Python os.stat() 方法

  • stat概述:stat 函数是用来获取文件的各种属性的一个linux下的常用API函数。
  • os.stat()概述:用于在给定的路径上执行一个系统 stat 的调用
  • 语法:os.stat(path)
  • 参数:path指的是文件路径
  • 返回值:
  1. st_mode:inode  保护模式
  2. st_ino:inode  节点号
  3. st_dev:inode  驻留的设备
  4. st_nlink:inode  的链接数
  5. st_uid:所有者的用户ID
  6. st_gid:所有者的组ID
  7. st_size:普通文件以字节为单位的大小;包含等待某些特殊文件的数据
  8. st_atime:上次访问的时间
  9. st_mtime:最后一次修改的时间
  10. st_ctime:由操作系统报告的“ctime”。在某些系统上(如Unix)是最新的元数据更改的时间,再其他系统上(如Windows)是创建时间
import os

file_path = 'C:/Users/86183/Desktop/test_dir/phone.png'
print(os.stat(file_path))
print(type(os.stat(file_path)))


#输出(做了调整好看一点)
os.stat_result(st_mode=33206,
 st_ino=33214047251881945, 
st_dev=3559340628, 
st_nlink=1, 
st_uid=0, 
st_gid=0, 
st_size=206921,
 st_atime=1591156383, 
st_mtime=1591156383, 
st_ctime=1591156383)
<class 'os.stat_result'>

二、Python bytes()函数

  • 概述:bytes 函数返回一个新的 bytes 对象,该对象是一个 0 <= x < 256 区间内的整数不可变序列。它是 bytearray 的不可变版本。
  • 语法:bytes([source[, encoding[, errors]]])
  • 参数:
  1. 如果 source 为整数,则返回一个长度为 source 的初始化数组
  2. 如果 source 为字符串,则按照指定的 encoding 将字符串转换为字节序列
  3. 如果 source 为可迭代类型,则元素必须为[0 ,255] 中的整数
  4. 如果 source 为与 buffer 接口一致的对象,则此对象也可以被用于初始化 bytearray
  5. 如果没有输入任何参数,默认就是初始化数组为0个元素
  • 返回值:一个新的bytes对象

三、Python struct模块

struct模块执行Python的和以Python的bytes表示的C结构体之间的转换,这可以用于处理存储在文件中或来自网络连接以及其他源的二进制数据;它使用一定格式的字符串作为C语言结构布局的简洁描述以及到或从Python值的预期转换

pack(fmt,v1,v2…)

  • 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回
  • 返回string

pack_into(fmt,buffer,offset,v1,v2…)

  • 按照给定的格式(fmt),将数据转换成字符串(字节流),并将字节流写入以offset开始的buffer中
  • buffer为可写的缓冲区,可用array模块
  • 无返回

unpack(fmt,v1,v2…..)

  • 按照给定的格式(fmt)解析字节流,并返回解析结果
  • 返回tuple

pack_from(fmt,buffer,offset)

  • 按照给定的格式(fmt)解析以offset开始的缓冲区,并返回解析结果
  • 返回tuple

calcsize(fmt)

  • 计算给定的格式(fmt)占用多少字节的内存,注意对齐方式
  • 返回size of fmt

这里面的fmt是格式字符(format的谐音),不要觉得这个很奇怪,看这个就明白这个是什么了:(大概就是下面的这个意思)

a = 10
b = 10.00
print('{:d} {:f}'.format(a,b))

#输出
1010 10.000000

struct模块支持的格式化字符如下:

这里写图片描述

下面来看一看关于pack()的一些例子:

关于unpack()的一些例子:

关于calcsize()的例子:

  • 作者:手可摘星辰不去高声语
  • 原文链接:https://blog.csdn.net/weixin_44917390/article/details/106723604
    更新时间:2022-08-14 09:05:49