用python的socket写一个局域网传输文件的程序

局域网传输文件是最最常用的功能,我参考https://www.jb51.net/python/345837qrz.htm这篇文章,复制粘贴,开发了一个。但发现进度条没有用,也没有显示传输用时和传输速度的功能,于是我改写了代码,使它实现这个功能。

python 复制代码
import socket
import os

def start_server(host='192.168.1.145', port=8888):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((host, port))
    server_socket.listen(1)
    print(f"服务器启动,监听 {host}:{port}")

    while True:
        client_socket, addr = server_socket.accept()
        print(f"连接来自 {addr}")

        # 接收文件名和大小
        file_info = client_socket.recv(1024).decode()
        file_name, file_size = file_info.split('|')
        file_size = int(file_size)

        # 创建文件并写入数据
        with open('d:/socketfile/'+file_name, 'wb') as file:
            received = 0
            while received < file_size:
                data = client_socket.recv(4096)
                file.write(data)
                received += len(data)
                print(f"接收进度: {received}/{file_size} 字节")

        client_socket.close()
        print(f"文件 {file_name} 接收完成")

if __name__ == "__main__":
    start_server()

上面是服务器端,没有怎么改,增加了'd:/socketfile/'+file_name,把文件放在我们想要放的文件夹。

python 复制代码
# -*- coding: utf-8 -*-
"""
Created on Mon Sep  8 14:31:28 2025

@author: YBK
"""

import socket
import os
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import threading
from tkinter import ttk
import time

class FileTransferGUI:
    def __init__(self, master):
        self.master = master
        master.title("局域网文件传输工具")
        master.geometry("500x400")

        # 输入服务器IP
        tk.Label(master, text="服务器IP:").pack(pady=5)
        self.ip_entry = tk.Entry(master, width=30)
        self.ip_entry.pack(pady=5)
        self.ip_entry.insert(0, "192.168.1.145")  # 默认IP,根据实际修改
        
        #发生使用时间、传输速度
        self.usetime = 0
        self.sudu = 0

        # 文件选择按钮
        tk.Button(master, text="选择文件", command=self.select_file).pack(pady=10)
        self.file_path = ""

        # 发送按钮
        tk.Button(master, text="发送文件", command=self.start_send).pack(pady=10)

        # 进度显示
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(master, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X, padx=20, pady=10)

        # 日志区域
        self.log_area = scrolledtext.ScrolledText(master, height=10)
        self.log_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        self.log_area.config(state=tk.DISABLED)
        
    def send_file(self,server_ip, file_path, port=8888):        
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            client_socket.connect((server_ip, port))
            file_name = os.path.basename(file_path)
            file_size = os.path.getsize(file_path)

            # 发送文件信息
            client_socket.send(f"{file_name}|{file_size}".encode())

            # 分块发送文件数据
            with open(file_path, 'rb') as file:
                sent = 0
                start = time.time()
                while sent < file_size:
                    data = file.read(4096)
                    client_socket.send(data)
                    sent += len(data)
                    # print(f"发送进度: {sent}/{file_size} 字节")
                    self.progress_var.set(int(sent/file_size*100))
            self.usetime = time.time() - start
            self.sudu = file_size / 1024 / 1024 / self.usetime
            print(f"文件发送成功,耗时{self.usetime:.2f}秒,速度:{self.sudu:.2f}m/s")
        except Exception as e:
            print(f"错误: {e}")
        finally:
            client_socket.close()

    def select_file(self):
        self.file_path = filedialog.askopenfilename()
        if self.file_path:
            self.log(f"已选择文件: {self.file_path}")

    def start_send(self):
        server_ip = self.ip_entry.get()
        if not server_ip or not self.file_path:
            messagebox.showerror("错误", "请输入IP并选择文件")
            return

        # 在新线程中发送文件,避免GUI冻结
        threading.Thread(target=self.send_file_thread, args=(server_ip, self.file_path)).start()

    def send_file_thread(self, server_ip, file_path):
        try:
            self.log("开始发送文件...")
            self.send_file(server_ip, file_path)  # 调用步骤2的发送函数
            self.progress_var.set(100)
            self.log(f"文件发送完成!耗时{self.usetime:.4f}秒,速度:{self.sudu:.2f}m/s")
        except Exception as e:
            self.log(f"错误: {e}")

    def log(self, message):
        self.log_area.config(state=tk.NORMAL)
        self.log_area.insert(tk.END, message + "\n")
        self.log_area.config(state=tk.DISABLED)
        self.log_area.yview(tk.END)

