Python Client Server UDP File Transfer with ACKs - python

As an exercise for the university, I have to create a client-server application that allows you to send files from client to server and vice versa. The only part that I cannot implement concerns the sending of the ACKs (that is required) to confirm the correct reception of the packet.
Regarding what I did so far, the code works fine.
How can I implement it?
Here is the send and receive parts of code:
def send_file(address, chosen_file_name):
# Server information
host, port = address
# File buffer
buffer_size = 4096 * 10
# Transfer file name
filename = chosen_file_name
# File size
file_size = os.path.getsize(filename)
# Create socket link
s = sk.socket(sk.AF_INET, sk.SOCK_DGRAM)
s.setsockopt(sk.SOL_SOCKET, sk.SO_REUSEADDR, 1)
print(f'Server connection {host}:{port}')
s.connect((host, port))
print('Successful connection to server')
# Send file name and size, which must be encoded
sleep(1)
s.send(f'{filename}{Separator}{file_size}'.encode('utf-8'))
# File transfer
progress = tqdm.tqdm(range(file_size), f'Send {filename}', unit='B', unit_divisor=1024)
with open(filename, 'rb') as f:
# Read the file
for _ in progress:
bytes_read = f.read(buffer_size)
if not bytes_read:
print('Exit transmission, transmission is complete!')
s.sendall('file_upload_exit'.encode('utf-8'))
break
s.sendall(bytes_read)
progress.update(len(bytes_read))
sleep(0.001)
# Close resources
s.close()
def receive_file(address):
# File buffer
buffer_size = 4096 * 10
udp_socket = sk.socket(sk.AF_INET, sk.SOCK_DGRAM)
udp_socket.setsockopt(sk.SOL_SOCKET, sk.SO_REUSEADDR, 1)
udp_socket.bind(address)
recv_data = udp_socket.recvfrom(buffer_size)
recv_file_info = recv_data[0].decode('utf-8') # Storing the received data, Filename
c_address = recv_data[1] # Storing the address information of the customer
print(f'Client {c_address} Connect')
chosen_file_name, file_size = recv_file_info.split(Separator)
# Get the name of the file, Size
chosen_file_name = os.path.basename(chosen_file_name)
file_size = int(file_size)
# File receiving processing
progress = tqdm.tqdm(range(file_size), f'Receive {chosen_file_name}', unit='B', unit_divisor=1024, unit_scale=True)
with open('r_' + chosen_file_name, 'wb') as f:
for _ in progress:
# Read data from client
bytes_read = udp_socket.recv(buffer_size)
# If there is no data transfer content
if bytes_read == b'file_download_exit':
print('Complete transmission!')
break
# Read and write
f.write(bytes_read)
# Update progress bar
progress.update(len(bytes_read))
udp_socket.close()

Related

TCP transfer files without closing socket

I coded a simple TCP server - client file sharing but it only work when the script close the socket in the client side.
I tried to remove the s.close inside the client python script and it gave me an error. Any idea?
I dont know if it even possible to do this kind of things with TCP without closing socket.
here is my code
server.py
import socket
import tqdm
import os
SERVER_HOST = "192.168.1.48"
SERVER_PORT = 5001
BUFFER_SIZE = 4096
SEPARATOR = "<SEPARATOR>"
s = socket.socket()
s.bind((SERVER_HOST, SERVER_PORT))
s.listen(5)
print(f"[*] Listening as {SERVER_HOST}:{SERVER_PORT}")
client_socket, address = s.accept()
print(f"[+] {address} is connected.")
def receive_file(filename):
received = client_socket.recv(BUFFER_SIZE).decode()
filename, filesize = received.split(SEPARATOR)
filename = os.path.basename(filename)
filesize = int(filesize)
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))
while True:
command = input("-->")
client_socket.send(str.encode(command))
filename = command[9:]
receive_file(filename)
client.py
import socket
import tqdm
import os
SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096 # send 4096 bytes each time step
host = "192.168.1.48"
port = 5001
filesize = os.path.getsize(filename)
s = socket.socket()
print(f"[+] Connecting to {host}:{port}")
s.connect((host, port))
print("[+] Connected.")
def send_file(filename,filesize):
s.send(f"{filename}{SEPARATOR}{filesize}".encode())
progress = tqdm.tqdm(range(filesize), f"Sending {filename}", unit="B", unit_scale=True, unit_divisor=1024)
with open(filename, "rb") as f:
while True:
bytes_read = f.read(BUFFER_SIZE)
if not bytes_read:
# file transmitting is done
break
s.sendall(bytes_read)
progress.update(len(bytes_read))
# close the socket
s.close()
while True:
command = s.recv(4096)
if command[:8].decode("utf-8") == "download":
print("yes it start")
filename = command[9:].decode("utf-8")
print(filename)
filesize = os.path.getsize(filename)
send_file(filename,filesize)
Break out of the loop when you've received filesize bytes.
with open(filename, "wb") as f:
total_bytes = 0
while total_bytes < filesize:
# 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)
total_bytes += len(bytes_read)
# update the progress bar
progress.update(len(bytes_read))

