Sending Data as Instances using Python Sockets
I'm working on the networking part of a 2 player game (similar to tetris), and I'm trying to pass the game grid from client to server and vice versa. However, when I tried using send(grid) I get a TypeError: send() argument 1 must be string or read-only buffer, not instance.
Is there anyway to circumvent开发者_运维问答 this, or do I have to convert my grid instance into a string and then interpret it from the other side? Thanks in advance!
Unless you're in total control over both the client and server software, you should hesitate to use pickle to transmit data back and forth. Pickle is great if you're sure the data you're unpickling is trustworthy, but if it might have been tampered with, it is insecure. See Why Python Pickle is Insecure, or the security cautionary suggestions in the pickle module documentation.
JSON is a good choice for formatting of data to send back and forth; XML is okay, YAML is pretty good. A simple messaging format might be to send the size of the message data, a delimiter (CRLF
or \r\n
is common) and then the message data.
If you use JSON, you'll have to either stick with the objects the json
module knows how to encode/decode, or write JSONEncoder
and JSONDecoder
subclasses to deal with the types you're interested in.
Below is a little JSON proof of concept you can run (not sure if it would work on Windows or not). Just run it in each of two terminals, type in one and it should show up in the other.
import socket
import select
import sys
import json
CRLF = '\r\n'
class MalformedMessage(Exception): pass
class ConnectionClosed(Exception): pass
def read_exactly(sock, buflen):
data = ''
while len(data) != buflen:
data += sock.recv(buflen - len(data))
return data
def peek(sock, buflen):
data = sock.recv(buflen, socket.MSG_PEEK)
return data
def socket_send(sock, obj):
data = json.dumps(obj)
size = len(data)
sock.sendall('%i%s%s' % (size, CRLF, data))
def socket_recv(sock):
peekdata = peek(sock, 1024)
if peekdata == '':
raise ConnectionClosed
sizepos = peekdata.find(CRLF)
if sizepos == -1:
raise MalformedMessage('Did not find CRLF in message %r' % peekdata)
sizedata = read_exactly(sock, sizepos)
read_exactly(sock, len(CRLF))
try:
size = int(sizedata)
except ValueError:
raise MalformedMessage(
'size data %r could not be converted to an int' % sizedata)
data = read_exactly(sock, size)
return json.loads(data)
if __name__ == '__main__':
netloc = ('', 7777)
try:
servsock = socket.socket()
servsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
servsock.bind(netloc)
servsock.listen(5)
sock, _ = servsock.accept()
except socket.error:
sock = socket.socket()
sock.connect(netloc)
try:
while True:
r_ok, _, _ = select.select([sys.stdin, sock], [], [])
for fd in r_ok:
if fd == sys.stdin:
obj = eval(fd.readline().strip())
socket_send(sock, obj)
elif fd == sock:
obj = socket_recv(sock)
print repr(obj)
except (KeyboardInterrupt, ConnectionClosed):
pass
finally:
print '\nexiting...'
Look into data serialisation: pickle. It would do data serialisation (read convers into a string) on the sender side, and then de-serialisation (ie, converts back into the data structure) on the receiver side.
精彩评论