if __name__ == "__main__":
    root = tk.Tk()
    app = FileTransferGUI(root)
    root.mainloop()

以上就改动比较大,也让我慢慢熟悉面向对象编程,用self.可以为公用变量,增加时间和速度的显示,原来代码出现tk.Progressbar报错,进度条无法正常显示等问题。

速度还是可以的。

用一段时间,感觉还是不爽,要开2个程序,一个是服务器端,一个是客户端,才能在2台电脑中互传文件,为此,我把它一体化,只要用一个程序就实现了服务器端和客户端的功能,又能接受文件,又能发送文件。同时,加上我一直喜欢用的拖放功能,不能每次去选择文件传输。

python 复制代码
# -*- coding: utf-8 -*-
"""
Created on Mon Sep  8 14:31:28 2025

@author: YBK
"""

import socket
import os
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import threading
from tkinter import ttk
import time
import windnd
from tkinter import LabelFrame
import chardet

def start_server(host='192.168.1.140', port=8888):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((host, port))
    server_socket.listen(1)
    print(f"服务器启动,监听 {host}:{port}")

    while True:
        client_socket, addr = server_socket.accept()
        print(f"连接来自 {addr}")

        # 接收文件名和大小
        raw_data = client_socket.recv(1024)
        encoding = chardet.detect(raw_data)['encoding']
        file_info = raw_data.decode(encoding)
        file_name, file_size = file_info.split('|')
        file_size = int(file_size)

        # 创建文件并写入数据
        with open('e:/socketfile/'+file_name, 'wb') as file:
            received = 0
            while received < file_size:
                data = client_socket.recv(4096)
                file.write(data)
                received += len(data)
                print(f"接收进度: {received}/{file_size} 字节")

        client_socket.close()
        print(f"文件 {file_name} 接收完成")

class FileTransferGUI:
    def __init__(self, master):
        self.master = master
        master.title("局域网文件传输工具")
        master.geometry("500x600")

        # 输入服务器IP
        tk.Label(master, text="服务器IP:").pack(pady=5)
        self.ip_entry = tk.Entry(master, width=30)
        self.ip_entry.pack(pady=5)
        self.ip_entry.insert(0, "192.168.1.145")  # 默认IP,根据实际修改
        
        tk.Label(master, text="本机IP(作为服务器):").pack(pady=5)
        self.bip_entry = tk.Entry(master, width=30)
        self.bip_entry.pack(pady=5)
        self.bip_entry.insert(0, "192.168.1.140")  # 默认IP,根据实际修改
        
        # 启动服务器按钮
        self.bt_fwq = tk.Button(master, text="启动服务器", command=self.start_fwq)
        self.bt_fwq.pack(pady=10)
        
        #发生使用时间
        self.usetime = 0
        self.sudu = 0

        # 文件选择按钮
        tk.Button(master, text="选择文件", command=self.select_file).pack(pady=10)
        self.file_path = ""
        
        # 文件拖放区域
        self.f_frame = LabelFrame(master, text="也可以拖放文件到此", width=250)
        self.f_frame.pack(expand=True, padx=10)
        self.label1 = tk.Label(self.f_frame,
                          wraplength=580,
                          text="")
        self.label1.pack(anchor="w", pady=5)
        windnd.hook_dropfiles(self.f_frame, func=self.handle_file)

        # 发送按钮
        tk.Button(master, text="发送文件", command=self.start_send).pack(pady=10)

        # 进度显示
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(master, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X, padx=20, pady=10)

        # 日志区域
        self.log_area = scrolledtext.ScrolledText(master, height=10)
        self.log_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        self.log_area.config(state=tk.DISABLED)
        
    def start_server(self,host='192.168.1.140', port=8888):
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind((host, port))
        server_socket.listen(1)
        print(f"服务器启动,监听 {host}:{port}")

        while True:
            client_socket, addr = server_socket.accept()
            print(f"连接来自 {addr}")

            # 接收文件名和大小
            raw_data = client_socket.recv(1024)
            encoding = chardet.detect(raw_data)['encoding']
            file_info = raw_data.decode(encoding)
            file_name, file_size = file_info.split('|')
            file_size = int(file_size)

            # 创建文件并写入数据
            with open('e:/socketfile/'+file_name, 'wb') as file:
                received = 0
                while received < file_size:
                    data = client_socket.recv(4096)
                    file.write(data)
                    received += len(data)
                    print(f"接收进度: {received}/{file_size} 字节")

            client_socket.close()
            print(f"文件 {file_name} 接收完成")
            self.log(f"文件 {file_name} 接收完成")
        
    def handle_file(self,files):
        if len(files) > 0:
            path = files[0].decode('gbk').replace('\\', '/')
            self.file_path = path
        if self.file_path:
            self.log(f"已选择文件: {self.file_path}")
    def start_fwq(self):
        fwq_ip = self.bip_entry.get()
        if not fwq_ip:
            messagebox.showerror("错误", "请输入IP并选择文件")
            return
        # print(fwq_ip)
        # 在新线程中发送文件,避免GUI冻结
        threading.Thread(target=self.start_server, args=(fwq_ip, 8888)).start()
        self.bt_fwq.config(state="disabled", bg="lightgray")
        
        
        
    def send_file(self,server_ip, file_path, port=8888):        
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            client_socket.connect((server_ip, port))
            file_name = os.path.basename(file_path)
            file_size = os.path.getsize(file_path)

            # 发送文件信息
            client_socket.send(f"{file_name}|{file_size}".encode())

            # 分块发送文件数据
            with open(file_path, 'rb') as file:
                sent = 0
                start = time.time()
                while sent < file_size:
                    data = file.read(4096)
                    client_socket.send(data)
                    sent += len(data)
                    # print(f"发送进度: {sent}/{file_size} 字节")
                    self.progress_var.set(int(sent/file_size*100))
            self.usetime = time.time() - start
            self.sudu = file_size / 1024 / 1024 / self.usetime
            print(f"文件发送成功,耗时{self.usetime:.2f}秒,速度:{self.sudu:.2f}m/s")
        except Exception as e:
            print(f"错误: {e}")
        finally:
            client_socket.close()

    def select_file(self):
        self.file_path = filedialog.askopenfilename()
        if self.file_path:
            self.log(f"已选择文件: {self.file_path}")

    def start_send(self):
        server_ip = self.ip_entry.get()
        if not server_ip or not self.file_path:
            messagebox.showerror("错误", "请输入IP并选择文件")
            return

        # 在新线程中发送文件,避免GUI冻结
        threading.Thread(target=self.send_file_thread, args=(server_ip, self.file_path)).start()

    def send_file_thread(self, server_ip, file_path):
        try:
            self.log("开始发送文件...")
            self.send_file(server_ip, file_path)  # 调用步骤2的发送函数
            self.progress_var.set(100)
            self.log(f"文件发送完成!耗时{self.usetime:.4f}秒,速度:{self.sudu:.2f}m/s")
        except Exception as e:
            self.log(f"错误: {e}")

    def log(self, message):
        self.log_area.config(state=tk.NORMAL)
        self.log_area.insert(tk.END, message + "\n")
        self.log_area.config(state=tk.DISABLED)
        self.log_area.yview(tk.END)