How to transfer more files rather than just one using python?

I made a file transferrer and the problem is that out of unknown reason it lets me only send one file... When I try the second time the program works fine, but the file doesn't show up in directory. It would mean a lot to me if someone helped me out.
Code for sender:
import os
import shutil
import socket
import time
# Creating a socket.
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((socket.gethostname(), 22222))
sock.listen(5)
print("Host Name: ", sock.getsockname())
# Accepting the connection.
client, addr = sock.accept()
original = ""
print("When you want to stop with file uploading type -> STOP <-")
while (1):
original = input(str("Filepath:"))
if (original!="STOP"):
filename = os.path.basename(original)
target = r'C:\Users\Gasper\Desktop\transfer\filename'
path = target.replace('filename', filename)
new_file = shutil.copy(original, path)
# Getting file details.
file_name = filename
file_size = os.path.getsize(file_name)
# Sending file_name and detail.
client.send(file_name.encode())
client.send(str(file_size).encode())
# Opening file and sending data.
with open(file_name, "rb") as file:
c = 0
# Running loop while c != file_size.
while c <= file_size:
data = file.read(1024)
if not data:
break
client.sendall(data)
c += len(data)
os.remove(filename)
else:
break
print("File Transfer Complete!")
input("Press enter to exit...")
# Closing the socket.
sock.close()
Code for receiver:
import socket
import time
host = input("Host Name: ")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Trying to connect to socket.
try:
sock.connect((host, 22222))
print("Connected Successfully")
except:
print("Unable to connect")
exit(0)
# Send file details.
file_name = sock.recv(100).decode()
file_size = sock.recv(100).decode()
# Opening and reading file.
with open("./rec/" + file_name, "wb") as file:
c = 0
# Starting the time capture.
start_time = time.time()
# Running the loop while file is recieved.
while c <= int(file_size):
data = sock.recv(1024)
if not data:
break
file.write(data)
c += len(data)
# Ending the time capture.
end_time = time.time()
print("File transfer Complete!")
input("Press enter to exit...")
# Closing the socket.
sock.close()
Example:
Filepath: C\Users\Admin\Desktop\Directory\File1.txt(I put in the first file path and it transfers successfully)
Filepath: C\Users\Admin\Desktop\Directory\File2.txt(I put in the second file path and it doesnt transfer at all)

Python Sockets multiple file transfers

