踢功能仅适用于一个用户的Python套接字聊天

时间:2019-04-17 19:03:34

标签: python python-3.x sockets tkinter chat

这是tkinter套接字Python聊天室,用户可以在其中相互发送消息。

我遇到的问题是,当我使用此代码时:

kick = input("Enter address you want to kick: ")
if addr[0] != kick:
    conn.close()

第一次踢用户,但是当我尝试再次使用它时,kick输入不会显示。所以我想要的是,在一个用户被踢后,我希望它给我一个选择来踢另一个用户。

Server.py

import socket, threading, json

host = "127.0.0.1"
port = 4000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen()
clients = {}
addresses = {}
print(host)
print("Server is ready...")
serverRunning = True



def to_json(p):
    return bytes(json.dumps(p), "utf8")


def handle_client(conn):
    try:
        user_name = conn.recv(1024).decode('utf8')
        conn.send(to_json({'CONNECTED':user_name, 'JOINED':list(clients.values())}))
        #welcome = 'Welcome %s! If you ever want to quit, type {quit} to exit.' % user_name
        #conn.send(bytes(welcome, "utf8"))
        #msg = "%s has joined the chat" % user_name
        broadcast(to_json({'JOINED':user_name}))
        clients[conn] = user_name
        while True:
            found = False
            msg = conn.recv(1024)
            if msg != bytes("{quit}" or '**', "utf8"):
                broadcast(msg, user_name+": ")
            else:
                conn.send(bytes("{quit}", "utf8"))
                conn.close()
                del clients[conn]
                broadcast(to_json({'DISJOINED':user_name}))
                print("%s has left the chat." % user_name)
                break
            if user_name in clients.values():
                conn.send("username has been taken")
            else:
                continue
    except Exception as e:
        print('Exception: {} while connected with user: {}'.format(e, user_name))

def broadcast(msg, prefix=""):
    for sock in clients:
        sock.send(bytes(prefix, "utf8")+msg)

while True:
    conn,addr = s.accept()
    conn.send("You have connected to the server \r\n ".encode("utf8"))
    conn.send("Enter username: ".encode("utf8"))
    print("%s:%s has connected." % addr)
    addresses[conn] = addr
    threading.Thread(target = handle_client, args = (conn,)).start()
    kick = input("Enter address you want to kick: ")
    if addr[0] != kick:
        conn.close()

Client.py

import socket,threading,tkinter,json,winsound
tk = tkinter
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


def echo_data(sock):
   while True:
      try:
         msg = sock.recv(1024).decode('utf8')

         if msg[0] == '{': # JSON Message
            message = json.loads(msg)

            # We don't have the `app` object, therefore you have to handle the message here

            if 'CONNECTED' in message:
               msg_list.insert(tkinter.END, "Welcome {} If you ever want to quit, type quit to exit".format(message['CONNECTED']))
               users_online.connected(message)
            elif 'JOINED' in message:
               msg_list.insert(tkinter.END, "{} has joined the chat.".format(message['JOINED']))
               users_online.joined(message)
            elif 'DISJOINED' in message:
               msg_list.insert(tkinter.END, "{} has left the chat.".format(message['DISJOINED']))
               users_online.disjoined(message)
            else:
               print('FAIL: Unknow Message {}'.format(message))
         else:# TEXT Message
            msg_list.insert(tkinter.END, msg)
      except OSError:
         print('Terminate thread echo_data')
         msg_list.insert(tkinter.END, "Connection got closed, for unknown reason, try to reconnect.")
         connect_button.configure(state=['normal'])
         entry_field.configure(state=['disabled'])
         send_button.configure(state=['disabled'])
         return

def send(event=None):
   try:
      msg = my_msg.get()
      my_msg.set("")
      s.send(bytes(msg, "utf8"))
      if msg == "{quit}":
         s.close()
         top.quit()
   except Exception:
      top.quit()
      pass 

def on_closing(event=None):
   my_msg.set("{quit}")
   send()

def connect():
   port = 4000
   send_button.configure(state=['normal'])
   connect_button.configure(state=['disabled'])
   entry_field.configure(state=['normal'])
   print('Server address:{}'.format(entry_server_address.get()))
   host = entry_server_address.get()
   address = (host,port)
   s.connect(address)
   threading.Thread(target=echo_data, args = (s,)).start()


