【问题标题】:How to implement threading in a Socket Programming Chat Application如何在套接字编程聊天应用程序中实现线程
【发布时间】:2020-10-25 04:49:47
【问题描述】:

所以我还包括代码的服务器端,但客户端的问题。这是一个简单的 TCP 客户端套接字代码。问题是在第 21 行,在第一个 while 循环之后(我还在代码中注释了我所指的地方),我要求用户输入。

然后发生的情况是,当更多用户连接时,任何用户的聊天屏幕都不会更新,除非他们按下回车键,如您所见,只有在向“消息”变量提供输入后才会继续。

现在我知道线程需要在这里完成,但我并没有真正获得足够的相关知识。因此,如果有人可以指导我或帮助我修改代码,以便无需输入即可更新聊天。

Cient 代码(第 21 行的问题,在第一个 while 循环之后...已注释)

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

服务器代码(如果有人需要):

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]

我还包含了图像...正如您所见,当我在客户端 1 的窗口中输入 hello 时,客户端 2 没有显示它。直到我输入一些东西并按回车后才会这样做

非常感谢:)

【问题讨论】:

    标签: python multithreading sockets networking websocket


    【解决方案1】:

    为什么不像 服务器 那样将select.select 用于客户端? 效果很好。

    Linux 版本

    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 版本(由于select 限制)

    注意: Windows 上的文件对象是不可接受的,但套接字是。在 Windows 上, 底层的 select() 函数由 WinSock 库提供,并不 处理并非源自 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()
    

    【讨论】:

    • 我试过了,但它给出了错误:“read_sockets, write_sockets, error_sockets = select.select(socket_list, [], []) OSError: [WinError 10038]不是套接字” .... 我认为这可能与将 sys.stdin 作为套接字列表的输入有关
    • 这是个问题。因为我的代码可以在 Linux 上运行。但是 Windows 显然不允许使用带有“选择”功能的文件。
    • 哦...那么有没有解决方法或者我应该将代码转移到虚拟机上的 linux 并从那里继续...非常感谢:)
    • 谢谢你这么多
    猜你喜欢
    • 2016-04-30
    • 1970-01-01
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 2017-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多