1、基于socket库 TCP 协议实现普通小文件上传

客户端代码:

# tcp_small_file_client.py
import socket
import os
import json

client = socket.socket()
client.connect(('127.0.0.1', 9090))
menu = {"1":"upload","2":"download","3":"exit"}
for key,val in menu.items():
    print(key, val)

while True:
    option = input('请输入功能:')
    if option == '1':
        file_path = input('请输入要上传文件的绝对路径:')
        file_name = os.path.basename(file_path)
        with open(file_path,'r') as f:
            file_data = f.read()
        dic = {"option":"1","name":file_name,"data":file_data}
        client.send(json.dumps(dic).encode())
        msg = client.recv(1024)
        if msg.decode() == 'ok':
            print('上传完成...')
    elif option == '2':
        print('暂未开放...')
    elif option == '3':
        break
    else:
        print('不支持的功能,请重新输入。')
client.close()

服务端代码:

# tcp_small_file_server.py
import socket
import os
import json

server = socket.socket() # 默认就是tcp
server.bind(('127.0.0.1', 9090))
server.listen()

print('服务启动...')
while True:
    conn, addr = server.accept()
    print('新的客户端:', addr)
    while True:
        try:
            data = conn.recv(2048)
            dic = json.loads(data.decode())
            if dic.get('option') == '1':
                base_path = os.path.dirname(os.path.abspath(__file__))
                file_path = os.path.join(base_path,dic.get('name'))
                with open(file_path,'w') as f:
                    f.write(dic.get('data'))
                conn.send('ok'.encode())
        except Exception as e:
            print('有连接出现异常断开:', str(e))
            break
    conn.close()
server.close()

运行:
先运行服务端

python tcp_small_file_server.py

再运行客户端,并输入对于的功能上传文件

python tcp_small_file_client.py

2、基于socket库 TCP 协议实现大文件上传,并引入进度条

客户端代码:

# tcp_big_file_client.py
import socket
import os
import json
import time


def print_bar1(percent):
    bar = '\r' + '*' * int((percent * 100)) + ' %3.0f%%|' % (percent*100) + '100%'
    print(bar, end='', flush=True)


client = socket.socket()
client.connect(('127.0.0.1', 9090))
menu = {"1":"upload","2":"download","3":"exit"}
for key,val in menu.items():
    print(key, val)

while True:
    option = input('请输入功能:')
    if option == '1':
        file_path = input('请输入要上传文件的绝对路径:')
        file_name = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)
        dic = {"option":"1","name":file_name,"file_size":file_size}
        client.send(json.dumps(dic).encode())
        msg = client.recv(100) # 判断服务端准备好接收文件了,还可以防止粘包
        begin_size = file_size
        if msg.decode() == 'ok':
            # 服务端表示准备好接收文件了,开始循环发送文件
            with open(file_path, 'rb') as f:
                while file_size:
                    content = f.read(1024)
                    client.send(content)
                    file_size -= len(content)
                    print_bar1(round((begin_size - file_size) / begin_size, 2))
                print('')
    elif option == '2':
        print('暂未开放...')
    elif option == '3':
        break
    else:
        print('不支持的功能,请重新输入。')
client.close()

服务端代码:

# tcp_big_file_server.py
import socket
import os
import json
import time

server = socket.socket() # 默认就是tcp
server.bind(('127.0.0.1', 9090))
server.listen()

print('服务启动...')
while True:
    conn, addr = server.accept()
    print('新的客户端:', addr)
    while True:
        try:
            data = conn.recv(2048)
            dic = json.loads(data.decode())
            if dic.get('option') == '1':
                base_path = os.path.dirname(os.path.abspath(__file__))
                file_path = os.path.join(base_path,dic.get('name'))
                conn.send('ok'.encode()) # 告诉客户端,准备好接收文件了
                # 准备接收发来的文件内容
                with open(file_path, 'ab') as f:
                    while dic['file_size']:
                        content = conn.recv(1024)
                        f.write(content)
                        dic['file_size'] -= len(content)
        except Exception as e:
            print('有连接出现异常断开:', str(e))
            break
    conn.close()