class LabelFrame(tk.LabelFrame):
   def __init__(self, parent, text):
      self._width = 100
      self._bg = 'white'
      super().__init__(parent, text=text, width=self._width, background=self._bg)
      self.pack(side=tk.RIGHT, fill=tk.Y)

   def joined(self, message):
      global user_online
      print('joined({})'.format(message))
      # Add a `tk.Label(self, ...` and assign `text=<username>`
      user_online = tk.Label(self, text=message['JOINED'],bg=self._bg)
      user_online.grid(row=None, column=None)

   def connected(self, message):
      print('connected({})'.format(message))
      tk.Label(self, text=message['CONNECTED'], bg=self._bg).grid()
      if 'JOINED' in message:
         for user_name in message['JOINED']:
            tk.Label(self, text=user_name, bg=self._bg).grid()

   def disjoined(self, message):
      print('disjoined({})'.format(message))
      for lbl in self.grid_slaves():
         if lbl['text'] in message.values():
            lbl.destroy()


top = tkinter.Tk()
top.title("Chat Room")
messages_frame = tkinter.Frame(top)
my_msg = tkinter.StringVar()
my_msg.set("Type your messages here.")
scrollbar = tkinter.Scrollbar(messages_frame)
msg_list = tkinter.Listbox(messages_frame, height=15, width=100, yscrollcommand=scrollbar.set)
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
users_online = LabelFrame(messages_frame, 'Users online:')
messages_frame.pack()
entry_field = tkinter.Entry(top, textvariable=my_msg)
entry_field.configure(state='disabled')
entry_field.bind("<Return>", send)
entry_field.pack()
send_button = tkinter.Button(top, text="Send", command=send)
send_button.configure(state=['disabled'])
send_button.pack()

le_frame = tkinter.Frame(top)
le_frame.pack(fill=tkinter.X)

entry_server_address = tkinter.Entry(le_frame)
tkinter.Label(le_frame, text='Server address:', width=20, anchor='w').pack(side=tkinter.LEFT, padx=5)
entry_server_address.pack(side=tkinter.LEFT, fill=tkinter.X, expand=True, padx=5)
connect_button = tkinter.Button(le_frame, text='Connect', command=connect)
connect_button.pack(side=tkinter.LEFT, padx=5)

top.protocol("WM_DELETE_WINDOW", on_closing)



tkinter.mainloop()

1 个答案:

答案 0 :(得分:1)

它在线程中运行accept(),而input()while True中正常运行

这样,线程中的循环可以接受许多客户端,而普通循环可以运行input()并等待答案。我使用端口而不是地址将连接保持在列表中并踢人。 port更好,因为两个用户不能具有相同的端口,但是他们可能具有sam地址-即。您可以在计算机的两个终端上运行两个客户端,它们的地址相同,但端口始终不同。


编辑:连接有效,您可以启动某人,但是在发送之前可能需要将字符串编码为字节,并在接收到字符串时将其从字节解码为字符串。


import socket, threading, json

host = "127.0.0.1"
port = 4000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind((host,port))
s.listen()
clients = {}
addresses = {}
print(host)
print("Server is ready...")
serverRunning = True

def to_json(p):
    return bytes(json.dumps(p), "utf8")

def handle_client(conn):
    try:
        user_name = conn.recv(1024).decode('utf8')
        conn.send(to_json({'CONNECTED':user_name, 'JOINED':list(clients.values())}))
        #welcome = 'Welcome %s! If you ever want to quit, type {quit} to exit.' % user_name
        #conn.send(bytes(welcome, "utf8"))
        #msg = "%s has joined the chat" % user_name
        broadcast(to_json({'JOINED':user_name}))
        clients[conn] = user_name
        while True:
            found = False
            msg = conn.recv(1024)
            if msg != bytes("{quit}" or '**', "utf8"):
                broadcast(msg, user_name+": ")
            else:
                conn.send(bytes("{quit}", "utf8"))
                conn.close()
                del clients[conn]
                broadcast(to_json({'DISJOINED':user_name}))
                print("%s has left the chat." % user_name)
                break
            if user_name in clients.values():
                conn.send("username has been taken")
            else:
                continue
    except Exception as e:
        print('Exception: {} while connected with user: {}'.format(e, user_name))

def broadcast(msg, prefix=""):
    for sock in clients:
        sock.send(bytes(prefix, "utf8")+msg)

#--- new code ---

addresses = dict()

def loop():
    while True:
        conn,addr = s.accept()
        conn.send("You have connected to the server \r\n ".encode("utf8"))
        conn.send("Enter username: ".encode("utf8"))
        print("%s:%s has connected." % addr)
        addresses[addr[1]] = (addr,conn)
        threading.Thread(target=handle_client, args=(conn,)).start()

threading.Thread(target=loop).start()

while True:
    print(addresses.keys())
    port = int(input('port> '))
    if port in addresses:
        addresses[port][1].close()
        print('kick:', addresses[port][0])