运行界面如下:
总结一下代码的客户端和服务端的运行顺序及逻辑:
服务端逻辑
1. 启动服务器
启动tinkter主界面及相关窗口。
通过start_server函数启动服务器线程,在该线程中服务器创建UDP socket并绑定到127.0.0.1:8888。
2. 等待接收客户端消息
服务器线程通过recvfrom接收消息。
在接收到客户端连接的初始消息(例如“Client connected”)后,记录客户端的地址和端口(例如('127.0.0.1', 56913)),并显示在文本框中。
3. 发送消息
服务器用户输入消息并点击发送按钮。
send_message函数将消息通过sendto发送到记录的客户端地址和端口。
如果没有客户端连接,则提示“没有客户端连接”。
4. 关闭服务器
点击“退出聊天”按钮,触发quit_chat函数,设置停止事件,关闭服务器socket,并终止tinkter主界面。
客户端逻辑
1. 启动客户端
启动tinkter主界面及相关窗口。
通过start_link函数启动连接线程,在该线程中客户端创建UDP socket并向服务器(127.0.0.1:8888)发送初始连接消息。
2. 等待接收服务器消息
客户端线程通过recvfrom接收来自服务器的消息,如果收到则在文本框中显示。
3. 发送消息
客户端用户输入消息并点击发送按钮。
send_message函数将消息通过sendto发送到服务器地址和端口(127.0.0.1:8888)。
4. 关闭客户端
点击“退出聊天”按钮,触发quit_chat函数,设置停止事件,关闭客户端socket,并终止tinkter主界面。
详细流程
1. 服务器启动
显示tinkter界面。
服务器socket监听127.0.0.1:8888。
2. 客户端启动
显示tinkter界面。
客户端socket连接服务器,并发送初始连接消息。
3. 服务器接收消息
接收到客户端初始连接消息后,记录客户端地址和端口。
显示客户端信息在文本框中。
4. 数据传递
服务器/客户端用户输入消息,点击发送按钮。
消息通过UDP socket发送到指定地址和端口。
对方接收消息,并在文本框中显示。
5. 结束通信
服务器/客户端用户点击退出按钮。
关闭socket连接,终止tinkter主界面。
服务端代码:
import tkinter
import threading
from socket import socket, AF_INET, SOCK_DGRAM
import time
def server_interface(win):
def start_server():
global server_socket, client_addr
server_socket = socket(AF_INET, SOCK_DGRAM)
server_socket.bind(('127.0.0.1', 8888))
print("Server started and listening on port 8888")
threading.Thread(target=receive_message, daemon=True).start() # 在服务器启动后再启动接收消息的线程
def receive_message():
global client_addr
while not stop_event.is_set():
try:
server_socket.settimeout(1.0) # 设置超时时间,以便在需要时能退出
recv_data, client_addr = server_socket.recvfrom(1024)
print(f"Server received data from {client_addr}")
if recv_data:
text_box.insert(tkinter.END, 'A: ' + recv_data.decode('utf-8') + '\n')
except OSError:
continue # 超时异常处理,继续等待接收新消息
except Exception as e:
print(f"Server receive error: {e}")
continue
def send_message():
global client_addr
user_input = message.get()
text_box.insert(tkinter.END, 'B: ' + user_input + '\n')
message.set('') # 清空 Entry 内容
if client_addr is not None:
try:
server_socket.sendto(user_input.encode('utf-8'), client_addr)
print(f"Server sent data to client {client_addr}")
except Exception as e:
print(f"Server send error: {e}")
else:
text_box.insert(tkinter.END, 'No client connected.\n')
def quit_chat():
stop_event.set()
server_socket.close()
win.quit()
global message
message = tkinter.StringVar()
client_addr = None
win.geometry('500x500+800+200')
win.title('服务端聊天室')
win.config(background='light yellow')
label = tkinter.Label(win, text='欢迎来到服务端聊天室', font='宋体')
label.config(background='light yellow', fg='black', height=5, width=20)
label1 = tkinter.Label(win, text='请输入信息:', font='宋体')
server_input = tkinter.Entry(win, textvariable=message, width=50)
label.grid(row=0, column=0)
label1.grid(row=1, column=0)
server_input.grid(row=1, column=1)
btn_send = tkinter.Button(win, text='点击发送', command=send_message)
btn_exit = tkinter.Button(win, text='退出聊天', command=quit_chat)
btn_send.grid(row=2, column=1)
btn_exit.grid(row=4, column=1)
text_box = tkinter.Text(win, height=10, width=50)
text_box.grid(row=3, column=0, columnspan=2)
threading.Thread(target=start_server, daemon=True).start()
win.mainloop()
if __name__ == '__main__':
stop_event = threading.Event() # 用于控制线程停止
server_socket = None
client_addr = None
win = tkinter.Tk()
server_interface(win)
客户端代码:
import tkinter
import threading
from socket import socket, AF_INET, SOCK_DGRAM
import time
def client_interface(win):
def start_link():
global client_socket, server_addr
client_socket = socket(AF_INET, SOCK_DGRAM)
server_addr = ('127.0.0.1', 8888)
print("Client connected to server at 127.0.0.1:8888")
# 向服务器发送初始消息以确立连接
client_socket.sendto("Client connected".encode('utf-8'), server_addr)
threading.Thread(target=receive_message, daemon=True).start() # 在客户端启动后再启动接收消息的线程
def receive_message():
while not stop_event.is_set():
try:
client_socket.settimeout(1.0) # 设置超时时间
recv_data, _ = client_socket.recvfrom(1024)
print("Client received data")
if recv_data:
text_box.insert(tkinter.END, 'B: ' + recv_data.decode('utf-8') + '\n')
time.sleep(1)
except OSError:
continue # 超时异常处理,继续等待接收新消息
except Exception as e:
print(f"Client receive error: {e}")
continue
def send_message():
user_input = message.get()
text_box.insert(tkinter.END, 'A: ' + user_input + '\n')
message.set('') # 清空 Entry 内容
try:
client_socket.sendto(user_input.encode('utf-8'), server_addr)
print("Client sent data to server")
except Exception as e:
print(f"Client send error: {e}")
def quit_chat():
stop_event.set()
client_socket.close()
win.quit()
global message
message = tkinter.StringVar()
win.geometry('500x500+300+200')
win.title('客户端聊天室')
win.config(background='light blue')
label = tkinter.Label(win, text='欢迎来到客户端聊天室', font='宋体')
label.config(background='light blue', fg='black', height=5, width=20)
label1 = tkinter.Label(win, text='请输入信息:', font='宋体')
client_input = tkinter.Entry(win, textvariable=message, width=50)
label.grid(row=0, column=0)
label1.grid(row=1, column=0)
client_input.grid(row=1, column=1)
btn_send = tkinter.Button(win, text='点击发送', command=send_message)
btn_exit = tkinter.Button(win, text='退出聊天', command=quit_chat)
btn_send.grid(row=2, column=1)
btn_exit.grid(row=4, column=1)
text_box = tkinter.Text(win, height=10, width=50)
text_box.grid(row=3, column=0, columnspan=2)
threading.Thread(target=start_link, daemon=True).start()
win.mainloop()
if __name__ == '__main__':
stop_event = threading.Event() # 用于控制线程停止
client_socket = None
server_addr = None
win = tkinter.Tk()
client_interface(win)
需要注意的是:客户端-服务器(Client-Server)模型的通信中,先运行服务器是一个必要步骤!!!