tags:

views:

106

answers:

2

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!

+1  A: 

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.

pulegium
+5  A: 

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...'
Matt Anderson