python socket 发送文件例子 不定长的信息皆可

后两篇的地址

https://blog.csdn.net/y363893017/article/details/105702848

https://blog.csdn.net/y363893017/article/details/105695328

本例子是单线程的,大家可以再服务端加上多线程

性能未测试,但是很操蛋,再我电脑的两个centos7.4的虚拟机上运行服务端,再win上上传同样的文件,一个秒传,另一个最快要2:3秒,慢都要2:15秒,

由于服务端和客户端的代码差不多,所以服务端做了详细的解释,客户端偶尔解释

#### 下午上传的代码有点小问题,现在修改后重新编辑一次

服务端

#! /usr/bin/env python3

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

import socket
import pretty_errors  # 一个错误模块,导入即可,它会把错误的信息显示的五颜六色的
import os.path
import time          # 如果是linux服务器,则很有可能需要time.sleep(0.1),原因还不太清楚
import struct        # 本来传输数字应该就这个模块的,但是不太熟,所以最终未用
import json
#import readline      # 如果此脚本运行在linux服务器上,为了输入方便,则导入此模块


# 获取socket对象,如果觉得这样写麻烦,可以不用写在方法里面,直接写在外面即可
def sock_obj():
    addr_port = ("0.0.0.0", 8989)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 关闭连接后马上释放端口
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(addr_port)
    s.listen(5)
    return s


# 这个方法用于接收文件的字节,目前是单线程的,一次只能有一个人上传
# 这里的想法是三步骤:
# 第一步:接收文件名字和大小的字节的长度(有点拗口哈,我也不知道怎么说,反正就是一个数字)
# 第二步:接收实际文件的名字和大小的数据
# 第三步:接收实际文件的内容
def recv_file(sock, addr, dirname): #这里的 addr参数可以不要,因为我这里未用
    # 定义第第一次收到的数字的大小(主要不知道如何解释,看代码能看懂就行)
    tcp_min_len = 0
    # 定义第二次收到的消息的内容
    tcp_head_msg = b""
    # 这是第一次收到的消息,结果是一个str类型的数字(由于struct不熟,所以这里把int转换成str来传输)
    tcp_head = sock.recv(1024)
    tcp_head_len = struct.unpack(">H", tcp_head)  #这里是下午注释的地方,研究一下后换成这种方法
    time.sleep(0.1) #如果是linux系统的话,最好是延迟一下,win可以不用,因为我实测的时候linux会把几次的消息合并成一次接收,所以给一个延迟,原因未找到
    # 循环接收信息,这里循环接收的是第二次收到的消息,这里要保证第二次收到的消息一定是完整的,如果这里收到的消息不完整,那么会影响后面真正的数据
    while tcp_min_len < int(tcp_head_len[0]):
        # 开始第二次接收数据
        msg = sock.recv(1024)
        # 把第二次接收到的消息的长度加到tcp_min_len,如果它们之和不小于int(tcp_head_len)的话就说明数据已经接收完了
        tcp_min_len += len(msg)
        # 把接收到的消息无限的拼接到tcp_head_msg
        tcp_head_msg += msg
    print(json.loads(tcp_head_msg.decode("utf-8")))
    # 当第二次的数据接收完成后,这里就开始解码并且转换成python的dict类型,这里面的数据就是要传送文件的大小和名字
    tcp_head_dict = json.loads(tcp_head_msg.decode("utf-8"))
    file_min_len = 0
    # i = 0  如果想看每次接收的数据,可以把这里和下面有关i的行的注释都去掉
    # 打开一个文件
    with open(os.path.join(dirname, tcp_head_dict["name"]), "wb") as wf:
        # 循环接收到第三次发送的信息
        while file_min_len < int(tcp_head_dict["size"]):
            # 这里开始接收
            file_data = sock.recv(4096)
            #print("第%d次数据结果:%s" % (i, file_data))
            # 。。。同上
            file_min_len += len(file_data)
            # 把数据写到文件
            wf.write(file_data)
            # i += 1
    print("上传完毕")

if __name__ == "__main__":
    while True:
        #dir_name = input("please enter dir:")
        #dir_name = "D:\dvd"    # 如果觉得每次输入存放文件的目录麻烦,就用这个固定的吧。
        dir_name = "/root"    # 如果觉得每次输入存放文件的目录麻烦,就用这个固定的吧。
        if not os.path.isdir(dir_name):continue
        break
    sock, addr = sock_obj().accept()
    recv_file(sock, addr, dir_name)

客户端

大家可以看见里面每次send前都会time.sleep(0.1)一下,说实话为啥要这样我也不清楚,只是我看recv的信息的时候发现是连在一起的,所以加了sleep,有时候不加没问题,有时候有问题,所以我这里就选择加上了

#! /usr/bin/env python3
# -*- coding:utf-8 -*-

import pretty_errors
import os, os.path
import socket
import json
import struct
import time
from tqdm import tqdm

def is_file(path):
    if os.path.exists(path):
        if os.path.isfile(path):
            return True
        else:
            return False
    else:
        return False

def get_file_json(path):
    file_size = os.path.getsize(path)
    file_name = os.path.basename(path)
    file_dict = {"size": file_size, "name": file_name}
    return json.dumps(file_dict)
# 未使用此方法
def sock_obj():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((addr, int(port)))
    return s

def tcp_link(sock):
    buff_size = 4096
    while True:
        file_path = input("please enter file path:")
        if not is_file(file_path):continue
        break
    file_struct = struct.pack(">H", len(get_file_json(file_path).encode("utf-8")))
    sock.send(file_struct)
    time.sleep(0.1)
    sock.send(get_file_json(file_path).encode("utf-8"))
    time.sleep(0.1)
    with open(file_path, "rb") as rf:
        for i in tqdm(range(int(json.loads(get_file_json(file_path))["size"]) // buff_size + 1)):
            sock.send(rf.read(buff_size))
    print("send end")


if __name__ == "__main__":
    addr = "localhost"
    port = 8989
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((addr, port))
    tcp_link(s)




下图是我的测试结果,服务端是自己的阿里云不在大陆1M带宽,客户端就是我自己的电脑,上传忒慢。 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值