如何使用Python中的套接字在网络中传输文件?

2021年11月16日20:46:49 发表评论 875 次浏览

Python如何使用套接字传输文件?编写服务器和客户端 Python 脚本,使用 Python 中的套接字模块在网络中接收和发送文件。

文件传输是通过网络或 Internet 连接将文件从一台计算机复制或移动到另一台计算机的过程。在本教程中,我们将逐步介绍如何编写处理该问题的客户端/服务器 Python 脚本。

基本思想是创建一个监听特定端口的服务器,这个服务器将负责接收文件(你可以让服务器也发送文件)。另一方面,客户端将尝试连接到服务器并发送任何类型的文件。

Python如何在网络中传输文件?我们将使用Python 内置的socket模块,它为我们提供了在 Internet 上广泛使用的 socket 操作,因为它们支持与任何网络的任何连接。

相关: 如何在 Python 中发送电子邮件。

首先,我们需要安装tqdm库,这将使我们能够打印精美的进度条:

pip3 install tqdm

客户端代码

Python如何使用套接字传输文件?让我们从客户端开始,发送者:

import socket
import tqdm
import os

SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096 # send 4096 bytes each time step

Python套接字传输文件示例 - 我们需要指定要连接的服务器的 IP 地址和端口,以及要发送的文件的名称。

# the ip address or hostname of the server, the receiver
host = "192.168.1.101"
# the port, let's use 5001
port = 5001
# the name of file we want to send, make sure it exists
filename = "data.csv"
# get the file size
filesize = os.path.getsize(filename)

该文件名必须在当前目录中存在,或者你也可以使用绝对路径到你的计算机上文件的某处。这是你要发送的文件。

os.path.getsize(filename)以字节为单位获取该文件的大小,这很好,因为我们需要它来在客户端和服务器中打印进度条。

Python如何在网络中传输文件?让我们创建 TCP 套接字:

# create the client socket
s = socket.socket()

连接到服务器:

print(f"[+] Connecting to {host}:{port}")
s.connect((host, port))
print("[+] Connected.")

connect()方法需要一对(主机、端口)的地址来将套接字连接到该远程地址。建立连接后,我们需要发送文件的名称和大小:

# send the filename and filesize
s.send(f"{filename}{SEPARATOR}{filesize}".encode())

我在这里使用SEPARATOR只是为了分隔数据字段,它只是一个垃圾消息,我们可以使用send()两次,但我们可能不想这样做。encode()函数对我们传递给'utf-8'编码的字符串进行编码(这是必要的)。

现在我们需要发送文件,在发送文件时,我们将使用tqdm库打印漂亮的进度条:

# start sending the file
progress = tqdm.tqdm(range(filesize), f"Sending {filename}", unit="B", unit_scale=True, unit_divisor=1024)
with open(filename, "rb") as f:
    while True:
        # read the bytes from the file
        bytes_read = f.read(BUFFER_SIZE)
        if not bytes_read:
            # file transmitting is done
            break
        # we use sendall to assure transimission in 
        # busy networks
        s.sendall(bytes_read)
        # update the progress bar
        progress.update(len(bytes_read))
# close the socket
s.close()

基本上我们在这里做的是打开以二进制读取的文件,从文件中读取块(在本例中为4096字节或4KB)并使用sendall()函数将它们发送到套接字,然后我们更新进度条每次,一旦完成,我们关闭该套接字。

相关: 如何在 Python 中制作聊天应用程序。

Python套接字传输文件示例:服务器代码

好的,我们已经完成了与客户的沟通。让我们深入研究服务器,打开一个新的空 Python 文件,然后:

import socket
import tqdm
import os
# device's IP address
SERVER_HOST = "0.0.0.0"
SERVER_PORT = 5001
# receive 4096 bytes each time
BUFFER_SIZE = 4096
SEPARATOR = "<SEPARATOR>"

我已经初始化了一些我们要使用的参数,注意我已经使用“0.0.0.0”作为服务器 IP 地址,这意味着本地机器上的所有 IPv4 地址。你可能想知道,为什么我们不只使用我们的本地 IP 地址或“localhost”或“127.0.0.1”?好吧,如果服务器有两个 IP 地址,让我们说一个网络上的“192.168.1.101”,另一个网络上的“10.0.1.1”,并且服务器侦听“0.0.0.0”,那么这两个 IP 都可以访问.

