我一直在使用一些Raspberry Pis,Python和一些按钮/开关来开发游戏。我的游戏需要一个向多个客户端发出命令的中央服务器。
对于编程而言,我并不陌生,但对Python和更低级别的网络通信不熟悉,并且在过去的两天里,我们在编写服务器代码的过程中已经在杂草中迷失了。
客户端程序是一个简单的socket.connect,然后等待发送数据。没问题。
我很难确定如何写作以及如何使服务器工作。
这是我的服务器代码目前的样子:
import socket, time, sys
import threading
TCP_IP = ''
TCP_PORT = 8888
BUFFER_SIZE = 1024
CLIENTS = {}
clientCount = 0
def listener():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP,TCP_PORT))
s.listen(5)
while True:
conn, addr = s.accept()
print("new connection from:"+ str(addr))
#print(len(CLIENTS))
global clientCount
clientCount = clientCount+1
print (clientCount)
# register client
CLIENTS[conn.fileno()] = conn
def broadcast():
for client in CLIENTS.values():
client.send('this is a broadcats msg')
if __name__ == '__main__':
listener()
while clientCount > 0:
broadcast()
print(len(CLIENTS)) #print out the number of connected clients every 5s
time.sleep(5)
这是所需的流程:
1.服务器启动并等待第一次连接或更多。我相信这个" Server"应该在后台线程上运行?
2.如果connectionCount > 0
启动主程序循环
3.现在主程序循环应该只显示已连接客户端的数量,并每隔5秒向所有客户端广播一条消息。
我有大约5个版本的服务器。我尝试过async,select.select和几种线程方法,但不能完全确定我所寻求的行为。我应该将服务器放在后台线程中吗?如果是这样,如何广播到所有连接?
我唯一没试过的就是Twisted,那是因为我无法将它安装在Windows中......所以我现在就把裁决推迟了。如果有人指出要去哪里,我会非常感激!
更新
好的,根据@Armans的建议,我已经更新了我的代码,以便有一个服务器类,但它仍然执行相同的操作。
class server():
def __init__(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP,TCP_PORT))
s.listen(10)
while 1:
client_socket, addr = s.accept()
print ('Connected with ' + addr[0] + ':' + str(addr[1]))
global clientCount
clientCount = clientCount+1
print (clientCount)
# register client
CLIENTS[client_socket.fileno()] = client_socket
threading.Thread(target=self.handler, args=(client_socket, addr)).start()
def handler(self, client_socket, addr):
while 1:
data = client_socket.recv(BUFFER_SIZE)
print ('Data : ' + repr(data) + "\n")
data = data.decode("UTF-8")
def broadcast(self, message):
for c in self.CLIENTS:
c.send(message.encode("utf-8"))
if __name__ == '__main__':
s = server() #create new server listening for connections
while clientCount > 0:
s.broadcast('msg here')
print(len(CLIENTS)) #print out the number of connected clients every 5s
time.sleep(5)
我可以连接多个客户端,控制台显示以下内容:
Connected with 10.0.0.194:38406
1
Connected with 10.0.0.169:36460
2
但是"而clientCount"中的代码循环从不运行。这是我被困一段时间的区域,所以如果你有更多的想法我会喜欢这里的任何想法!
答案 0 :(得分:1)
终于搞定了!非常感谢@Arman用线程指引我正确的方向。我终于觉得我明白一切是如何运作的!
这是我完整的服务器&客户代码。希望这可以帮助其他人与主人>客户端设置。 _broadcast()函数正在工作,因为你会看到它只是广播一个静态的消息,但这应该是一个简单的更新。
如果有人对代码清理有任何建议,那么使用此代码作为示例的python最佳实践我很乐意听到并了解更多信息。再次感谢SE!
##Client
import socket
import sys
import json
#vars
connected = False
#connect to server
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('10.0.0.158',8888))
connected = True
while connected == True:
#wait for server commands to do things, now we will just display things
data = client_socket.recv(1024)
cmd = json.loads(data) #we now only expect json
if(cmd['type'] == 'bet'):
bet = cmd['value']
print('betting is: '+bet)
elif (cmd['type'] == 'result'):
print('winner is: '+str(cmd['winner']))
print('payout is: '+str(cmd['payout']))
##Server
import socket, time, sys
import threading
import pprint
TCP_IP = ''
TCP_PORT = 8888
BUFFER_SIZE = 1024
clientCount = 0
class server():
def __init__(self):
self.CLIENTS = []
def startServer(self):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP,TCP_PORT))
s.listen(10)
while 1:
client_socket, addr = s.accept()
print ('Connected with ' + addr[0] + ':' + str(addr[1]))
global clientCount
clientCount = clientCount+1
print (clientCount)
# register client
self.CLIENTS.append(client_socket)
threading.Thread(target=self.playerHandler, args=(client_socket,)).start()
s.close()
except socket.error as msg:
print ('Could Not Start Server Thread. Error Code : ') #+ str(msg[0]) + ' Message ' + msg[1]
sys.exit()
#client handler :one of these loops is running for each thread/player
def playerHandler(self, client_socket):
#send welcome msg to new client
client_socket.send(bytes('{"type": "bet","value": "1"}', 'UTF-8'))
while 1:
data = client_socket.recv(BUFFER_SIZE)
if not data:
break
#print ('Data : ' + repr(data) + "\n")
#data = data.decode("UTF-8")
# broadcast
for client in self.CLIENTS.values():
client.send(data)
# the connection is closed: unregister
self.CLIENTS.remove(client_socket)
#client_socket.close() #do we close the socket when the program ends? or for ea client thead?
def broadcast(self, message):
for c in self.CLIENTS:
c.send(message.encode("utf-8"))
def _broadcast(self):
for sock in self.CLIENTS:
try :
self._send(sock)
except socket.error:
sock.close() # closing the socket connection
self.CLIENTS.remove(sock) # removing the socket from the active connections list
def _send(self, sock):
# Packs the message with 4 leading bytes representing the message length
#msg = struct.pack('>I', len(msg)) + msg
# Sends the packed message
sock.send(bytes('{"type": "bet","value": "1"}', 'UTF-8'))
if __name__ == '__main__':
s = server() #create new server listening for connections
threading.Thread(target=s.startServer).start()
while 1:
s._broadcast()
pprint.pprint(s.CLIENTS)
print(len(s.CLIENTS)) #print out the number of connected clients every 5s
time.sleep(5)
答案 1 :(得分:0)
我在这里采用multithread
方法:
s.listen(10)
while 1:
client_socket, addr = s.accept()
print ('Connected with ' + addr[0] + ':' + str(addr[1]))
threading.Thread(target=self.handler, args=(client_socket, addr)).start()
def handler(self, client_socket, addr):
while 1:
data = client_socket.recv(BUFF)
print ('Data : ' + repr(data) + "\n")
data = data.decode("UTF-8")
我强烈建议您为Server
和Client
编写一个类,为每个客户端创建一个客户端object
并将其连接到Server
,并存储每个连接的客户端(例如,它的套接字和名称)到字典,然后你想广播一条消息,你可以通过Server
中所有连接的客户端和你想要的广播消息:
def broadcast(self, client_socket, message):
for c in self.clients:
c.send(message.encode("utf-8"))
<强>更新强>
因为你有一个thread
运行main,你需要另一个thread
来运行服务器,我建议你为服务器写一个start
方法并在thread
中调用它:
def start(self):
# all server starts stuff comes here as define socket
self.s.listen(10)
while 1:
client_socket, addr = self.s.accept()
print ('Connected with ' + addr[0] + ':' + str(addr[1]))
threading.Thread(target=self.handler, args=(client_socket, addr)).start()
现在在main部分或主文件中创建服务器对象后运行启动线程:
a = server()
threading.Thread(target=a.start).start()