I recently wrote a code for a file transfer in Python. Sockets connect fine when I connect them from different terminals on the same system. But the same doesn’t seem to work when I connect them from different computers which are connected over the same Wifi network. Here’s the server code:
import os import socket # Creating a socket sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.bind(("192.164.X.X",2222)) sock.listen(5) print("Host Name: " , sock.getsockname()) # Accepting the connection client , addr = sock.accept() # Getting file details file_name = input("File Name:") file_size = os.path.getsize(file_name) # Sending file name and details 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 while c <= file_size: data = file.read(1024) if not (data): break client.sendall(data) c += len(data) # closing the socket sock.close()
Here’s my client code:
import os import socket host = input("Host Name: " ) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Trying to connect to socket sock.connect((host,2222)) print("Connected Successfully") # send file details file_name = sock.recv(100).decode() file_size = sock.recv(100).decode() with open("./rec/" + file_name , "wb") as file: c = 0 while c <= int(file_size): data = sock.recv(1024) if not (data): break file.write(data) c += len(data) sock.close()
When I try to connect The client From a different computer I get this error :
while c <= int(file_size): ValueError: invalid literal for int() with base 10: '3hin'
The file I am trying to transfer has a single word ‘hi’.
File transfer works correctly from different terminals on same machine. But the same doesn’t work on different computers which are connected over the same wifi network.
I understand the error (trying to convert string
to int
) but I don’t WHY it’s happening and how to fix it.
Advertisement
Answer
Your server code is sending a single TCP packet containing the content of multiple client.send()
calls. This is commonly known as “corking”, and can usually be disabled (depending on your OS) using the socket.TCP_NODELAY
socket option after accepting the connection.
client, addr = sock.accept() client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
This is however not guaranteed to work, and depends on your OS and OS settings.
The real solution would be to create a more robust protocol and avoid relying on data being sent in different packets. In fact, this is the only sane way of implementing any protocol based on TCP. Never rely on data being split in packets in a specific way.
Decide a fixed size for encoding and sending lengths, then do the following on the server:
- Send a length (of fixed size, for example 8 characters or 8 bytes, or whatever you would like) for the file name.
- Send the filename.
- Send the file size (again of fixed size).
- Send the file contents.
While on the client:
- Receive exactly 8 bytes and decode the length.
- Receive exactly length bytes for the filename.
- Receive exactly 8 bytes and decode the file size.
- Receive exactly size bytes for the file contents.
Most importantly, note that the .recv()
method of sockets can return less than the requested amount (you seem to already know that), so whatever kind of receiving operation you need to do, you will need to accumulate data in a loop until you have received the expected amount, for example:
expected = 100 data = b'' while len(data) < expected: data += sock.recv(expected - len(data))