- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
抱歉打扰大家,但是我已经被困了一段时间了。
问题是我决定重新配置我使用套接字的聊天程序,以便它有一个服务器,然后是两个单独的客户端,而不是一个客户端和一个服务器/客户端。
前面我问过我如何让服务器“管理”客户端的这些连接,以便它可以在它们之间重定向数据。而且我得到了一个奇妙的答案,该答案为我提供了我显然需要执行此操作的确切代码。
问题是我不知道它是如何工作的,我确实在评论中提出了要求,但是除了一些文档链接之外,我没有得到太多答复。
这是给我的:
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)
最佳答案
注意:我在这里的解释假设您正在谈论TCP套接字,或者至少是某种基于连接的类型。 UDP和其他数据报(即非基于连接的)套接字在某些方面相似,但是在它们上使用select
的方式略有不同。
每个套接字就像一个打开的文件,可以读取和写入数据。您写入的数据进入系统内部的缓冲区,等待在网络上发送出去。从网络到达的数据将在系统内部进行缓冲,直到您读取它为止。下面有很多聪明的东西,但是当您使用套接字时,您真正需要知道的全部(至少在最初是这样)。
记住,系统在下面的说明中进行缓冲通常是很有用的,因为您将意识到OS中的TCP/IP堆栈独立于您的应用程序发送和接收数据-这样做是为了使您的应用程序可以拥有一个简单的接口(interface)(套接字就是这种接口(interface),一种从代码中隐藏所有TCP/IP复杂性的方式)。
进行读写的一种方法是阻塞。使用该系统,例如,当您调用recv()
时,如果系统中有数据等待,则将立即返回它。但是,如果没有数据等待,则调用将阻塞-也就是说,程序将暂停直到有要读取的数据为止。有时您可以使用超时来执行此操作,但是在纯阻塞IO中,您实际上可以永远等待直到另一端发送一些数据或关闭连接。
在某些简单的情况下,这并不太坏,但仅在与另一台计算机通信的情况下-当您在一个以上的套接字上通信时,您不能只等待来自一台计算机的数据,因为另一台计算机可能正在向您发送东西。还有其他一些问题,在这里我将不作过多详细介绍-可以说这不是一个好方法。
一种解决方案是为每个连接使用不同的线程,因此阻塞是可以的-可以阻塞其他连接的其他线程而不会互相影响。在这种情况下,每个连接需要两个线程,一个线程读取,一个线程写入。但是,线程可能是棘手的野兽-您需要仔细地在线程之间同步数据,这会使编码变得有些复杂。同样,对于这样的简单任务,它们效率低下。select
模块为您提供了解决此问题的单线程解决方案-而不是在单个连接上进行阻塞,它使您可以使用一个函数,该函数显示“进入休眠状态,直到这些套接字中的至少一个有一些我可以读取的数据为止”(这是一种简化,我待会儿纠正。因此,一旦对select.select()
的调用返回,您可以确定正在等待的连接之一具有一些数据,并且可以安全地读取它(即使小心也可以使用阻塞IO -因为您确定那里有数据,您将永远不会阻塞等待它)。
首次启动应用程序时,只有一个套接字即监听套接字。因此,您只需在对select.select()
的调用中将其传递。我之前所做的简化是,实际上该调用接受三个套接字列表以进行读取,写入和错误处理。监视第一个列表中的套接字以进行读取-因此,如果其中任何一个套接字有要读取的数据,select.select()
函数将控制权返回给您的程序。第二个列表用于写入-您可能会认为您总是可以写入套接字,但是实际上,如果连接的另一端读取数据的速度不够快,则系统的写入缓冲区可能已满,并且您可能暂时无法写入。看起来像是为您提供代码的人忽略了这种复杂性,对于一个简单的示例而言,这并不算太糟糕,因为通常缓冲区足够大,您不太可能在像这样的简单情况下遇到问题,但这是您应该解决的问题您其余的代码正常工作后,将来再解决。监视最终列表是否有错误-并未得到广泛使用,因此我暂时将其跳过。在此处传递空列表是可以的。
此时,有人连接到您的服务器-就select.select()
而言,这使监听套接字“可读”,因此该函数返回,并且可读套接字列表(第一个返回值)将包括监听套接字。
下一部分将遍历所有要读取数据的连接,并且您可以看到监听套接字s
的特殊情况。该代码在其上调用accept()
,它将从监听套接字获取下一个等待的新连接,并将其转换为该连接的全新套接字(监听套接字继续监听,并且可能还有其他新连接也在等待,但这很好-我会在稍后介绍)。全新的套接字已添加到connections
列表中,这就是处理监听套接字的结尾-continue
将继续到select.select()
返回的下一个连接(如果有)。
对于其他可读的连接,代码在其上调用recv()
以恢复下一个1024
字节(如果少于1024个字节,则可用任何字节)。重要说明-如果您未使用select.select()
来确保连接可读,则对该recv()
的调用可能会阻止并暂停您的程序,直到数据到达该特定连接为止-希望这说明了为什么需要select.select()
的原因。
读取某些数据后,代码将在所有其他连接(如果有)上运行,并使用send()
方法将数据向下复制。该代码正确地跳过了与刚到达的数据相同的连接(这是关于q != i
的问题),并且还跳过了s
,但是由于我看到它实际上从未真正添加到connections
列表中,因此这不是必需的。
处理完所有可读连接后,代码将返回select.select()
循环以等待更多数据。请注意,如果一个连接仍然有数据,则调用将立即返回-这就是为什么只接受来自监听套接字的单个连接是可以的。如果有更多连接,select.select()
将立即再次返回,并且循环可以处理下一个可用的连接。您可以使用非阻塞IO来提高效率,但是这会使事情变得更复杂,因此让我们暂时保持简单。
这是一个合理的说明,但不幸的是,它存在一些问题:
send()
,但是如果您的一个连接的另一端接收不正确(可能是计算机过载),则此处的代码可能填满了发送缓冲区,然后挂起当它尝试调用send()
时。 recv()
返回空字符串。这应该导致连接被关闭并从connections
列表中删除,但是此代码不执行此操作。 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]
select
时使套接字无阻塞-这意味着对
recv()
或
send()
的调用将被阻塞,否则将返回错误(在Python术语中引发异常)。这样做的部分目的是为了安全起见,以确保不会导致粗心的代码最终不会阻塞应用程序。但是它还允许使用一些效率更高的方法,例如以多个块的形式读取或写入数据,直到没有剩余的块为止。使用阻塞IO是不可能的,因为
select.select()
调用仅保证可以读取或写入一些数据,而不保证有多少数据。因此,您只能在每个连接上安全地调用一次阻塞的
send()
或
recv()
,然后才能再次调用
select.select()
以查看是否可以再次调用。监听套接字上的
accept()
也是如此。
libev
之类的东西,它实际上是
select.select()
的几种替代品,它们在不同的平台上都可以很好地工作。但是,它们的原理大致相似,因此最好暂时关注
select
,直到您运行代码,然后研究更改它。
select
模块与它所基于的C/C++版本非常接近)。
关于python - 如何通过套接字和选择模块管理聊天服务器(Python)的套接字连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15231861/
我正在维护一些 Java 代码,我目前正在将它们转换为 C#。 Java 代码是这样做的: sendString(somedata + '\000'); 在 C# 中,我正在尝试做同样的事情: sen
如何确定函数中传递的参数是字符串还是字符(不确定如何正确调用它)文字? 我的函数(不正确): void check(const char* str) { // some code here }
我真的不知道如何准确地提出这个问题,但我希望标题已经说明了这一点。 我正在寻找一种方法(一个框架/库),它提供了执行 String.contains() 函数的能力,该函数告诉我给定的字符串是否与搜索
我正在尝试编写一些读取 Lambda 表达式并输出 beta 缩减版本的东西。 Lambda 的类型如下:\variable -> expression,应用程序的形式为 (表达式) (表达式)。因此
StackOverflow 上的第 1 篇文章,如果我没能把它做好,我深表歉意。我陷入了一个愚蠢的练习,我需要制作一个“刽子手游戏”,我尝试从“.txt”文件中读取单词,然后我得到了我的加密函数,它将
我想在 Groovy 中测试我的 Java 自定义注释,但由于字符问题而未能成功。 Groovyc: Expected 'a' to be an inline constant of type cha
当我尝试在单击按钮期间运行 javascript location.href 时,出现以下错误“字 rune 字中的字符过多”。 最佳答案 这应该使用 OnClientClick相反? 您可能还想停
我想要类似的东西: let a = ["v".utf8[0], 1, 2] 我想到的最接近的是: let a = [0x76, 1, 2] 和 "v".data(using: String.Encod
有没有办法在 MySQL 中指定 Unicode 字 rune 字? 我想用 Ascii 字符替换 Unicode 字符,如下所示: Update MyTbl Set MyFld = Replace(
阅读 PNG 规范后,我有点惊讶。我读过字 rune 字应该用像 0x41 这样的二进制值进行硬编码,而不是在(程序员友好的)'A' 中。问题似乎是在具有不同底层字符集的不同系统上编译期间字 rune
考虑一个具有 UTF-8 执行字符集的 C++11 编译器(并且符合要求 char 类型为有符号 8 位字节的 x86-64 ABI) . 字母 Ä(元音变音)具有 0xC4 的 unicode 代码
为什么即使有 UTF-8 字符串文字,C11 或 C++11 中也没有 UTF-8 字 rune 字?我知道,一般来说,字 rune 字表示单个 ASCII 字符,它与单字节 UTF-8 代码点相同,
我怎样才能用 Jade 做到这一点? how would I do this 我几乎可以做任何事情,除了引入一个 span 中间句子。 最佳答案 h3.blur. how would I do t
这似乎是一个非常简单的问题,但我只是想澄清我的疑问。我正在查看其他开发人员编写的代码。有一些涉及 float 的计算。 示例:Float fNotAvlbl = new Float(-99); 他为什
我想知道第 3 行“if dec:”中的“dec”是什么意思 1 def dec2bin(dec): 2 result='' 3 if dec:
我试图在字符串中查找不包含任何“a”字符的单词。我写了下面的代码,但它不起作用。我怎么能对正则表达式说“不包括”?我不能用“^”符号表示“不是”吗? import re string2 = "asfd
这个问题在这里已经有了答案: Is floating point math broken? (31 个答案) Is floating point arbitrary precision availa
我正在创建一个时尚的文本应用程序,但在某些地方出现错误(“字 rune 字中的字符太多”)。我只写了一个字母,但是当我粘贴它时,它会转换成许多这样的字母:“\uD83C\uDD89”,原始字母是“🆉
我正在尝试检查用户是否在文本框中输入了一个数字值,是否接受了小数位。非常感谢任何帮助。 Private Sub textbox1_AfterUpdate() If IsNumeric(textbox1
我知道一个 Byte 是 8 位,但其他的代表什么?我正在参加一个使用摩托罗拉 68k 架构的汇编类(class),我对目前的词汇感到困惑。 最佳答案 如 operator's manual for
我是一名优秀的程序员,十分优秀!