简体   繁体   English

Python TCP服务器接受连接和广播命令

[英]Python TCP Server Accepting Connections and Broadcasting Commands

I've been working on a game using a number of Raspberry Pis, Python, and some buttons/switches. 我一直在使用多个Raspberry Pi,Python和一些按钮/开关进行游戏。 My game requires a central server that issues commands to multiple clients. 我的游戏需要一个中央服务器,该服务器向多个客户端发出命令。

I'm not new to programming but new to Python and lower-er level network communication and have been lost in the weeds for the past 2 days on how exactly to write my server code. 我不是编程新手,而是Python和较低级别的网络通信的新手,在过去两天里,我一直迷失在如何精确编写服务器代码方面。

The client program is a simple socket.connect and then waits for data to be sent. 客户端程序是一个简单的socket.connect,然后等待发送数据。 No problems there. 那里没有问题。

I have had a tough time determining exactly how to write and how to make work the server. 我很难确定确切的编写方式以及如何使服务器正常工作。

Here's what my server code looks like at the moment: 这是我目前的服务器代码:

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) 

Here's the desired flow: 1. Server starts and waits for first connection or more. 这是所需的流程:1.服务器启动并等待首次连接或更多连接。 I believe this "Server" should be running on a background thread? 我相信这个“​​服务器”应该在后台线程上运行吗? 2. If connectionCount > 0 start the main program loop 3. For now the main program loop should just display the number of connected clients and broadcast a message to all of them every 5 seconds. 2.如果connectionCount > 0启动主程序循环。3.现在,主程序循环应仅显示已连接客户端的数量,并每5秒向所有客户端广播一条消息。

I have about 5 versions of this server. 我有大约5个版本的服务器。 I have tried async, select.select, and several threaded approaches but can't quite nail down the behavior I seek. 我已经尝试了async,select.select和几种线程方法,但无法完全确定我寻求的行为。 Should I be putting the server in a background thread? 我应该将服务器置于后台线程中吗? If so how to broadcast to all the connections? 如果是这样,如何广播到所有连接?

The only thing I haven't tried is Twisted and that is because I could not get it installed in Windows... so I'm ruling that optino out for the moment. 我唯一没有尝试过的就是Twisted,那是因为我无法将其安装在Windows中……所以我暂时不选择optino。 If anyone has a pointer on where to go on this I would really appreciate it! 如果有人在哪里进行指导,我将不胜感激!

Update 更新资料

Ok, based on @Armans suggestion I have updated my code so that there is a server class but it still performs the same. 好的,根据@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) 

I can connect multiple clients and the console displays the following: 我可以连接多个客户端,并且控制台显示以下内容:

Connected with 10.0.0.194:38406
1
Connected with 10.0.0.169:36460
2

But the code in the "while clientCount" loop never runs. 但是“ while clientCount”循环中的代码永远不会运行。 This is the zone where I have been stuck for some time so if you have a couple more ideas I would love to here any ideas! 这是我被困住一段时间的区域,因此,如果您有更多的想法,我很乐意在此提出任何想法!

Finally got it working! 终于成功了! Much thanks to @Arman for pointing me in the right direction with the threading. 非常感谢@Arman为我指出正确的穿线方向。 I finally feel like I understand how everything is working! 我终于感觉到我明白一切正常!

Here is my complete Server & Client code. 这是我完整的服务器和客户端代码。 Hopefully this helps someone else with a master > client setup. 希望这可以通过master> client setup帮助其他人。 The _broadcast() function is working as you will see it just broadcasts a static msg at the moment but that should be an easy update. _broadcast()函数正在工作,因为您将看到它现在仅广播静态消息,但这应该是一个简单的更新。

If anyone has any advice on code cleanup, python best practices using this code as the sample I would love to hear and learn more. 如果有人对代码清除有任何建议,请使用此代码作为示例的python最佳实践,我希望听到并了解更多。 Thanks again SE! 再次感谢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) 

I have a multithread approach here : 我在这里有一个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")

I strongly recommend you two write a class for Server and Client , for each client create a Client object and connect it to Server , and store each connected Client (its socket and a name for example) to a dictionary as you did, then you want to broadcast a message you can go through all connected Clients in Server and broadcast message you want like this: 我强烈建议您两个为ServerClient编写一个类,为每个客户端创建一个Client object并将其连接到Server ,然后像您所做的那样将每个连接的Client(例如其套接字和名称)存储到字典中,然后要广播消息,您可以遍历Server所有已连接客户端,并广播所需消息,如下所示:

def broadcast(self, client_socket, message):
        for c in self.clients:
            c.send(message.encode("utf-8"))

Update 更新资料

Because you have a thread that runs main you need another thread for running server , I suggest you write a start method for server and call it in a thread : 因为你有一个thread运行主你需要另一个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()

now in main section or main file after create server object run the start thread : 现在,在创建服务器对象之后,在主部分或主文件中运行启动线程:

a = server()
threading.Thread(target=a.start).start()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 服务器是否在主机“postgres_db”(10.10.0.4)上运行并接受端口 5434 上的 TCP/IP 连接? - Is the server running on host “postgres_db” (10.10.0.4) and acceptinG TCP/IP connections on port 5434? TCP服务器正在关闭连接 - TCP server is closing connections Python套接字不接受外部连接 - Python socket not accepting external connections python中的多个TCP连接 - Multiple TCP connections in python 服务器是否在主机“localhost”上运行并接受端口 5432 上的 TCP/IP 连接? & 引发 HaltServer(原因,self.WORKER_BOOT_ERROR) - Is the server running on host "localhost" and accepting TCP/IP connections on port 5432? & raise HaltServer(reason, self.WORKER_BOOT_ERROR) “10.9.8.5”,端口 5433 失败:连接超时 (0x0000274C/10060) 服务器是否在该主机上运行并接受 TCP/IP 连接? - "10.9.8.5", port 5433 failed: Connection timed out (0x0000274C/10060) Is the server running on that host and accepting TCP/IP connections? Python中的最大TCP连接数? - Maximum number of TCP connections in Python? 多个 TCP 套接字连接 Python - Multiple TCP socket connections Python Python 套接字服务器不接受来自本地 WIFI 网络外部的连接 - Python Socket Server Isn't Accepting Connections From Outside The Local WIFI Network Python 通过套接字接受连接的速度很慢 - Python slow at accepting connections via sockets
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM