多人蛇游戏Python

时间:2014-07-28 22:21:38

标签: python sockets tcp pygame broadcast

我将创建一个多人游戏" Snake Game"在Python中。在服务器上,我使用线程来处理多个客户端,但现在我不知道如何将套接字发送给所有客户端。我需要它来通知所有用户" food"当有人"吃食物时。"

这是我的服务器代码:

from socket import *
import threading, os, sys, random
#from constants import *

def isOnline(username):
    return username in playerList

def getNewColor():
    r = random.randrange(0, 255)
    g = random.randrange(0, 255)
    b = random.randrange(0, 255)
    return (r, g, b)

def genFood():
    x = random.randrange(1, 49)
    y = random.randrange(1, 49)
    return (x, y)

def handler(clientsocket, clientaddr):
    print ('New Client ', clientaddr)

    player = ''
    points = 0
    index = 0

    while 1:
        try:
            data = clientsocket.recv(1024).decode()
            if data != '':
                print(data)
            if data.split(' ')[0] == 'login':
                if isOnline(data.split(' ')[1]):
                    clientsocket.send('already_on'.encode())
                else:
                    color = getNewColor()
                    clientsocket.send(('success ' + str(color)).encode())
                    player = data.split(' ')[1]
                    index = playerList.index(player)
                    playerList.append(player)
                    pointList.append(0)

            if player != '':

                if data == 'eat':
                    points += 1
                    pointList[index] = points
                    foodX, foodY = genFood()                                  
        except:
            print('Disconnected Client')
            clientsocket.close()
            if player != '':
                del playerList[index]
                del pointList[index]
            return 0

addr = ('localhost', 50000)
serversocket = socket(AF_INET, SOCK_STREAM)
serversocket.bind(addr)
serversocket.listen(20)
playerList = []
pointList = []


while 1:
    clientsocket, clientaddr = serversocket.accept()
    threading._start_new_thread(handler, (clientsocket, clientaddr))

serversocket.close()

3 个答案:

答案 0 :(得分:2)

快速和彻底的解决方案是将所有客户端套接字存储在列表中,如下所示:

clientsockets = []
# ...

clientsocket, clientaddr = serversocket.accept()
clientsockets.append(clientsocket)
threading._start_new_thread(handler, (clientsocket, clientaddr))

当然,当客户端关闭时,请记住remove列表中的套接字。

然后,向所有人发送消息:

for socket in clientsockets:
    socket.send(msg)

除非你想要一些错误处理,否则一个死客户端不能将整个服务器关闭。


但是,这有一个问题:永远不能保证send发送整个邮件。您可以使用sendall来修复此问题,但这并不能保证是原子的;它总是有可能一个线程将发送一部分消息,然后另一个线程将发送其部分消息,而不是第一个线程将发送其余消息。

因此,您需要添加一些锁定。

更简单的解决方案是为每个客户端创建一个读线程和一个写线程,每个写线程都有一个queue.Queue。与套接字不同,队列保证是原子的,因此您不必担心线程安全。

(您也可以创建一个"广播"队列,但是每个客户端都需要等待其广播队列及其特定于客户端的队列,此时您正在编写确切的队列同样类型的select - 类似于您希望通过线程避免使用的代码。)


在阅读方面也有类似的问题。您只是致电recv,期望只收到一条消息。但是sockets are byte streams, not message streams。我们无法保证您在一个recv中无法获得半条消息或两条消息。您必须编写一些协议,并缓冲传入的字节并从缓冲区中分离出消息。

在这种情况下,您的消息只是文本行,(据我所知)消息中不可能嵌入换行符。这允许一个死简单的协议:每一行都是一条消息。并且socket.makefile完全可以作为此协议的处理程序。像这样:

def handler(clientsocket, clientaddr):
    # your existing initial setup code
    with clientsocket.makefile('rb') as f:
        for data in f:
            try:
                # your existing loop, minus the recv command

您可能需要考虑使用Client类来包装两个处理程序和三个列表,而不是将它们全部分开,然后您还可以将客户列表作为类的属性来代替一个全球性的,但基本的想法是一样的。

答案 1 :(得分:0)

我建议使用已建立的网络服务器框架。网络问题很难诊断和修复。您的服务器代码将无法满足您的需求。就个人而言,我个人推荐Tornado

答案 2 :(得分:0)

如果希望所有客户端线程都能够查看新数据,则需要在创建任何线程之前建立某种全局变量。线程的一个常见构造是为工作线程创建一个队列,以从中获取信息并进行处理;但问题是,每个线程都需要查看进入该队列的所有内容,因此线程无法从中获取内容。

这是我的建议:维护一个“消息”队列,其中包含线程需要传递给每个客户端的数据。将时间戳附加到每个消息,以便每个线程可以知道消息何时是新的还是旧的。每个线程将跟踪它发送的最新消息的时间戳,因此它将知道队列中的指令是否已经发送。如果每个线程将其时间戳保存在主线程可见的列表中,那么主线程可以查看并确保哪些指令被保证已被发送/发送给每个客户端,并且可以相应地删除过时的指令。

请记住,我没有必要使用线程一段时间,但我希望逻辑是正确的。正如其他人所说,可能还有其他框架可以让您更有效地获得所需的结果。但是如果你坚持使用“每个套接字都有自己的线程”机制,那么这应该让你开始考虑如何思考这个问题。