Мне нужна помощь в понимании использования потоков - PullRequest
0 голосов
/ 23 апреля 2019

Я настраиваю небольшой сервер с командами и другими вещами, но я не получаю функцию потока, когда я подключаю свой сервер, кажется, что все в порядке, я могу подключить первый клиент "без "проблемы, но когда я хочу подключить другого клиента, он никогда не подключается, код запускается, но я могу отправить все, что захочу, чтобы он никогда не появлялся на другом клиенте или сервере.

Я уже прочитал документацию по 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 Ответ

1 голос
/ 23 апреля 2019

Самая большая проблема, которую я вижу, это то, что вы звоните 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.

Кажется, у вас есть код, который связывается с клиентом (получение сообщений и отправка ответов) в 2 разных местах: в главном потоке после вас SERVER.accept() и вверх в thread_client.run().Это не имеет смысла, все должно быть в thread_client.run().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...