I suggest to transfer data structures use json
or pickle, although with json the serialization can be faster.
That being said, this question unintentionally becomes a bit of a blatant/challenging.
The description of the error itself talks about the problem, that is, you need to send bytes and not a tuple, and how you cannot convert a tuple
in bytes directly but can do it with strings, you can send a json (although in this case it becomes more laborious and so I will not do it, I explain below the reason).
However we still have a problem, you cannot send the bytes of the file to json because there are problems with serialization (list of the types of data that json understands), you’d have to send two separate messages, one with the adjacent information (name, file size etc. in the form of a Dict for example) and another with the bytes that compose it, and VERY IMPORTANT, you would have to send a flag (null byte for ex, b'\x00'
) at the end of the first to indicate that it has come to an end and that the server can then start receiving binary data from the file.
This flag must exist because TCP is a Protocol streaming there is no guarantee whatsoever that the method recv
(server side in this case) received exactly the json with the information before, may contain only a portion of json or may include some of the binary information from the file to the mix.
Here I put a basic example of file upoload for the server with pickle, where we can convert a data structure (objects, dictionaries, tuples, etc...) into a data structure byte stream, so with pickle, we can immediately send the necessary data "at once", so in this case it makes it easier:
Client:
import socket, pickle
f = input('escreva o nome do ficheiro')
f_bin = open(f, 'rb').read()
info = {'name': f, 'file': f_bin, 'opcao': 1} # ajustar opcao para adequar ao teu codigo
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('', 9005))
s.sendall(pickle.dumps(info))
Server:
import socket, threading, pickle, time
def run(conn):
info_bin = b''
st = time.time()
while True:
c = conn.recv(2048)
if not c:
break
info_bin += c
if time.time() - st >= 2: # opcional, informacao sobre o total ja carregado
print('bytes downloaded:', len(info_bin))
st = time.time()
info = pickle.loads(info_bin)
if info['file']:
dest = 'files/{}'.format(info['name'])
with open(dest, 'wb') as f:
f.write(info['file'])
print('success on receiving and saving {} for {}'.format(info['name'], conn.getpeername()))
conn.close()
host, port = ('', 9005)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(5)
while True:
conn, addr = sock.accept()
print('conn', addr)
threading.Thread(target=run, args=(conn,)).start()
I have not tested with multiple clients but in principle will also work.
You must adjust your connection and where the server will write the file
How the object is defined
socket_object
? Use the packagesocket
python?– Woss
socket_object = socket(AF_INET, SOCK_STREAM)
– Ricardo Mendes
First, I believe you can’t send a tuple through the socket, just a string, so you can convert it to JSON before, for example:
socket_object.sendall(str.encode(json.dumps(tupla)))
, or something similar.– Woss
I made a mistake
NameError: name 'json' is not defined
, I’m working with Ubuntu terminals and his standard python3– Ricardo Mendes
You need to include the package
import json
– Woss
I managed to send it through the socket, but I could not recover them on the server, I used it
data = socket_object.recv(1024)
 data = (str.decode(json.dumps()))
– Ricardo Mendes
And I got this error on the server
OSError: [Errno 107] Transport endpoint is not connected
– Ricardo Mendes
I was able to fix the bug
OSError: [Errno 107] Transport endpoint is not connected
, the problem is to perform the Decode, as could be done on the server side to go back to being a tuple?– Ricardo Mendes
See in the answer given.
– Woss
@Andersoncarloswoss I’m sorry, maybe you were getting ready to answer and "I got in the way" I didn’t realize you were talking here. If you think it’s less correct I’ll remove it
– Miguel
@Miguel, no problem. I had not answered for not being sure if it would solve the problem.
– Woss
Because @Andersoncarloswoss I think it didn’t solve, it wasn’t enough lol. Ricardo you should be sure that you are excutating the function
recv
under the right socket, and that this is connected.– Miguel
I got this error:
tupla_recebida = json.loads(data.decode())
 File "/usr/lib/python3.5/json/__init__.py", line 319, in loads
 return _default_decoder.decode(s)
 File "/usr/lib/python3.5/json/decoder.py", line 339, in decode
 obj, end = self.raw_decode(s, idx=_w(s, 0).end())
 File "/usr/lib/python3.5/json/decoder.py", line 355, in raw_decode
 obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 7 (char 6)
– Ricardo Mendes
Ricardo, I chose to take the answer because to send data in binary cannot be inside a json. But I’m working to help you anyway
– Miguel
All right, no problem, thanks to @Miguel
– Ricardo Mendes
Look I’m creating the functionality, tell me something... Is your server for multiple clients? Or just one?
– Miguel
Only one customer
– Ricardo Mendes
Ricardo tests this: https://pastebin.com/53GDpmCg. It is very basic but it does the supposed... Python3 . Sets the name of the folder where the file will be saved on the server side
– Miguel
The trick is to send a json with the information you want (in this case, the file size/name), and only then the bytes that make up the file. And Inform the server that the json message has been fully received, we do this by sending a null byte at the end of the json message.... And only then the server starts receiving bytes from the file
– Miguel