Search code examples
pythonsocketsblocking

Why is this socket.recv() blocking?


I am working on a simple chat server, storing the connected clients in a dict. I set blocking to false, but it appears everything is holding up while polling for user input.

I can connect with one client, the server shows that it will receive and broadcast a message successfully, but when another client tried to connect, it cannot until the first user "shakes it loose" by submitting another message.

server.py

import socket
import time
import threading

def handle_new_connection(connection):
    def handle_t():
        while 1:
            connection.send("Please enter desired username:  ")
            try:
                username = connection.recv(256)
                if username in connections:
                    connection.send("Username taken.")
                elif username:
                    connections[username] = connection
                    connection.send('Welcome to chat, ' + username)
                    print (username + ' has entered chat.')
                    return
            except socket.error as e:
                print e
                break
    handle = threading.Thread(target=handle_t, args=())
    handle.daemon = False
    handle.start()

def broadcast(sender, message):
    print 'Broadcasting:  ' + message
    for username, conn in connections.items():
        if username != sender:
            try:
                conn.send(''.join((sender, ": ", message)).encode('utf-8'))
            except socket.error as e:
                print e
                pass

serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setblocking(False)
serverSocket.bind(('', 55555))
serverSocket.listen(5)

connections = {}

print 'Booting Server...'

while 1:
    try:
        while 1:
            try:
                conn, addr = serverSocket.accept()
                print ("New Incoming Connection")
            except socket.error as e:
                break
            handle_new_connection(conn)

        for user, conn in connections.items():
            print 'polling for ' + user
            try:
                raw_data = conn.recv(256) # <-- HANGING HERE
                data = raw_data.decode('utf-8')
            except socket.error as e:
                continue

            if data:
                print data
                broadcast(user, data)
            else:
                del connections[user]
                broadcast(user, 'left chat. ' + str(len(connections)) + ' users.')
                print user + " left chat. " + str(len(connections)) + " users."
    except (SystemExit, KeyboardInterrupt):
        break

print 'Closing Server Socket...'
serverSocket.close()    
exit()

I am using the following script in another terminal to connect.

client.py

import socket
import threading

def create_sender():
    def sender_t():
        while 1:
            try:
                while 1:
                    try:
                        data = raw_input()
                        clientsocket.send(data.encode('utf-8'))
                    except socket.error as e:
                        break
                    except (SystemExit, KeyboardInterrupt):
                        break
            except (SystemExit, KeyboardInterrupt):
                break

    send = threading.Thread(target=sender_t, args=())
    send.start()

clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.connect(('', 55555))
clientsocket.setblocking(False)
print 'Connected to server.'

create_sender()
while 1:
    try:
        while 1:
            try:
                msg = clientsocket.recv(256).decode('utf-8')
                print msg
            except socket.error as e:
                break

    except (SystemExit, KeyboardInterrupt):
        break

clientsocket.close()

I think the problem is in how I'm declaring my socket, though I'm not positive I'm using threads properly either. Thanks!


Solution

  • The issue is that accept returns a new socket and, "initially all sockets are in blocking mode." (from https://docs.python.org/2/library/socket.html). So you need to add a conn.setblocking(0) right after the accept call returns.