Reputation: 2525
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?
Upvotes: 2
Views: 6154
Reputation: 365597
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:
gevent
, are basically the same idea.)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.
Upvotes: 1
Reputation: 7923
One call to accept
accepts one connection. To accept two connections, call accept
twice.
Upvotes: 0