我需要帮助来了解线程利用情况

时间:2019-04-22 22:32:15

标签: python multithreading networking server client

我正在使用命令和其他类型的东西设置一个小的服务器,但是,我没有线程功能,当我连接服务器时,一切似乎都很好,我可以连接第一个客户端“而无需问题,但是当我想连接另一个客户端时,它永远不会被连接,代码会运行,但是我可以发送任何我希望它永远不会显示在另一个客户端或服务器上的东西。

我已经阅读了Threading文档,但是即使有示例,我还是听不懂,有人可以给我一些有关如何继续处理多个客户端的线索吗?

服务器代码

#!/usr/bin/python3+x
import socket
import sys
from time import sleep
import threading
import random

HOST = "localhost"
PORT = 33700

MSG_SIZE = 32768
serveur_on = True


CLIENT_NICK_CHAN = {} #clients" nicks dict like {nickname : channel} -> needed to easily know who is where
CLIENT_NICK_SOCKET = {} #clients" dict like {nickname : socket} -> needed to send private message to the nickname's socket easily
CLIENT_NICK_THREAD = {} #clients" dict with like {nick : thread} 


Rand_disconnection_msg = [" has drown in the abyss.", " is disconnected.", " is now in a better place.", " is now a part of our past", " passed away, in really tragic circumstances..."]
CHANNELS = ["main lobby", "test"]
CMD_LIST = [b"HELP",b"NICK",b"JOIN",b"CHANNELS",b"LEAVE"]
COMMANDS = ["/NICK <nickname>: Use only when you\'re connecting, allow you to choose a unique nickname",
            "/JOIN <channel_name>: Allow you to join or create a channel, you can\'t use this command if your are already in another channel than the" + CHANNELS[0],
            "/CHANNELS : Allow you to see every channels on the server with every connected people",
            "/LEAVE : You leave the channel your within and get bringed back to the" + CHANNELS[0],
            "/HELP : Gives you the whole command list",
            "/BYE : Disconnect ou from the server, you have to in the " + CHANNELS[0] + " to use this command"
            ]


class Colors:
    Blue, Cyan, Green, Red, Magenta, Yellow, White =b"\033[94m", b"\033[96m", b"\033[92m", b"\033[91m", b"\033[95m", b"\033[93m", b"\033[0m"
    Normal, Bold, Italics, Thin = b"\033[0m", b"\033[1m", b"\x1B[3m", b"\033[2m"


class thread_client(threading.Thread):
    def __init__(self,conn):
        self.nom = ""
        if(self.nom == ""):
            nickname_input(connexion, self)
            print("nom : " + self.nom.decode("utf8"))
        self.channel = CHANNELS[0]
        self.admin = False
        self.adress = ""
        threading.Thread.__init__(self)
        self.connexion = conn
        print("init done")


    def run(self):   
        while True:
            msgClient = self.connexion.recv(MSG_SIZE)
            if not msgClient or msgClient == b"BYE":
                break
            print(type(self.nom))
            print(type(msgClient))
            str_name = self.nom.decode("utf8")
            msg = str_name + " > " + msgClient.decode("utf8")
            print("string type name is : " + str_name + "\n")
            str_msg = msgClient.decode("utf8")
            print("{} > {}".format(str_name, str_msg))
            for clients in CLIENT_NICK_SOCKET:
                if clients != self.nom:
                    CLIENT_NICK_SOCKET[clients].send(bytes(str_msg,"utf8"))
        self.connexion.send(b"You are now disconnected.\n")
        self.connexion.close()
        del CLIENT_NICK_SOCKET[self.nom.decode("utf8")]
        del CLIENT_NICK_CHAN[self.nom.decode("utf8")]
        rand_leave = random.randint(0, len(Rand_disconnection_msg)-1)
        leaving_msg = Rand_disconnection_msg[rand_leave]
        print(str_name + leaving_msg + "\n")


def nickname_input(client_socket, thread):
    print("now in input nickname")
    msg_nom = client_socket.recv(MSG_SIZE)
    print("msg_nom = " + msg_nom.decode("utf8"))
    msg_nom_arr = msg_nom.split()
    if not msg_nom_arr[0]:
        client_socket.send(b"Please send a non void message")
        nickname_input(client_socket, thread)
    print("msg_nom_arr[0] = " + str(msg_nom_arr[0]))
    if(msg_nom_arr[0] == b"NICK"):
        if(len(msg_nom_arr)== 1):
            client_socket.send(b"Please do not just enter '/NICK' -> you have to type '/NICK <your_nickname>', please proceed again :\n")
            nickname_input(client_socket, thread)
        else:
            thread.nom = msg_nom_arr[1]
    else:
        client_socket.send(b"It seems like you forgot to use '/NICK' before entering your nickname, please proceed again:\n")
        nickname_input(client_socket, thread)
    return   


def print_channels(client_socket, thread):
    client_socket.send(b"Here\'s the current channel list :\n\n")
    for chan in CHANNELS:
        sleep(0.70)
        client_socket.send( bytes(chan,"utf8") + b":\n    current members :\n")
        for chan_user in CLIENT_NICK_CHAN:
            if(CLIENT_NICK_CHAN[chan_user] == chan):
               sleep(0.35)
               if(chan_user == thread.nom):
                    if(thread.admin):
                       client_socket.send(b"          " +Colors.Bold + Colors.Yellow + b"@"+ thread.nom + b"@" + Colors.Normal + b"\n")
                    else:
                        client_socket.send(b"          " +Colors.Bold + Colors.Yellow + thread.nom + Colors.Normal + b"\n")
               else:
                    client_socket.send(b"       " +bytes(chan_user,"utf8") +  b"@\n")
        client_socket.send(b"\n")
    client_socket.send(b"\n")
    return


def join_channel(client_socket, thread, data, data_array):
    if(not data_arr[1]):
        connexion.send(b"Please select a channel you want to join using '/JOIN <channel_name>'\nNote that if the channel you asked for doesn\'t exists a new channel <channel_name> will be created and you will be the administrator of this channel")
        return
    else:
        asked_channel = data_arr[1]
        if( not (asked_channel in CHANNELS)):
            thread.channel = asked_channel
            thread.admin = True
            connexion.send(b"Welcome in " + asked_channel + b" channel, since you\'re the on who created this channel you are granted as administrator for this channel")
            connexion.send(b"Note that being administrator allow you tu use some new commands as '/GRANT', '/REVOKE' or  '/REN', for more information please use '/HELP'")
        else:
            thread.channel = asked_channel
            connexion.send(b"Welcome in " + asked_channel + b" channel !")
    return


SERVER = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
SERVER.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
try:
    SERVER.bind((HOST,PORT))
except socket.error:
    print("Server connexion failed")
    sys.exit()
print("Server is now connected\nWaiting for connexions...\n")


SERVER.listen(5)


connexion, adresse = SERVER.accept()
thread = thread_client(connexion)
thread.start()

print("thread type = " +str(type(thread)) +"\n")
print("thread = ")
print(thread)
connexion.send(bytes("Welcome ","utf8") + Colors.Yellow + Colors.Bold + thread.nom + Colors.Normal)
nick = thread.nom #type -> bytes
str_nick = nick.decode("utf8")
CLIENT_NICK_CHAN[str_nick] = thread.channel
CLIENT_NICK_SOCKET[str_nick] = connexion
CLIENT_NICK_THREAD[str_nick] = thread
print("client list : ")
print(CLIENT_NICK_CHAN)
print("\n")
print("CLIENT_NICK_SOCKET = ")
print(CLIENT_NICK_SOCKET)
print("\n")
while serveur_on:
    conn_msg = str_nick + " joined the chat\n"
    print(conn_msg)
    connexion.send(b"hello world 3\n\n")
    connexion.send(b"*" * 80 + b"\n")
    connexion.send(Colors.Red + Colors.Bold + b"\nWELCOME IN THE MAIN LOBBY \n" + Colors.Normal+b"\nTo enter a channel use '/JOIN <channel_name>'\nthe <channel_name> have to be composed by one world or use underscores to join words\nIf the channel does not exists a new one will be created\n\nNote that you have to be in another channel than the main lobby to chat\n")
    print_channels(connexion, thread)
    connexion.send(b"*" * 80 + b"\n\n")
    while True:
        print("thread list = ")
        print(CLIENT_NICK_THREAD)
        data = connexion.recv(MSG_SIZE) #receiving data from client
        data_arr= data.split() #convert data into an array to check if the first word in the message is "MSG" or not
        print(str_nick +" is now in -> " + thread.channel + "\n") 
        if(data_arr[0] in CMD_LIST):
            if(data.startswith(b"HELP")): #HELP CMD
                for command in COMMANDS:
                    connexion.send(bytes(command,"utf") + b"\n")
            if(data.startswith(b"CHANNELS")): #Channels + current members CMD
               connexion.send(b"\n")
               print_channel(connexion, thread)
               connexion.send(b"\n")
            if(data.startswith(b"JOIN")):
                join_channel(connexion, thread, data, data_arr)
                connexion.send(b"\n")



        else:
            if ((thread.channel != CHANNELS[0]) and (data.startswith("MSG"))):
                for chan_user in thread.channel:
                    chan_user.send(nick + b" > " + bytes(data,"utf8"))
                    print("data = " + data)
            elif (thread.channel == CHANNELS[0]):
                connexion.send(b"You have to be in another channel than the " + bytes(CHANNELS[0], "utf8") + b" to start chating !\nPlease use '/JOIN <channel_name>' or '/HELP' to learn how to join another channel.\n\n")

和客户端代码:

#!/usr/bin/python3+x
host = ''
port = 33700
MSG_SIZE = 32768
emission_stop = False

import socket
import sys
import threading
import time


def Tsend():
    while True:
        msg_envoi = input("> ")
        if msg_envoi.startswith("/"):
            msg_envoi = msg_envoi.replace("/","",1)
        else:
            msg_envoi = msg_envoi
        CLIENT.send(bytes(msg_envoi,"utf8"))
        if emission_stop:
            CLIENT.close()

def Trecv():
    while True:
        msg_recu = CLIENT.recv(MSG_SIZE).decode("utf8")
        print("\n" + msg_recu)
        if not msg_recu:
            break
    emission_stop = True
    print("connexion lost\n")
    CLIENT.close()

CLIENT = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
try:
    CLIENT.connect((host,port))
except socket.error:
    print("connexion failed\n")
    sys.exit()
print("Now connected to the server on port: {}\n".format(port))
print("Please now enter your nickname using '/NICK'\n")
thread_emission = threading.Thread(target = Tsend)
thread_reception = threading.Thread(target = Trecv)
thread_emission.start()
thread_reception.start()

我想要的只是让多个客户端互相交谈,但我什至不能获得两个客户端。

1 个答案:

答案 0 :(得分:1)

我能看到的最大问题是,您只打一次SERVER.accept()。这意味着您将只接受1个客户端连接。照原样使用阻塞套接字时,一种典型的方法是在循环中继续执行SERVER.accept(),以便您可以继续接受所有客户端套接字。 accept()新套接字之后,创建专用于该套接字的发送/接收的新线程,这样就不会阻塞接受线程。然后,您继续接受更多连接。像这样:

#SERVER:
while serveur_on:
    connexion, adresse = SERVER.accept()

    # Possibly do some limited IO with client socket here, but be careful not
    # to block this thread too long because that will prevent more clients from
    # connecting.

    thread = thread_client(connexion)
    thread.start()

    # No more client IO on this thread, it's the client thread's job now.

您似乎在两个不同的地方都有与客户端通信的代码(接收消息和发送响应):SERVER.accept()之后的主线程上,thread_client.run()上的主线程上。这样没有意义,应该全部放在thread_client.run()中。