server.close()

运行同上,先运行服务端,再运行客户端。这次不同的时客户端有上传进度条,可视化查看上传进度。

3、基于socket库 TCP 协议实现大文件上传,并支持断点续传

客户端代码:

# tcp_continue_big_file_client.py
import socket
import os
import json
import time


def print_bar1(percent):
    bar = '\r' + '*' * int((percent * 100)) + ' %3.0f%%|' % (percent*100) + '100%'
    print(bar, end='', flush=True)


client = socket.socket()
client.connect(('127.0.0.1', 9090))
menu = {"1":"upload","2":"download","3":"exit"}
for key,val in menu.items():
    print(key, val)

while True:
    option = input('请输入功能:')
    if option == '1':
        file_path = input('请输入要上传文件的绝对路径:')
        file_name = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)
        dic = {"option":"1","name":file_name,"file_size":file_size}
        client.send(json.dumps(dic).encode())
        file_seek = int(client.recv(100).decode())
        if file_seek == file_size:
            print('文件已经存在服务端,退出此次传输...')
        else:
            new_size = file_size - file_seek
            begin_size = new_size
            # 服务端表示准备好接收文件了,开始循环发送文件
            with open(file_path, 'rb') as f:
                f.seek(file_seek)
                while new_size:
                    content = f.read(1024)
                    client.send(content)
                    new_size -= len(content)
                    print_bar1(round((begin_size - new_size) / begin_size, 2))
                    time.sleep(0.2)
                print('')
    elif option == '2':
        pass
    elif option == '3':
        print('暂未开放...')
    else:
        print('不支持的功能,请重新输入。')
client.close()

服务端代码:

# tcp_continue_big_file_server.py
import socket
import os
import json
import time

server = socket.socket() # 默认就是tcp
server.bind(('127.0.0.1', 9090))
server.listen()

print('服务启动...')
while True:
    conn, addr = server.accept()
    print('新的客户端:', addr)
    while True:
        try:
            is_conn = True
            data = conn.recv(2048)
            dic = json.loads(data.decode())
            if dic.get('option') == '1':
                base_path = os.path.dirname(os.path.abspath(__file__))
                file_path = os.path.join(base_path,dic.get('name'))
                if os.path.exists(file_path):
                    file_seek = os.path.getsize(file_path)
                else:
                    file_seek = 0
                # 将文件指针发送过去,同时也可以解决粘包
                conn.send(str(file_seek).encode())
                if file_seek == dic['file_size']:
                    print('文件已经传输完成,退出此次传输...')
                else:
                    # 重新设置需要接收的文件大小
                    new_size = dic['file_size'] - file_seek
                    # 准备接收发来的追加文件内容
                    with open(file_path, 'ab') as f:
                        while new_size:
                            content = conn.recv(1024)
                            f.write(content)
                            new_size -= len(content)
                            # 因为Python中recv()是阻塞的,只有连接断开或异常时,接收到的是b''空字节类型,因此需要判断这种情况就断开连接。
                            if content == b'':
                                is_conn = False
                                break
            if not is_conn:
                print('有连接客户端断开...')
                break
        except Exception as e:
            print('有连接出现异常断开:', str(e))
            break
    conn.close()
server.close()

运行同上,先运行服务端,再运行客户端。这次不同的时客户端有上传进度条,可视化查看上传进度。
这里为了要测试断点续传,在客户端代码中每发一次数据都会停顿0.2秒,在测试上传时,在进度条还没完成就手动kill调客户端。去看看服务端看文件存到什么程度了,再次运行客户端上传上一次为传输完成的文件,等传输完成后。再去服务端看目标文件是否完整、有效。

好了,到这里就基本完成我们需要的功能,小伙伴们可以在本地做测试,笔者的环境是Ubuntu + Python3.8。有兴趣的小伙伴可以把下载功能实现了,有啥问题大家可以多交流学习。