简体   繁体   中英

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). But that did not seem to allow a second person to connect. Can someone explain why?

One call to accept accepts one connection. To accept two connections, call accept twice.

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. Then it'll accept one connection, handle it until the socket closes and your break executes, then handle the second connection, and so on.

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; 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.)
  • 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. (There are many variations on this idea, from coroutine schedulers like asyncio to simple loops around 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.

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. (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?)


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.


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.


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. TCP guarantees no such thing. See Sockets are byte streams, not message streams for more details.

Also, in nickNameList.insert(0, str(data)) , what is the str for? In Python 2.x, data is already a str , so this just wastefully makes an extra copy for no good reason. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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