简体   繁体   中英

How to implement threading in a Socket Programming Chat Application

So i'm also including the server side of the code, but the issues in the client side. It's a simple TCP client socket code.The thing is that in line 21, after the first while loop (i've also commented in the code to where i'm referring to), i'm asking for user input.

What then happens is that when more user connects, the chat screen of any user doesn't gets updated unless they press enter, as you can see, it only continues after an input is given to the 'message' variable.

Now i do know that threading need to be done in here, but i've not really got enough knowledge related to that. So if someone could kindly guide me or help me modify the code so that the chat gets updated without the need to enter.

Cient Code (Issue in line 21, after first while loop...commented)

from socket import *
import select
import errno
import sys

header_length = 10
ip = "127.0.0.1"
port = 1234

my_username = input("Username: ")
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((ip, port))
client_socket.setblocking(False)

username = my_username.encode()
username_header = f"{len(username):<{header_length}}".encode()
client_socket.send(username_header + username)

while True:
    #Where my issue is
    message = input(f"{my_username} > ")

    if message:
        message = message.encode()
        message_header = f"{len(message):<{header_length}}".encode()
        client_socket.send(message_header + message)
        
    try:
        while True:
            #receive messages
            username_header = client_socket.recv(header_length)
            if not len(username_header):
                print("Connection closed by the server...")
                sys.exit()
            
            username_length = int(username_header.decode().strip())
            username = client_socket.recv(username_length).decode()

            message_header = client_socket.recv(header_length)
            message_length = int(message_header.decode().strip())
            message = client_socket.recv(message_length).decode()

            print(f"{username} > {message}")

    except IOError as e:
        if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
            print("Reading error: ", str(e))
            sys.exit()
        continue

    except Exception as e:
        print("General error: ", str(e))
        sys.exit()

Server Code (Just if someone need):

from socket import *
import select

header_length = 10
ip = "127.0.0.1"
port =  1234

server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

server_socket.bind((ip, port))
server_socket.listen()
 
socket_list = [server_socket]
clients = {}


#Handles message receiving
def recieve_message(client_socket):
    try:
        message_header = client_socket.recv(header_length)

        if not len(message_header):
            return False
        
        message_length = int(message_header.decode().strip())
        return {'header': message_header, 'data': client_socket.recv(message_length)}

    except:
        return False


print(f'Listening for connections on {ip}:{port}...')

while True:
    read_sockets, _, exception_sockets = select.select(socket_list, [], socket_list)

    for notified_socket in read_sockets:
        if notified_socket == server_socket:
            client_socket, client_address = server_socket.accept()
            user = recieve_message(client_socket)

            if user is False:
                continue

            socket_list.append(client_socket)
            clients[client_socket] = user
            print(f"Accepted new connection from {client_address[0]}:{client_address[1]} username:{user['data'].decode()}")
        
        else:
            message = recieve_message(notified_socket)

            if message is False:
                print(f"Closed connection from {clients[notified_socket]['data'].decode()}")
                socket_list.remove(notified_socket)
                del clients[notified_socket]
                continue

            user = clients[notified_socket]
            print(f"Recieved messasge from {user['data'].decode()}: {message['data'].decode()}")

            for client_socket in clients:
                if client_socket != notified_socket:
                    client_socket.send(user['header'] + user['data'] + message['header'] + message['data'])                
    
    for notified_socket in exception_sockets:
        socket_list.remove(notified_socket)
        del clients[notified_socket]

I've also included the image...as you can see as i typed hello in cliend 1's window, client 2's doesnt show it. And it will not until i input something and press enter

Thanks a lot:)

Why do you not use select.select for the client as you do for the server ? It works perfectly.

Linux version

from socket import *
import select
import errno
import sys


def prompt(username):
    sys.stdout.write(f"{username} > ")
    sys.stdout.flush()


header_length = 10
ip = "127.0.0.1"
port = 1234

my_username = input("Username: ")
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((ip, port))
client_socket.setblocking(False)

username = my_username.encode()
username_header = f"{len(username):<{header_length}}".encode()
client_socket.send(username_header + username)

while True:
    socket_list = [sys.stdin, client_socket]
    prompt(my_username)

    read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
    for socket in read_sockets:
        try:
            if socket == sys.stdin:
                message = sys.stdin.readline()
                message = message.encode()
                message_header = f"{len(message):<{header_length}}".encode()
                client_socket.send(message_header + message)
            elif socket == client_socket:
                username_header = client_socket.recv(header_length)
                if not len(username_header):
                    print("Connection closed by the server...")
                    sys.exit()

                username_length = int(username_header.decode().strip())
                username = client_socket.recv(username_length).decode()
                message_header = client_socket.recv(header_length)
                message_length = int(message_header.decode().strip())
                message = client_socket.recv(message_length).decode()
                print(f"\n{username} > {message}")

        except IOError as e:
            if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
                print("Reading error: ", str(e))
                sys.exit()
            continue
        except Exception as e:
            print("General error: ", str(e))
            sys.exit()

Windows/Linux version (because of select restriction)

Note: File objects on Windows are not acceptable, but sockets are. On Windows, the underlying select() function is provided by the WinSock library, and does not handle file descriptors that don't originate from WinSock.

import threading
from socket import *
import select
import errno
import sys


def prompt(username):
    sys.stdout.write(f"{username} > ")
    sys.stdout.flush()


def redirect_sdtin(dest):
    for ln in sys.stdin:
        dest.send(ln.encode())


header_length = 10
ip = "127.0.0.1"
port = 1234

my_username = input("Username: ")
stdin_in, stdin_out = socketpair()
threading.Thread(target=redirect_sdtin, args=(stdin_in,), daemon=True).start()

client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((ip, port))
client_socket.setblocking(False)

username = my_username.encode()
username_header = f"{len(username):<{header_length}}".encode()
client_socket.send(username_header + username)

while True:
    socket_list = [stdin_out, client_socket]
    prompt(my_username)

    read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
    for socket in read_sockets:
        try:
            if socket == stdin_out:
                message = stdin_out.recv(1024)
                message_header = f"{len(message):<{header_length}}".encode()
                client_socket.send(message_header + message)
            elif socket == client_socket:
                username_header = client_socket.recv(header_length)
                if not len(username_header):
                    print("Connection closed by the server...")
                    sys.exit()

                username_length = int(username_header.decode().strip())
                username = client_socket.recv(username_length).decode()
                message_header = client_socket.recv(header_length)
                message_length = int(message_header.decode().strip())
                message = client_socket.recv(message_length).decode()
                print(f"\n{username} > {message}")

        except IOError as e:
            if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
                print("Reading error: ", str(e))
                sys.exit()
            continue
        except Exception as e:
            print("General error: ", str(e))
            sys.exit()

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