So I have a python program that basically let's the client connect to the server and send it an excel file which is used as an input for an optimization problem. I then want the server to send the output of this optimization (also an excel file) back to the client. The model itself takes about a minute to solve, and I think this is causing some issues with the client trying to receive the output 'too early'.
The client code:
SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096
HEADER = 64
HEADERSIZE = 10
port = 1234
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
host = "123.45.678.910"
s = socket.socket()
s.connect((host, port))
filename = "input/Model_Input.xlsx"
filesize = os.path.getsize(filename)
s.send(f"{filename}{SEPARATOR}{filesize}".encode())
with open(filename, "rb") as f:
while True:
bytes_read = f.read(BUFFER_SIZE)
if not bytes_read:
break
s.sendall(bytes_read)
out_received = s.recv(BUFFER_SIZE).decode()
out_filename, out_filesize = out_received.split(SEPARATOR)
out_filename = os.path.basename(out_filename)
out_filesize = int(out_filesize)
with open(out_filename, "wb") as h:
while True:
out_bytes_read = s.recv(BUFFER_SIZE)
if not out_bytes_read:
break
h.write(out_bytes_read)
And the server code:
SERVER_PORT = 1234
SERVER_HOST = socket.gethostbyname(socket.gethostname())
BUFFER_SIZE = 4096
SEPARATOR = "<SEPARATOR>"
s = socket.socket()
s.bind((SERVER_HOST, SERVER_PORT))
s.listen(5)
client_socket, address = s.accept()
received = client_socket.recv(BUFFER_SIZE).decode()
filename, filesize = received.split(SEPARATOR)
filename = os.path.basename(filename)
filesize = int(filesize)
with open(filename, "wb") as f:
while True:
bytes_read = client_socket.recv(BUFFER_SIZE)
if not bytes_read:
break
f.write(bytes_read)
##################
## MODEL CODE ##
##################
outfilename = 'Model_Output.xlsx'
outfilesize = os.path.getsize(filename)
client_socket.send(f"{outfilename}{SEPARATOR}{outfilesize}".encode())
with open(outfilename, "rb") as h:
while True:
# read the bytes from the file
bytes_readed = h.read(BUFFER_SIZE)
if not bytes_readed:
break
client_socket.sendall(bytes_readed)
I am able to send the input file to the server and get the model to run, and save the output to storage. However, as soon as I add in the part to try send it back to the client, it gets stuck. It still sends and receives the input file successfully, but then the model never runs. Neither the client nor the server disconnect, they both just seem to be stuck.
Thank you
I can be very difficult for someone (i.e. me) to remotely debug this type of code, so I can't really point to a particular line of code that is necessarily the problem. If, however, your client and server are running on the same machine, then there is a potential problem in the client code that begins:
with open(out_filename, "wb") as h:
while True:
out_bytes_read = s.recv(BUFFER_SIZE)
if not out_bytes_read:
break
h.write(out_bytes_read)
When the open is executed, this sets the file size to 0. The server, meanwhile is reading this file to transmit it to the file and can possibly find that there are now only 0 bytes. But it has already sent a "header" saying that there are N bytes where N is non-zero. But that is a different problem than the one you describe. But it could be happening in the other direction, also. That is, when the client is sending the file and the server is opening the file for output, it is now zeroing out the file that the client is still reading. The code below solves that problem in both directions. Of course, if your client and server are on different computers not accessing the same files concurrently, then what I have described is not an issue. Not yet, anyway.
I can, however, offer a slightly different approach, which does seem to work:
See the Using a Socket section from the Socket Programming HOWTO article in the Python 3 Manual. I have adopted the suggestion to use fixed length messages. It's a little more laborious, perhaps, but also a bit more fullproof. That means that if you want to transmit the filename, you have to first transmit the length of the encoded filename as fixed length length message (3 bytes can handle encoded filenames up to 999 bytes in length) and then you can transmit the encode filename. Similarly, we transmit the length of a file as a 9 byte length (left-padded with zeroes), which can handle file sizes up to 999,999,999 bytes (I set the width of 9 arbitrarily). I have two functions, receive_msg and send_msg that will robustly send and receive complete byte messages and can be used by both the client and server. These are modeled on the MySocket.mysend and MySocket.myreceive methods from the article.
I assumed that the server should be able to handle more than one request before terminating. In fact, it should be able to handle requests concurrently. To that end the server passes a request to a thread pool worker function, process_request, for processing. It wasn't clear what the nature of the so-called "Model Code" was. Assuming the function that performs this computation, process_model, is CPU-intensive, process_requestis passed a multiprocessing pool instance that can be used to perform the process_model processing so that the CPU-intensive portion of processing will not be limited by the Global Interpreter Lock. If there is no real CPU-intensive processing involved, then remove the code that created the multiprocessing pool and then call process_model as a regular function.
Server Code
import socket
from multiprocessing.pool import Pool, ThreadPool
import os.path
BUFFER_SIZE = 4096
SERVER_PORT = 1234
SERVER_HOST = socket.gethostbyname(socket.gethostname())
def receive_msg(sock, msg_length):
chunks = []
bytes_recd = 0
while bytes_recd < msg_length:
chunk = sock.recv(min(msg_length - bytes_recd, BUFFER_SIZE))
if chunk == b'':
raise RuntimeError("socket connection broken")
chunks.append(chunk)
bytes_recd = bytes_recd + len(chunk)
return b''.join(chunks)
def send_msg(sock, msg):
msg_length = len(msg)
totalsent = 0
while totalsent < msg_length:
sent = sock.send(msg[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
def server():
s = socket.socket()
s.bind((SERVER_HOST, SERVER_PORT))
s.listen(5)
process_pool = Pool(5)
thread_pool = ThreadPool(5)
while True:
client_socket, address = s.accept()
thread_pool.apply_async(process_request, args=(process_pool, client_socket))
def process_request(process_pool, s):
# Fixed length fields:
# width 3 for filename length, followed by filename, width 9 for filesize
filename_size = int(receive_msg(s, 3).decode())
filename = receive_msg(s, filename_size).decode()
filename = os.path.basename(filename)
filesize = int(receive_msg(s, 9).decode())
msg = receive_msg(s, filesize)
with open(filename, "wb") as f:
f.write(msg)
# Assuming processing the model is CPU-intensive,
# we use a process pool for doing that:
out_filename = process_pool.apply(process_model, args=(filename,))
out_filesize = os.path.getsize(out_filename)
encoded_filename = out_filename.encode()
msg1 = b"%03d%s%09dfilesize" % (len(encoded_filename), encoded_filename, out_filesize)
with open(out_filename, "rb") as h:
msg2 = h.read()
send_msg(s, msg1)
send_msg(s, msg2)
def process_model(filename):
...
# Returned filename should probably be a function of the passed filename
return 'Model_Output.xlsx' # name of the output file
if __name__ == '__main__':
server()
Client Code
import socket
import os.path
BUFFER_SIZE = 4096
port = 1234
host = "123.45.678.910"
def receive_msg(sock, msg_length):
chunks = []
bytes_recd = 0
while bytes_recd < msg_length:
chunk = sock.recv(min(msg_length - bytes_recd, BUFFER_SIZE))
if chunk == b'':
raise RuntimeError("socket connection broken")
chunks.append(chunk)
bytes_recd = bytes_recd + len(chunk)
return b''.join(chunks)
def send_msg(sock, msg):
msg_length = len(msg)
totalsent = 0
while totalsent < msg_length:
sent = sock.send(msg[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
def client():
s = socket.socket()
s.connect((host, port))
filename = "input/Model_Input.xlsx"
filesize = os.path.getsize(filename)
# Fixed length fields:
# width 3 for filename length, followed by filename, width 9 for filesize
encoded_filename = filename.encode()
msg1 = b"%03d%s%09dfilesize" % (len(encoded_filename), encoded_filename, filesize)
with open(filename, "rb") as f:
msg2 = f.read()
send_msg(s, msg1)
send_msg(s, msg2)
out_filename_size = int(receive_msg(s, 3).decode())
out_filename = receive_msg(s, out_filename_size).decode()
out_filename = os.path.basename(out_filename)
out_filesize = int(receive_msg(s, 9).decode())
msg = receive_msg(s, out_filesize)
with open(out_filename, "wb") as h:
h.write(msg)
if __name__ == '__main__':
client()
Update
The entire programming can greatly be simplified by implementing the service as a Remote Procedure Call. The code is based on Python Cookbook, 3rd Edition:
Server
import socket
import pickle
from multiprocessing.connection import Listener
from threading import Thread
from multiprocessing.pool import Pool
import os.path
SERVER_PORT = 1234
SERVER_HOST = socket.gethostbyname(socket.gethostname())
class RPCHandler:
def __init__(self):
self._functions = { }
def register_function(self, func):
self._functions[func.__name__] = func
def handle_connection(self, connection):
try:
while True:
# Receive a message
func_name, args, kwargs = pickle.loads(connection.recv())
# Run the RPC and send a response
try:
r = self._functions[func_name](*args,**kwargs)
connection.send(pickle.dumps(r))
except Exception as e:
connection.send(pickle.dumps(e))
except EOFError:
pass
def server():
global process_pool
handler = RPCHandler()
handler.register_function(process_request)
sock = Listener((SERVER_HOST, SERVER_PORT))
process_pool = Pool(5)
while True:
client = sock.accept()
t = Thread(target=handler.handle_connection, args=(client,))
t.daemon = True
t.start()
def process_request(filename, contents):
filename = os.path.basename(filename)
with open(filename, "wb") as f:
f.write(contents)
# Assuming processing the model is CPU-intensive,
# we use a process pool for doing that:
out_filename = process_pool.apply(process_model, args=(filename,))
with open(out_filename, "rb") as h:
out_contents = h.read()
return (out_filename, out_contents)
def process_model(filename):
...
# Returned filename should probably be a function of the passed filename
return 'Model_Output.xlsx' # name of the output file
if __name__ == '__main__':
server()
Client
import pickle
import socket
import os.path
from multiprocessing.connection import Client
port = 1234
host = "123.45.678.910"
class RPCProxy:
def __init__(self, connection):
self._connection = connection
def __getattr__(self, name):
def do_rpc(*args, **kwargs):
self._connection.send(pickle.dumps((name, args, kwargs)))
result = pickle.loads(self._connection.recv())
if isinstance(result, Exception):
raise result
return result
return do_rpc
def client():
c = Client((host, port))
proxy = RPCProxy(c)
filename = "input/Model_Input.xlsx"
with open(filename, "rb") as f:
contents = f.read()
(out_filename, out_contents) = proxy.process_request(filename, contents)
out_filename = os.path.basename(out_filename)
with open(out_filename, "wb") as h:
h.write(out_contents)
if __name__ == '__main__':
client()

Sending Multiple Files Python Using Socket

I currently am trying to create a client-server application in which the client can send multiple files to the server using TCP protocol. The server will eventually create a hash-algorithm and send it back to the client but I am running into issues sending multiple files from the client to the server. In it's current form, the first file sends correctly but the files after encounter an error where the information is merged together. IE the file size is listed as the second file's name. I am a javascript dude and very new to python so an explanation to how I can make this happen would be much appreciated. I believe threading is the answer but with my limited understanding of python, I do not know how to make this work. Currently I can send one file at a time and the server stays open. However, I would like to enter several file names from my current directory and have them processed. I eventually will convert the entire client side into C but I am struggling to get the server to work correctly in python. Any advice would be much appreciated!
Server.py
import socket
import hashlib
import threading
import struct
HOST = '127.0.0.1'
PORT = 2345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(10)
print("Waiting for a connection.....")
conn, addr = s.accept()
print("Got a connection from ", addr)
while True:
hash_type = conn.recv(1024)
print('hash type: ', hash_type)
if not hash_type:
break
file_name = conn.recv(1024)
print('file name: ', file_name)
file_size = conn.recv(1024)
file_size = int(file_size, 2)
print('file size: ', file_size )
f = open(file_name, 'wb')
chunk_size = 4096
while file_size > 0:
if file_size < chunk_size:
chuk_size = file_size
data = conn.recv(chunk_size)
f.write(data)
file_size -= len(data)
f.close()
print('File received successfully')
s.close()
Client.py
import socket
import threading
import os
HOST = '127.0.0.1'
PORT = 2345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
hash_type = input('Enter hash type: ')
files = input('Enter file(s) to send: ')
files_to_send = files.split()
for file_name in files_to_send:
s.send(hash_type.encode())
print(file_name)
s.send(file_name.encode())
file_size = os.path.getsize(file_name)
file_size = bin(file_size)
print(file_size)
s.send(file_size.encode())
f = open(file_name, 'rb')
l = f.read()
while(l):
s.send(l)
l = f.read()
f.close()
print('File Sent')
s.close()
One way to handle what you're doing is to buffer your socket data. Below is a class that buffers data and knows how to send and receive null-terminated, UTF-8-encoded strings, and raw chunks of bytes:
buffer.py:
class Buffer:
def __init__(self,s):
'''Buffer a pre-created socket.
'''
self.sock = s
self.buffer = b''
def get_bytes(self,n):
'''Read exactly n bytes from the buffered socket.
Return remaining buffer if <n bytes remain and socket closes.
'''
while len(self.buffer) < n:
data = self.sock.recv(1024)
if not data:
data = self.buffer
self.buffer = b''
return data
self.buffer += data
# split off the message bytes from the buffer.
data,self.buffer = self.buffer[:n],self.buffer[n:]
return data
def put_bytes(self,data):
self.sock.sendall(data)
def get_utf8(self):
'''Read a null-terminated UTF8 data string and decode it.
Return an empty string if the socket closes before receiving a null.
'''
while b'\x00' not in self.buffer:
data = self.sock.recv(1024)
if not data:
return ''
self.buffer += data
# split off the string from the buffer.
data,_,self.buffer = self.buffer.partition(b'\x00')
return data.decode()
def put_utf8(self,s):
if '\x00' in s:
raise ValueError('string contains delimiter(null)')
self.sock.sendall(s.encode() + b'\x00')
With this class, your client and server become:
client.py:
import socket
import threading
import os
import buffer
HOST = '127.0.0.1'
PORT = 2345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
with s:
sbuf = buffer.Buffer(s)
hash_type = input('Enter hash type: ')
files = input('Enter file(s) to send: ')
files_to_send = files.split()
for file_name in files_to_send:
print(file_name)
sbuf.put_utf8(hash_type)
sbuf.put_utf8(file_name)
file_size = os.path.getsize(file_name)
sbuf.put_utf8(str(file_size))
with open(file_name, 'rb') as f:
sbuf.put_bytes(f.read())
print('File Sent')
server.py:
import socket
import os
import buffer
HOST = ''
PORT = 2345
# If server and client run in same local directory,
# need a separate place to store the uploads.
try:
os.mkdir('uploads')
except FileExistsError:
pass
s = socket.socket()
s.bind((HOST, PORT))
s.listen(10)
print("Waiting for a connection.....")
while True:
conn, addr = s.accept()
print("Got a connection from ", addr)
connbuf = buffer.Buffer(conn)
while True:
hash_type = connbuf.get_utf8()
if not hash_type:
break
print('hash type: ', hash_type)
file_name = connbuf.get_utf8()
if not file_name:
break
file_name = os.path.join('uploads',file_name)
print('file name: ', file_name)
file_size = int(connbuf.get_utf8())
print('file size: ', file_size )
with open(file_name, 'wb') as f:
remaining = file_size
while remaining:
chunk_size = 4096 if remaining >= 4096 else remaining
chunk = connbuf.get_bytes(chunk_size)
if not chunk: break
f.write(chunk)
remaining -= len(chunk)
if remaining:
print('File incomplete. Missing',remaining,'bytes.')
else:
print('File received successfully.')
print('Connection closed.')
conn.close()
Demo
client:
Enter hash type: abc
Enter file(s) to send: demo1.dat demo2.dat
demo1.dat
File Sent
demo2.dat
File Sent
server:
Waiting for a connection.....
Got a connection from ('127.0.0.1', 22126)
hash type: abc
file name: uploads\demo1.dat
file size: 488892
File received successfully.
hash type: abc
file name: uploads\demo2.dat
file size: 212992
File received successfully.
Connection closed.
1.
file_size = conn.recv(1024)
In your server code you read 1024 bytes as your file_size, file_size is only 4 or 8 bytes long
2.
file_name = conn.recv(1024) Your server don't know how long the filename/hashtype is.
-> Use a long for both sizes and read only sizeof(long) bytes from the stream.
You can use https://docs.python.org/2/library/struct.html for packing/encoding of these numbers
-> Or just go the easy way and use https://docs.python.org/3/library/pickle.html for serialization

Simple Python Server Client File Transfer

I have this simple python server-client file transfer project going on.
There are two parts to each side. First, the client sends a file to server for the first part. Server then appends a line and sends back the file to client in the second part.
My issue is that for some reason, the server code is stuck on receiving whenever I have the return file code in it. If I should comment out the second section of the code, the server receives all the file sent by client. Otherwise it freezes on receiving. And yes, client did send it.
You can ignore all the print commands, just there to see where the problem is.
servercode:
import socket
ssFT = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssFT.bind((socket.gethostname(), 8756))
ssFT.listen(1)
while True:
(conn, address) = ssFT.accept()
text_file = 'fileProj.txt'
#Receive, output and save file
with open(text_file, "wb") as fw:
print("Receiving..")
while True:
print('receiving')
data = conn.recv(1024)
print('Received: ', data.decode('utf-8'))
if not data:
print('Breaking from file write')
break
fw.write(data)
print('Wrote to file', data.decode('utf-8'))
fw.close()
print("Received..")
#Append and send file
print('Opening file ', text_file)
with open(text_file, 'ab+') as fa:
print('Opened file')
print("Appending string to file.")
string = b"Append this to file."
fa.write(string)
fa.seek(0, 0)
print("Sending file.")
while True:
data = fa.read(1024)
conn.send(data)
if not data:
break
fa.close()
print("Sent file.")
break
ssFT.close()
client code:
import socket
csFT = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
csFT.connect((socket.gethostname(), 8756))
text_file = 'passphrase.txt'
#Send file
with open(text_file, 'rb') as fs:
#Using with, no file close is necessary,
#with automatically handles file close
while True:
data = fs.read(1024)
print('Sending data', data.decode('utf-8'))
csFT.send(data)
print('Sent data', data.decode('utf-8'))
if not data:
print('Breaking from sending data')
break
fs.close()
#Receive file
print("Receiving..")
with open(text_file, 'wb') as fw:
while True:
data = csFT.recv(1024)
if not data:
break
fw.write(data)
fw.close()
print("Received..")
csFT.close()
I tested your code locally with Python 3.
The problem that I saw was with conn.recv in the server code. Because of the conn.recv blocks the connection and it is waiting for more data.
The solution that I found was sending commands to server informing about BEGIN and END of the data transfer, like that:
client.py
#Send file
with open(text_file, 'rb') as fs:
#Using with, no file close is necessary,
#with automatically handles file close
csFT.send(b'BEGIN')
while True:
data = fs.read(1024)
print('Sending data', data.decode('utf-8'))
csFT.send(data)
print('Sent data', data.decode('utf-8'))
if not data:
print('Breaking from sending data')
break
csFT.send(b'ENDED') # I used the same size of the BEGIN token
fs.close()
server.py
with open(text_file, "wb") as fw:
print("Receiving..")
while True:
print('receiving')
data = conn.recv(32)
if data == b'BEGIN':
continue
elif data == b'ENDED':
print('Breaking from file write')
break
else:
print('Received: ', data.decode('utf-8'))
fw.write(data)
print('Wrote to file', data.decode('utf-8'))
fw.close()
print("Received..")
Client.py complete code:
https://pastebin.com/LySsgEe4
Server.py complete code:
https://pastebin.com/KADZpqkM
I hope to help!
The way I solved this same issue is by sending the file size first, then the server can stop waiting as soon as it receives the whole file. I dunno whether there is a better solution or not but this works like a charm:
BUFFER_SIZE = 1024
client.py
import os
fsize = os.path.getsize(text_file)
csFT.send(str(fsize).encode('utf-8'))
with open(text_file, 'rb') as fs:
data = fs.read(BUFFER_SIZE)
while data:
csFT.send(data)
data = fs.read(BUFFER_SIZE)
server.py
with open(text_file, 'wb') as fw:
msg = ssFT.recv(BUFFER_SIZE)
fsize = int(msg.decode('utf-8'))
rsize = 0
while True:
data = ssFT.recv(BUFFER_SIZE)
rsize = rsize + len(data)
fw.write(data)
if rsize >= fsize:
print('Breaking from file write')
break

Categories