Python 套接字,高级聊天框

标签 python multithreading sockets

我想创建一个服务器 handle 同时有很多客户端( 处理 :从客户端接收数据并同时向所有客户端发送数据!!!)

其实我正在尝试创建一个聊天框。该程序将像这样工作:

1)将有一个处理客户端的服务器。

2) 多个客户端可以加入服务器。

3) 客户端向服务器发送消息(字符串)。

4)服务器接收来自客户端的消息,然后将其发送给所有人
除了他得到它的客户之外的客户。

这就是客户端相互交流的方式。没有可用的私有(private)消息。当有人点击进入时,所有客户都会在他们的屏幕上看到消息。

客户端模块很容易制作,因为客户端只与一个套接字(服务器)通信。

另一方面,服务器模块真的很复杂,我不知道该怎么做(我也知道线程)。

这是我的尝试:

import socket, threading


class Server:

def __init__(self, ip = "", port = 5050):

    '''Server Constructor. If __init__ return None, then you can use
       self.error to print the specified error message.'''

    #Error message.
    self.error  = ""

    #Creating a socket object.
    self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    #Trying to bind it.
    try:
        self.server.bind( (ip, port) )
        pass


    #Failed, because socket has been shuted down.
    except OSError :
        self.error = "The server socket has been shuted down."
        return None

    #Failed, because socket has been forcibly reseted.
    except ConnectionResetError:
         self.error = "The server socket has been forcibly reseted."
         return None


    #Start Listening.
    self.server.listen()


    #_____Other Variables_____#

    #A flag to know when to shut down thread loops.
    self.running = True

    #Store clients here.
    self.clients = []

    #_____Other Variables_____#


    #Start accepting clients.
    thread = threading.thread(target = self.acceptClients)
    thread.start()

    #Start handling the client.
    self.clientHandler()





#Accept Clients.
def acceptClients(self):

    while self.running:
        self.clients.append( self.server.accept() )

    #Close the server.
    self.server.close()




#Handle clients.
def clientHandler(self):

    while self.running:

        for client in self.clients:

            sock = client[0]
            addr = client[1]

            #Receive at most 1 mb of data.
            #The problem is that recv will block the loop!!!
            data = sock.recv(1024 ** 2)

如您所见,我使用线程接受客户端,因此 server.accept() 不会阻塞程序。然后我将客户存储到一个列表中。

但问题出在 客户端处理程序 .我将如何从所有人中恢复
客户同时?第一个recv会阻塞循环!!!

我还尝试为每个新客户端启动新线程(clientHandlers)
但问题是同步。

那发送呢?服务器必须向所有客户端发送数据,因此 clientHandler 尚未完成。但是如果我混合使用这些方法 接收 发送那么问题就变得更加复杂了。

那么什么是正确和最好的方法呢?
我也想举个例子。

最佳答案

当不同的客户端彼此独立时,多线程非常有用:您编写代码时就像只存在一个客户端一样,并为每个客户端启动一个线程。

但是在这里,来自一个客户的东西必须发送给其他客户。每个客户端一个线程肯定会导致同步噩梦。所以让我们调用select救援! select.select允许轮询套接字列表并在准备好后立即返回。在这里,您可以构建一个包含监听套接字和所有接受的套接字的列表(该部分最初是空的......):

  • 当监听套接字准备好读取时,接受一个新的套接字并将其添加到列表
  • 当另一个套接字准备好读取时,从中读取一些数据。如果读取 0 字节,则其对等体已关闭或关闭:关闭它并将其从列表中删除
  • 如果您从一个已接受的套接字中读取了某些内容,则在列表中循环,跳过正在监听的套接字以及您已从中读取的套接字并将数据发送到任何其他套接字

  • 代码可能(或多或少):
        main = socket.socket()  # create the listening socket
        main.bind((addr, port))
        main.listen(5)
        socks = [main]   # initialize the list and optionaly count the accepted sockets
    
        count = 0
        while True:
            r, w, x = select.select(socks, [], socks)
            if main in r:     # a new client
                s, addr = main.accept()
                if count == mx:  # reject (optionaly) if max number of clients reached
                    s.close()
                else:
                    socks.append(s) # appends the new socket to the list
            elif len(r) > 0:
                data = r[0].recv(1024)  # an accepted socket is ready: read
                if len(data) == 0:      # nothing to read: close it
                    r[0].close()
                    socks.remove(r[0])
                else:
                    for s in socks[1:]:  # send the data to any other socket
                        if s != r[0]:
                            s.send(data)
            elif main in x:  # close if exceptional condition met (optional)
                break
            elif len(x) > 0:
                x[0].close()
                socks.remove(x[0])
        # if the loop ends, close everything
        for s in socks[1:]:
            s.close()
        main.close()
    

    您当然需要实现一种机制来要求服务器停止,并测试所有这些,但它应该是一个起点

    关于Python 套接字,高级聊天框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39618547/

    相关文章:

    Android volatile 不工作?

    c++ - boost::condition 会 boost 性能吗?

    c# - 在哪里可以找到 SocketException 抛出的 SocketErrorCode 和 NativeErrorCode 列表?

    python - 如何跟踪 csr 矩阵

    java - 线程-简单的并发问题

    Python 函数需要一个元组,而我拥有的是一个列表。我该如何调用这个函数?

    java - 套接字:使用 Java 发现端口可用性

    c++ - 使用同步或异步的单线程连接多个客户端?

    python - 捕获未定义错误 : 'openerp.tools.misc.frozendict object' has no attribute 'button_access' while email template rendering in odoo

    python - 如何将图表写入 Python DocX 文档