The following code establishes a client-server connection between two Python programs via the socket
standard module and sends a file from the client to the server. The file transfer logic is contained in two functions: the client defines a send_file()
function to send a file through a socket and inversely the server defines receive_file()
to receive it. This way the functions can be easily moved from this post to other programs that already implement a client-server connection. The code is ready to send any file format and size. Python 3.8 or greater is required.
You can download both Python files and a test file to be transfered from here: send-file-via-socket.zip.
Server:
|
# server.py
|
|
|
|
import socket
|
|
import struct
|
|
|
|
|
|
def receive_file_size(sck: socket.socket):
|
|
# This funcion makes sure that the bytes which indicate
|
|
# the size of the file that will be sent are received.
|
|
# The file is packed by the client via struct.pack(),
|
|
# a function that generates a bytes sequence that
|
|
# represents the file size.
|
|
fmt = "<Q"
|
|
expected_bytes = struct.calcsize(fmt)
|
|
received_bytes = 0
|
|
stream = bytes()
|
|
while received_bytes < expected_bytes:
|
|
chunk = sck.recv(expected_bytes - received_bytes)
|
|
stream += chunk
|
|
received_bytes += len(chunk)
|
|
filesize = struct.unpack(fmt, stream)[0]
|
|
return filesize
|
|
|
|
|
|
def receive_file(sck: socket.socket, filename):
|
|
# First read from the socket the amount of
|
|
# bytes that will be received from the file.
|
|
filesize = receive_file_size(sck)
|
|
# Open a new file where to store the received data.
|
|
with open(filename, "wb") as f:
|
|
received_bytes = 0
|
|
# Receive the file data in 1024-bytes chunks
|
|
# until reaching the total amount of bytes
|
|
# that was informed by the client.
|
|
while received_bytes < filesize:
|
|
chunk = sck.recv(1024)
|
|
if chunk:
|
|
f.write(chunk)
|
|
received_bytes += len(chunk)
|
|
|
|
|
|
with socket.create_server(("localhost", 6190)) as server:
|
|
print("Waiting for the client...")
|
|
conn, address = server.accept()
|
|
print(f"{address[0]}:{address[1]} connected.")
|
|
print("Receiving file...")
|
|
receive_file(conn, "received-image.png")
|
|
print("File received.")
|
|
|
|
|
|
print("Connection closed.")
|
Client:
|
# client.py
|
|
|
|
import os
|
|
import socket
|
|
import struct
|
|
|
|
|
|
def send_file(sck: socket.socket, filename):
|
|
# Get the size of the outgoing file.
|
|
filesize = os.path.getsize(filename)
|
|
# First inform the server the amount of
|
|
# bytes that will be sent.
|
|
sck.sendall(struct.pack("<Q", filesize))
|
|
# Send the file in 1024-bytes chunks.
|
|
with open(filename, "rb") as f:
|
|
while read_bytes := f.read(1024):
|
|
sck.sendall(read_bytes)
|
|
|
|
|
|
with socket.create_connection(("localhost", 6190)) as conn:
|
|
print("Connected to the server.")
|
|
print("Sending file...")
|
|
send_file(conn, "image.png")
|
|
print("Sent.")
|
|
|
|
print("Connection closed.")
|
To test the code, make sure to modify the send_file()
and receive_file()
calls with the path of the file you want to send and the path where you want to receive it. In the previous code there is a file called image.png
in the same folder where both Python files are located that is received as received-image.png
. Once you've adjusted the file names, first run the server:
python server.py
Then run the client in another terminal:
python client.py
The server output will look like this:
Waiting for the client...
127.0.0.1:60331 connected.
Receiving file...
File received.
Connection closed.
And client output:
Connected to the server.
Sending file...
Sent.
Connection closed.