如何在Socket编程聊天应用程序中实现线程

2024-06-16 12:46:44 发布

您现在位置:Python中文网/ 问答频道 /正文

因此,我也包括了代码的服务器端,但问题在于客户端。这是一个简单的TCP客户端套接字代码。问题是在第21行,在第一个while循环之后(我也在代码中对我所指的位置进行了注释),我请求用户输入

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

现在我知道线程需要在这里完成,但我还没有足够的相关知识。因此,如果有人能友好地指导我或帮助我修改代码,使聊天得到更新,而无需输入

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没有显示它。直到我输入一些东西并按下回车键,它才会出现

非常感谢:)


Tags: 代码clientmessagedataifserverusernamesocket
1条回答
网友
1楼 · 发布于 2024-06-16 12:46:44

为什么不像对服务器那样对客户端使用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限制)

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

相关问题 更多 >