使用Python套接字发送数据实例

1 投票
2 回答
3364 浏览
提问于 2025-04-15 22:14

我正在开发一个两人对战的游戏(类似于俄罗斯方块),现在我在处理网络部分。我想把游戏的网格从客户端传到服务器,再从服务器传回客户端。但是,当我尝试用send(grid)发送网格时,出现了一个错误:TypeError: send()的第一个参数必须是字符串或只读缓冲区,而不是实例。

有没有办法解决这个问题?还是说我必须把我的网格实例转换成字符串,然后在另一边再解析它?谢谢!

2 个回答

1

看看数据序列化的内容:pickle。它的作用是在发送方把数据转换成字符串(也就是序列化),然后在接收方再把这个字符串转换回原来的数据结构(也就是反序列化)。

6

除非你对客户端和服务器的软件都有完全的控制,否则在传输数据时最好不要使用pickle。pickle在你确定要解码的数据是可信的情况下很好用,但如果数据可能被篡改,那就不安全了。你可以查看为什么Python的pickle不安全,或者在pickle模块文档中找到一些安全方面的建议。

如果要传输数据,JSON是一个不错的选择;XML也可以,YAML也挺好的。一个简单的消息格式可以是先发送消息数据的大小,然后是一个分隔符(CRLF\r\n是常用的),最后是消息数据。

如果你使用JSON,你需要么使用json模块能处理的对象,要么自己写JSONEncoderJSONDecoder的子类来处理你感兴趣的类型。

下面是一个简单的JSON示例,你可以运行一下(不确定在Windows上是否能工作)。只需在两个终端中各运行一次,输入一个终端的内容,另一个终端应该能显示出来。

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

撰写回答