或者,你可以使用公共或私有 IP 地址,具体取决于你的客户端。如果连接的客户端在你的本地网络中,你应该使用你的私有 IP(你可以使用ipconfigWindows 中的ifconfig命令或Mac OS/Linux 中的命令来检查它),但如果你期望来自 Internet 的客户端,你绝对应该使用你的公共地址。

另外,请确保你在服务器中使用与客户端相同的端口。

Python如何使用套接字传输文件?让我们创建我们的 TCP 套接字:

# create the server socket
# TCP socket
s = socket.socket()

现在这与客户端不同,我们需要将刚刚创建的套接字绑定到我们的SERVER_HOST和SERVER_PORT:

# bind the socket to our local address
s.bind((SERVER_HOST, SERVER_PORT))

之后,我们将监听连接:

# enabling our server to accept connections
# 5 here is the number of unaccepted connections that
# the system will allow before refusing new connections
s.listen(5)
print(f"[*] Listening as {SERVER_HOST}:{SERVER_PORT}")

一旦客户端连接到我们的服务器,我们需要接受该连接:

# accept connection if there is any
client_socket, address = s.accept() 
# if below code is executed, that means the sender is connected
print(f"[+] {address} is connected.")

请记住,当客户端连接时,它会发送文件的名称和大小,让我们接收它们:

# receive the file infos
# receive using client socket, not server socket
received = client_socket.recv(BUFFER_SIZE).decode()
filename, filesize = received.split(SEPARATOR)
# remove absolute path if there is
filename = os.path.basename(filename)
# convert to integer
filesize = int(filesize)

如前所述,接收到的数据与文件名和文件大小相结合,我们可以通过SEPARATOR字符串拆分它们来轻松提取它们。

之后,我们需要删除文件的绝对路径,这是因为发送者发送的文件是他自己的文件路径,可能与我们的不同,os.path.basename()函数返回路径名的最后一部分.

Python套接字传输文件示例 - 现在我们需要接收文件:

# start receiving the file from the socket
# and writing to the file stream
progress = tqdm.tqdm(range(filesize), f"Receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)
with open(filename, "wb") as f:
    while True:
        # read 1024 bytes from the socket (receive)
        bytes_read = client_socket.recv(BUFFER_SIZE)
        if not bytes_read:    
            # nothing is received
            # file transmitting is done
            break
        # write to the file the bytes we just received
        f.write(bytes_read)
        # update the progress bar
        progress.update(len(bytes_read))

# close the client socket
client_socket.close()
# close the server socket
s.close()

Python如何使用套接字传输文件?与客户端代码没有太大区别。但是,我们在此处以二进制写入方式打开文件,并使用recv(BUFFER_SIZE)方法从客户端套接字接收BUFFER_SIZE字节并将其写入文件。完成后,我们关闭客户端和服务器套接字。

还学习: 如何使用 Python 列出 FTP 服务器中的所有文件和目录

好吧,让我在我自己的私人网络上试一试:

C:\> python receiver.py

[*] Listening as 0.0.0.0:5001

我需要转到我的 Linux 机器并发送一个示例文件:

root@rockikz:~/tools# python3 sender.py
[+] Connecting to 192.168.1.101:5001
[+] Connected.
Sending data.npy:   9%|███████▊                                                                            | 45.5M/487M [00:14<02:01, 3.80MB/s]

现在让我们看看服务器:

[+] ('192.168.1.101', 47618) is connected.
Receiving data.npy:  33%|███████████████████▍                                       | 160M/487M [01:04<04:15, 1.34MB/s]

太好了,我们完成了!

Python如何在网络中传输文件?你现在可以根据自己的需要扩展此代码,以下是你可以实现的一些示例:

  • 使服务器能够使用线程同时从多个客户端接收多个文件。
  • 在发送之前压缩文件,这可能有助于增加传输持续时间。
  • 在发送文件之前对其进行加密,以确保没有人能够拦截和读取该文件,本教程将有所帮助。
  • 通过检查两个文件(发送方的原始文件和接收方的发送文件)的校验和,确保文件正确发送。在这种情况下,你需要安全的散列算法来做到这一点。
  • 添加聊天室,以便你可以聊天和传输文件。
木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: