Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何通过套接字和选择模块管理聊天服务器(Python)的套接字连接_Python_Sockets_Select_Module - Fatal编程技术网

如何通过套接字和选择模块管理聊天服务器(Python)的套接字连接

如何通过套接字和选择模块管理聊天服务器(Python)的套接字连接,python,sockets,select,module,Python,Sockets,Select,Module,很抱歉打扰大家,我已经被难住了一段时间了 问题是,我决定使用套接字重新配置这个聊天程序,这样它就不用客户端和服务器/客户端,而是有一个服务器,然后是两个独立的客户端 我之前问过如何让我的服务器“管理”客户机的这些连接,以便在它们之间重定向数据。我得到了一个奇妙的答案,它为我提供了我显然需要的代码 问题是我不明白它是如何工作的,我确实在评论中询问过,但除了一些文档链接外,我没有得到多少回复 我得到的是: connections = [] while True: rlist,wlist,x

很抱歉打扰大家,我已经被难住了一段时间了

问题是,我决定使用套接字重新配置这个聊天程序,这样它就不用客户端和服务器/客户端,而是有一个服务器,然后是两个独立的客户端

我之前问过如何让我的服务器“管理”客户机的这些连接,以便在它们之间重定向数据。我得到了一个奇妙的答案,它为我提供了我显然需要的代码

问题是我不明白它是如何工作的,我确实在评论中询问过,但除了一些文档链接外,我没有得到多少回复

我得到的是:

connections = []

while True:
    rlist,wlist,xlist = select.select(connections + [s],[],[])
    for i in rlist:
        if i == s:
            conn,addr = s.accept()
            connections.append(conn)
            continue
        data = i.recv(1024)
        for q in connections:
            if q != i and q != s:
                q.send(data)
据我所知,select模块提供了在select.select情况下生成可等待对象的功能

我得到了rlist,未决待读列表,wlist,未决待写列表,然后是xlist,未决异常条件

他正在将待写列表分配给“s”,在我的聊天服务器部分,它是在分配的端口上侦听的套接字

这大概是我觉得我理解得足够清楚的程度。但我真的很想得到一些解释

如果你不觉得我问了一个合适的问题,请在评论中告诉我,我会删除它。我不想违反任何规则,而且我很确定我不会像我在求助于提问之前做了一段时间的研究那样复制线程

谢谢

注意:我在这里的解释假设您谈论的是TCP套接字,或者至少是某种基于连接的类型。UDP和其他数据报(即非基于连接的)套接字在某些方面类似,但对它们使用
select
的方式略有不同

每个套接字就像一个打开的文件,可以读取和写入数据。您写入的数据进入系统内部的缓冲区,等待在网络上发送。从网络到达的数据将在系统内缓冲,直到您读取它。下面有很多聪明的东西,但当你使用一个插座时,这就是你真正需要知道的(至少最初是这样)

请记住,在下面的解释中,系统正在进行这种缓冲,这通常很有用,因为您会意识到操作系统中的TCP/IP堆栈独立于您的应用程序发送和接收数据-这样做是为了让您的应用程序具有一个简单的接口(这就是套接字,一种对代码隐藏所有TCP/IP复杂性的方法)

读写的一种方法是阻塞,例如,如果系统中有数据等待,则会立即返回。但是,如果没有数据等待,则调用会阻塞-也就是说,您的程序会暂停,直到有数据要读取。有时您可以通过超时来执行此操作,但在纯阻塞IO中,您确实可以永远等待,直到另一端发送s删除数据或关闭连接

对于一些简单的情况来说,这并不太糟糕,但仅当您与另一台机器进行通信时——当您在多个套接字上进行通信时,您不能只等待来自一台机器的数据,因为另一台机器可能会向您发送内容。还有一些其他问题,我在这里不会详细介绍——可以说这不是一个问题d方法

一种解决方案是为每个连接使用不同的线程,因此阻塞是可以的-其他连接的其他线程可以在不影响彼此的情况下被阻塞。在这种情况下,每个连接需要两个线程,一个用于读取,一个用于写入。但是,线程可能很棘手-您需要小心地在线程之间同步数据它们会使编码变得有点复杂。而且,对于这样一个简单的任务来说,它们有些低效

select
模块可以为这个问题提供单线程解决方案,而不是阻塞单个连接,它允许您执行一个函数,该函数说“进入睡眠状态,直到这些套接字中至少有一个具有我可以读取的数据为止”(这是一个简化,我稍后会更正)因此,一旦调用
select.select()
返回,您就可以确定您正在等待的某个连接中有一些数据,并且您可以安全地读取它(即使使用阻塞IO,如果您小心的话-因为您确定那里有数据,所以您永远不会阻塞等待它)

第一次启动应用程序时,只有一个套接字作为侦听套接字。因此,只能在调用
select.select()时传递该套接字
。我前面做的简化是,实际上调用接受三个套接字列表,用于读取、写入和错误。第一个列表中的套接字被监视读取-因此,如果其中任何一个有数据要读取,则
select.select()
函数将控制权返回给您的程序。第二个列表用于写入-您可能认为您可以随时向套接字写入数据,但实际上,如果连接的另一端读取数据的速度不够快,则系统的写入缓冲区可能会满满,您可能暂时无法写入。它看起来像是向您提供代码的人忽略了这种复杂性,这对于一个简单的示例来说并不太糟糕,因为通常缓冲区足够大,在这样的简单情况下,您不太可能遇到问题,但这是一个问题,您应该在以后的其他代码工作后解决。最后的列表会关注错误-这没有广泛使用,因此我现在跳过它。不是吗他的空名单在这里很好

此时有人连接到您的服务器-就
select.select()
而言,这将被视为使侦听套接字“可读”,因此函数返回,可读套接字列表(第一个返回值)将包括
connections = []
buffered_output = {}

while True:
    rlist,wlist,xlist = select.select(connections + [s],buffered_output.keys(),[])
    for i in rlist:
        if i == s:
            conn,addr = s.accept()
            connections.append(conn)
            continue
        try:
            data = i.recv(1024)
        except socket.error:
            data = ""
        if data:
            for q in connections:
                if q != i:
                    buffered_output[q] = buffered_output.get(q, b"") + data
        else:
            i.close()
            connections.remove(i)
            if i in buffered_output:
                del buffered_output[i]
    for i in wlist:
        if i not in buffered_output:
            continue
        bytes_sent = i.send(buffered_output[i])
        buffered_output[i] = buffered_output[i][bytes_sent:]
        if not buffered_output[i]:
            del buffered_output[i]