简体   繁体   English

如何在Python中使用套接字接受两个连接

[英]How to accept two connections using sockets in Python

I am working on a chat program. 我正在做一个聊天程序。 But right now it can only except one client. 但是目前只能有一个客户。 How would I make it to where it can accept two clients? 我要如何使其能够接受两个客户? I am still a bit of a noob when it comes to sockets so can you explain very thoroughly? 在套接字方面,我还是有点菜鸟的,所以您可以很详尽地解释一下吗?

Server Code: 服务器代码:

import socket

def mainFunc():
    host = ""
    port = 50000

    ipList = []
    nickNameList = []
    num = True

    s = socket.socket()
    s.bind((host, port))

    s.listen(1)
    c, addr = s.accept()

    print("Connection from: " + str(addr) + "\n")
    ipList.insert(0, str(addr))

    while True:
        data = c.recv(1024)
        if not data:
            break

        if num == True:
            nickNameList.insert(0, str(data))
            num = False
        else:
            print("From " + nickNameList[0] + ": " + str(data) + "\n")

            message = raw_input("Message you want to send: ")
            print("\n")

            c.send(message)

    c.close()

I have tried changing the s.listen(1) to s.listen(2). 我尝试将s.listen(1)更改为s.listen(2)。 But that did not seem to allow a second person to connect. 但这似乎不允许第二个人进行联系。 Can someone explain why? 有人可以解释为什么吗?

One call to accept accepts one connection. 一个accept呼叫将accept一个连接。 To accept two connections, call accept twice. 要接受两种连接方式,呼叫accept两次。

If you want two connections in sequence, but never more than one at a time, you just need a loop around the c, addr = s.accept() and everything that follows it. 如果要顺序连接两个,但一次不超过一个, c, addr = s.accept()需要围绕c, addr = s.accept()的循环c, addr = s.accept()及其c, addr = s.accept()所有内容。 Then it'll accept one connection, handle it until the socket closes and your break executes, then handle the second connection, and so on. 然后它将接受一个连接,处理该连接,直到套接字关闭并执行break ,然后处理第二个连接,依此类推。

In this case, the listen backlog—the 2 in your s.listen(2) —means it'll queue up no more than 2 waiting connections while you're processing the first one; 在这种情况下, listen积压(即s.listen(2)在处理第一个连接时,它不会排队等待2个以上的等待连接。 anyone after that will get rejected. 之后的任何人都会被拒绝。


If you want two simultaneous connections, you have to do one of two things: 如果要同时进行两个连接,则必须执行以下两项操作之一:

  • Multithreading, with a thread for each connection. 多线程,每个连接都有一个线程。 (Multiprocessing, and magic green-threading a la gevent , are basically the same idea.) (多处理和神奇的绿色线程gevent是相同的想法。)
  • Multiplexing, with a reactor or proactor handling non-blocking or asynchronous I/O for all of the connections instead of just directly calling a socket with a single connection. 进行多路复用,反应器或proactor为所有连接处理非阻塞或异步I / O,而不是仅通过单个连接直接调用套接字。 (There are many variations on this idea, from coroutine schedulers like asyncio to simple loops around select .) (这个想法有很多变化,从协程调度程序(例如asyncio到围绕select简单循环)。)

In this case, the listen backlog is really only important if your program is too slow to keep up with connections as they come in. When that happens, it's usually better to refuse new connections than to accept them and slow things down even further, so keeping a small backlog is a good idea. 在这种情况下,仅当您的程序太慢而无法跟上进入连接的速度时, listen积压才真正重要。在这种情况下,拒绝新连接通常比接受新连接并进一步放慢速度要好,因此保持少量积压是一个好主意。

But since your connection handler blocks on raw_input after each socket messages, this is going to be a weird design, to say the least. 但是由于您的连接处理程序会在每个套接字消息之后阻塞raw_input ,至少可以这样说,这将是一个怪异的设计。 (Not the blocking part—you can fix that by assigning a thread, select entry, coroutine, etc. to stdin. But what actually happens with the input. You've got 8 connections, and only 1 input. Which connection gets the result when the user types something?) (不是阻塞部分,您可以通过为stdin分配线程, select条目,协程等来解决此问题。但是输入实际上会发生什么。您只有8个连接,只有1个输入。哪个连接可以得到结果用户何时输入内容?)


Here's a simple threaded server: 这是一个简单的线程服务器:

def connection(c, addr):
    while True:
        # your existing while True loop

while True:
    c, addr = s.accept()
    t = threading.Thread(target=connection, args=(c, addr))
    t.start()

However, for a realistic server that you want to be able to shut down in some way, you're going to want to provide some way to shut down the connection threads. 但是,对于您希望能够以某种方式关闭的现实服务器,您将需要提供某种方式来关闭连接线程。 Also, for servers that interact between clients (like sending one user's chat messages to all of the other users), you need some way to pass messages between the threads, or to share information between them. 同样,对于在客户端之间进行交互的服务器(例如向一个用户发送聊天消息给所有其他用户),您需要某种方式在线程之间传递消息或在线程之间共享信息。 Often you end up needing two threads per connection—one to block on c.recv , and another one to block on a queue and call c.send with other users' messages. 通常,每个连接最终需要两个线程-一个线程在c.recvc.recv ,另一个线程在队列上c.recv ,并与其他用户的消息一起调用c.send


For a multiplexing server, the different approaches look very different, but there are good examples for all of them. 对于多路复用服务器,不同的方法看起来非常不同,但是所有方法都有很好的示例。 See asyncio , selectors for their examples, Socket Programming HOWTO for the select examples, and Google for examples for third-party libraries like Twisted, gevent, etc. asyncioselectors了他们的榜样, Socket编程HOWTOselect的例子,而谷歌对于像扭曲,GEVENT等第三方库的例子


As a side note, you seem to be expecting that send is guaranteed to send an entire message in one go, and that the other side's recv will receive that entire message and nothing else. 附带说明一下,您似乎希望send肯定能一次发送完整的消息,而另一端的recv将接收到整个消息,而不会收到其他任何消息。 TCP guarantees no such thing. TCP不能保证没有这种事情。 See Sockets are byte streams, not message streams for more details. 有关更多详细信息请参见套接字是字节流,而不是消息流

Also, in nickNameList.insert(0, str(data)) , what is the str for? 另外,在nickNameList.insert(0, str(data))str是什么? In Python 2.x, data is already a str , so this just wastefully makes an extra copy for no good reason. 在Python 2.x中, data已经是一个str ,因此这没有充分的理由浪费了额外的副本。 In Python 3.x, data is a bytes , so this converts that into its string representation, like "b'0Cool'" instead of "0Cool" , which is almost certainly not what you want. 在Python 3.x中, data是一个bytes ,因此这会将其转换为字符串表示形式,例如"b'0Cool'"而不是"0Cool" ,这几乎肯定不是您想要的。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM