Skip to content
Advertisement

Python non-blocking socket

Hi I am trying to create a very simple peer-to-peer chatting program in python. The first user can runs the server.py program below to bind to a socket.

import sys
import socket
import select
import threading

# Bind to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 11111))
s.listen()

def chat(conn, addr):

    # Set blocking to false so that program can send and receive messages at the same time
    conn.setblocking(0)

    # Receive messages using select
    while conn in select.select([conn], [], [], 0)[0]:
        text = conn.recv(4096)
        if text:
            print("{}: {}".format(addr, text))
        else:
            return
    
    # get user input and send message
    while True:
        msg = input(">>>")
        conn.send(msg.encode())
            


if __name__ == '__main__':
    
    ## Accept connections and start new thread
    (conn, addr) = s.accept() 
    threading.Thread(target=chat, args=([conn, addr])).start()

Then another user can use netcat to connect to the server and communicate. However, the program is only able to get the user’s input and send to the other side. The user from the other side is unable to send messages.

One sided communication

Advertisement

Answer

input() blocks, so you are falling through your chat function and entering the input() loop and never checking for receiving again. Receive on the thread and enter the input loop on the main thread. TCP is full duplex so you can send/recv at the same time on two threads without turning off blocking.

I also needed to add a newline to the send() as my netcat was line-buffering.

import socket
import threading

# Bind to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 11111))
s.listen()

def chat(conn, addr):
    while True:
        text = conn.recv(4096)
        if not text: break
        print("{}: {}".format(addr, text))


if __name__ == '__main__':
    ## Accept connections and start new thread
    conn, addr = s.accept()
    threading.Thread(target=chat, args=(conn, addr), daemon=True).start()

    # get user input and send message
    while True:
        msg = input(">>>")
        conn.sendall(msg.encode() + b'n')
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement