How, in Python 3, can I have a client open a socket to a server, send 1 line of JSON-encoded data, read 1 line JSON-encoded data back, and continue?
Question:
I have the following code for a server listening on a port:
def handle_oracle_query(self, sock, address):
sockIn = sock.makefile('rb')
sockOut = sock.makefile('wb')
line = sockIn.readline()
submitted_data = json.loads(line)
self.get_thread_specific_storage()['environmental_variables'] = submitted_data['environmental_variables']
self.get_thread_specific_storage()['cgi'] = submitted_data['cgi']
generate_output()
print_output(sockOut)
sock.close()
sockIn.close()
sockOut.close()
self.remove_thread_specific_storage()
I am attempting to connect to it with the client below:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = server[0]
try:
port = int(server[1])
except:
port = DEFAULT_PORT
sock.connect((ip, port))
sockIn = sock.makefile("rb")
sockOut = sock.makefile("wb")
cgi_hash = {}
for key in cgi.FieldStorage().keys():
cgi_hash[key] = cgi.FieldStorage()[key].value
as_dictionary = dict(os.environ)
submitted_data = {
'environmental_variables': as_dictionary,
'cgi': cgi_hash}
encoded_data = json.dumps(submitted_data)
sys.stderr.write(encoded_data + 'rn')
sockOut.write(encoded_data + 'rn')
sockOut.flush()
result = json.loads(sockIn.read())
sock.close()
sockIn.close()
sockOut.close()
When I try to connect with the server I get the following stacktrace:
Traceback (most recent call last):
File "/home/christos/fathers/bin/./fathersd", line 4075, in <module>
multitasking.start_oracle()
File "/home/christos/fathers/bin/./fathersd", line 2396, in start_oracle
self.run_oracle()
File "/home/christos/fathers/bin/./fathersd", line 2372, in run_oracle
self.handle_oracle_query(newsocket, address)
File "/home/christos/fathers/bin/./fathersd", line 2309, in handle_oracle_query
submitted_data = json.loads(line)
File "/usr/lib/python3.10/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.10/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.10/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
What changes should I be making to the above code in the client and/or server so the client sends a single line of input containing a JSON string and the server, after constructing the output, returns a single line of output containing a JSON string of output which the client can parse and work from there?
I am in the process of porting an application to Python 3. I would like a character encoding of UTF-8.
Answers:
The problem here is at the client, not the server. Maybe you should take a look at your client’s output, you would get an error in line sockOut.write(encoded_data + 'rn')
, because your sockOut is a binary interface (you created it as a wb
and not w
), so it requires a bytes
object, not a string.
You can convert a string to bytes using .encode()
, like sockOut.write((encoded_data + 'rn').encode())
. You should take a look at the code where the server sends the response, if you did the same thing there.
And by the way: You can change 'rn'
to 'n'
, because a new line is just a n
character on every unix-like system, and python will also detect a new line by n
. rn
is a windows thing, but it’s not the standard way for new lines. Just wanted to mention that, but you can also leave it as it is.
Just change wb to w and rb to r when creating the socket IOs. You are working with strings, but "wb" stands for "write bytes" and expect a bytes
object.
I have the following code for a server listening on a port:
def handle_oracle_query(self, sock, address):
sockIn = sock.makefile('rb')
sockOut = sock.makefile('wb')
line = sockIn.readline()
submitted_data = json.loads(line)
self.get_thread_specific_storage()['environmental_variables'] = submitted_data['environmental_variables']
self.get_thread_specific_storage()['cgi'] = submitted_data['cgi']
generate_output()
print_output(sockOut)
sock.close()
sockIn.close()
sockOut.close()
self.remove_thread_specific_storage()
I am attempting to connect to it with the client below:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = server[0]
try:
port = int(server[1])
except:
port = DEFAULT_PORT
sock.connect((ip, port))
sockIn = sock.makefile("rb")
sockOut = sock.makefile("wb")
cgi_hash = {}
for key in cgi.FieldStorage().keys():
cgi_hash[key] = cgi.FieldStorage()[key].value
as_dictionary = dict(os.environ)
submitted_data = {
'environmental_variables': as_dictionary,
'cgi': cgi_hash}
encoded_data = json.dumps(submitted_data)
sys.stderr.write(encoded_data + 'rn')
sockOut.write(encoded_data + 'rn')
sockOut.flush()
result = json.loads(sockIn.read())
sock.close()
sockIn.close()
sockOut.close()
When I try to connect with the server I get the following stacktrace:
Traceback (most recent call last):
File "/home/christos/fathers/bin/./fathersd", line 4075, in <module>
multitasking.start_oracle()
File "/home/christos/fathers/bin/./fathersd", line 2396, in start_oracle
self.run_oracle()
File "/home/christos/fathers/bin/./fathersd", line 2372, in run_oracle
self.handle_oracle_query(newsocket, address)
File "/home/christos/fathers/bin/./fathersd", line 2309, in handle_oracle_query
submitted_data = json.loads(line)
File "/usr/lib/python3.10/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.10/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.10/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
What changes should I be making to the above code in the client and/or server so the client sends a single line of input containing a JSON string and the server, after constructing the output, returns a single line of output containing a JSON string of output which the client can parse and work from there?
I am in the process of porting an application to Python 3. I would like a character encoding of UTF-8.
The problem here is at the client, not the server. Maybe you should take a look at your client’s output, you would get an error in line sockOut.write(encoded_data + 'rn')
, because your sockOut is a binary interface (you created it as a wb
and not w
), so it requires a bytes
object, not a string.
You can convert a string to bytes using .encode()
, like sockOut.write((encoded_data + 'rn').encode())
. You should take a look at the code where the server sends the response, if you did the same thing there.
And by the way: You can change 'rn'
to 'n'
, because a new line is just a n
character on every unix-like system, and python will also detect a new line by n
. rn
is a windows thing, but it’s not the standard way for new lines. Just wanted to mention that, but you can also leave it as it is.
Just change wb to w and rb to r when creating the socket IOs. You are working with strings, but "wb" stands for "write bytes" and expect a bytes
object.