if __name__ == "__main__":
    root = tk.Tk()
    app = FileTransferGUI(root)
    root.mainloop()

填好ip,点击启动服务器就可以接收文件。下面还能显示接收到的文件。

相关推荐
2601_9679873724 分钟前
Sqlite参考手册
数据库·python·sqlite·api·教程
用户019027581611 小时前
A 股到价提醒机器人:AlphaFeed + 飞书/钉钉 Webhook
python
Lansonli1 小时前
大数据Spark(八十八):Spark任务资源之Standalone集群测试
java·服务器·spark
特立独行的猫A1 小时前
Ionic + 仓颉语言的 cj-tauri 开发框架实战:开发精美移动端 APP
java·后端·python
高洁012 小时前
数据同化与动态状态估计:让孪生持续贴合现实
人工智能·python·深度学习·transformer·迁移学习
通信仿真爱好者2 小时前
第【139】期--基于深度学习的OFDM信道估计方法研究--python完整代码
python·深度学习·ofdm·信道估计
威哥爱编程2 小时前
模拟面试智能体开发实战 07:答完一题就点评一题,结构化输出给分建议,让每道题都变成复盘
python·agent·harmonyos
cpolar技术支持2 小时前
中文文件名排序跟拼音对不上:os.listdir 返回的是文件系统顺序,三个中文名按字节排结果正好相反
python·文件系统·cpolar·编码·排序
Thanks_ks2 小时前
【第 008 讲】在终端中运行 Python 程序:交互模式与脚本模式 | 绝对路径与相对路径 | 以模块方式运行 | 常见报错的识别与排查
python·异常处理·终端·脚本·命令行·cmd·python 解释器
威哥爱编程2 小时前
模拟面试智能体开发实战 08:总评报告先过三道闸,防泄题防夸大防错建议,才敢发到候选人手上
python·agent·harmonyos