想要编写带有界面的聊天程序我决定用tkinter来实现图形化功能,由于比较简易我就不利用数据库来进行用户账户密码的存储而是利用文件的方式,注册时需自己手动同意,所以是个比较私有化的程序
服务端
import json
import socket
import threading
class Chat_Server:
def __init__(self):
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.s.bind(('0.0.0.0', 4001))
self.s.listen(5)
self.accept_idea()
def accept_idea(self):
while True:
threading.Thread(target = self.login_idea, args = (self.s.accept(), ), daemon = True).start()
def login_idea(self, accept_data):
conn, addr = accept_data
print(conn, addr)
while True:
if conn.recv(1).decode('utf-8') == '1':
header_size = int(conn.recv(8).decode('utf-8'))
header = json.loads(conn.recv(header_size).decode('utf-8'))
with open('loginuser.ini', 'r', encoding = 'utf-8')as f1:
for line1 in f1:
if header['username'] == line1.strip().split()[0] and header['password'] == line1.strip().split()[1]:
conn.sendall('1'.encode('utf-8').zfill(1))
self.s_chat_idea(conn)
return
else:
pass
else:
header_size = int(conn.recv(8).decode('utf-8'))
header = json.loads(conn.recv(header_size))
with open('check.log', 'a', encoding = 'utf-8')as f1:
f1.write(header['username'] + ' ' + header['password'] + '\n')
continue
conn.sendall('0'.encode('utf-8').zfill(1))
def s_chat_idea(self, conn):
threading.Thread(target = self.s_recv_chat_idea, args = (conn, ), daemon = True).start()
threading.Thread(target = self.s_send_chat_idea, args = (conn, ), daemon = True).start()
def s_recv_chat_idea(self, conn):
with open('data.log', 'r', encoding = 'utf-8') as f:
f.seek(0, 2)
while True:
res = f.readline()
if res:
conn.sendall(res.encode('utf-8'))
def s_send_chat_idea(self, conn):
with open('data.log', 'a+', encoding = 'utf-8') as f:
while True:
f.write(conn.recv(1024).decode('utf-8'))
f.flush()
Chat_Server()
客户端
import os
import json
import time
import socket
import tkinter
import hashlib
import threading
import tkinter.messagebox
class Chat_Client:
def __init__(self):
self.root = tkinter.Tk()
self.root.geometry('800x600')
self.root.title('星聊天')
self.username = ''
self.c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.run()
self.root.mainloop()
def create_chat_text(self):
self.chat_recv_frame = tkinter.Frame(self.root)
self.chat_recv_frame.pack(anchor = tkinter.W)
self.chat_recv_text = tkinter.Text(self.chat_recv_frame, state = tkinter.DISABLED)
self.chat_recv_text.pack(side = tkinter.LEFT, padx = 30, pady = 15)
self.chat_send_frame = tkinter.Frame(self.root)
self.chat_send_frame.pack(anchor = tkinter.W)
self.chat_send_text = tkinter.Text(self.chat_send_frame, state = tkinter.DISABLED)
self.chat_send_text.pack(side = tkinter.LEFT, padx = 30, pady = 10)
self.chat_send_button = tkinter.Button(self.chat_send_frame, text = '发送', width = 20, height = 10, command = self.c_send_chat_idea)
self.chat_send_button.pack(side = tkinter.LEFT, padx = 10)
def create_menu_idea(self):
self.menu = tkinter.Menu(self.root)
self.root.config(menu = self.menu)
self.choose_menu = tkinter.Menu(self.menu, tearoff = False)
self.menu.add_cascade(label = '选项', menu = self.choose_menu)
self.choose_menu.add_command(label = '登录', command = self.login_window_button)
self.choose_menu.add_command(label = '连接地址', command = self.connect_server_button)
def connect_server_button(self):
self.toplevel1 = tkinter.Toplevel(self.root)
self.toplevel1.geometry('300x100')
self.toplevel1.title('连接地址')
self.toplevel1_frame = tkinter.Frame(self.toplevel1)
self.toplevel1_frame.pack(pady = 30)
self.toplevel1_host_label = tkinter.Label(self.toplevel1_frame, text = 'IP地址:')
self.toplevel1_host_label.pack(side = tkinter.LEFT)
self.toplevel1_host_entry = tkinter.Entry(self.toplevel1_frame)
self.toplevel1_host_entry.pack(side = tkinter.LEFT)
self.toplevel1_port_label = tkinter.Label(self.toplevel1_frame, text = '端口号:')
self.toplevel1_port_label.pack(side = tkinter.LEFT)
self.toplevel1_port_entry = tkinter.Entry(self.toplevel1_frame)
self.toplevel1_port_entry.pack(side = tkinter.LEFT)
self.toplevel1_button = tkinter.Button(self.toplevel1, text = '设置并连接', command = self.set_ipport_button)
self.toplevel1_button.pack()
def set_ipport_button(self):
with open('config.ini', 'w', encoding = 'utf-8')as f1:
f1.write(self.toplevel1_host_entry.get().strip() + ' ' + self.toplevel1_port_entry.get().strip())
self.connect_server()
def connect_server(self):
try:
with open('config.ini', 'r+', encoding = 'utf-8')as f1:
self.host, self.port = f1.read().strip().split()
self.c.connect((self.host, int(self.port)))
tkinter.messagebox.showinfo(title = '提示', message = '服务器连接成功')
except:
tkinter.messagebox.showwarning(title = '提示', message = '服务器连接失败')
def login_window_button(self):
self.toplevel = tkinter.Toplevel(self.root)
self.toplevel.geometry('300x300')
self.toplevel.title('登录')
self.toplevel_frame_1 = tkinter.Frame(self.toplevel)
self.toplevel_frame_1.pack(pady = 30)
self.toplevel_username_Label = tkinter.Label(self.toplevel_frame_1, text = '账户:')
self.toplevel_username_Label.pack(side = tkinter.LEFT)
self.toplevel_username_entry = tkinter.Entry(self.toplevel_frame_1)
self.toplevel_username_entry.pack(side = tkinter.LEFT)
self.toplevel_frame_2 = tkinter.Frame(self.toplevel)
self.toplevel_frame_2.pack()
self.toplevel_password_Label = tkinter.Label(self.toplevel_frame_2, text = '密码:')
self.toplevel_password_Label.pack(side = tkinter.LEFT)
self.toplevel_password_entry = tkinter.Entry(self.toplevel_frame_2, show = '*')
self.toplevel_password_entry.pack(side = tkinter.LEFT)
self.toplevel_login_button = tkinter.Button(self.toplevel, text = '登录', width = 10, command = self.login_button)
self.toplevel_login_button.pack(pady = 10)
self.toplevel_register_button = tkinter.Button(self.toplevel, text = '注册', width = 10, command = self.register_button)
self.toplevel_register_button.pack(pady = 50)
def login_button(self):
try:
self.c.sendall('1'.encode('utf-8').zfill(1))
header = {
'username': self.toplevel_username_entry.get().strip(),
'password': hashlib.sha512(self.toplevel_password_entry.get().strip().encode('utf-8')).hexdigest()
}
self.c.sendall(bytes(str(len(json.dumps(header))), 'utf-8').zfill(8))
self.c.sendall((json.dumps(header).encode('utf-8')))
if self.c.recv(1).decode('utf-8') == '1':
tkinter.messagebox.showinfo(title = '登录提示', message = '登陆成功')
self.toplevel_username_entry.delete(0, tkinter.END)
self.toplevel_password_entry.delete(0, tkinter.END)
self.choose_menu.entryconfigure(0, state = tkinter.DISABLED)
self.toplevel.destroy()
self.username = header['username']
self.username_label = tkinter.Label(self.chat_recv_frame, text = f'用户: {self.username}', font=("Helvetica", 15))
self.username_label.pack(side = tkinter.LEFT)
self.chat_send_text.config(state = tkinter.NORMAL)
threading.Thread(target = self.c_recv_chat_idea, daemon = True).start()
else:
tkinter.messagebox.showerror(title = '登录提示', message = '登陆失败')
self.toplevel.destroy()
except:
tkinter.messagebox.showerror(title = '提示', message = '未连接服务器')
self.toplevel.destroy()
def register_button(self):
try:
self.c.sendall('0'.encode('utf-8').zfill(1))
header = {
'username': self.toplevel_username_entry.get().strip(),
'password': hashlib.sha512(self.toplevel_password_entry.get().strip().encode('utf-8')).hexdigest()
}
self.c.sendall(bytes(str(len(json.dumps(header))), 'utf-8').zfill(8))
self.c.sendall((json.dumps(header).encode('utf-8')))
tkinter.messagebox.showinfo(title = '提示', message = '注册中请等待管理员同意')
self.toplevel.destroy()
except:
tkinter.messagebox.showerror(title = '提示', message = '未连接服务器')
self.toplevel.destroy()
def check_config(self):
if not os.path.isfile('./config.ini'):
with open('config.ini', 'w', encoding = 'utf-8'):
pass
def c_send_chat_idea(self):
if self.chat_send_text.get('1.0', tkinter.END).strip() != '':
self.c.sendall(f'{self.username}:{self.chat_send_text.get("1.0", tkinter.END).strip()}\n'.encode('utf-8'))
self.chat_send_text.delete('1.0', tkinter.END)
else:
tkinter.messagebox.showwarning(title = '提示', message = '发送内容不能为空')
def c_recv_chat_idea(self):
while True:
data = self.c.recv(1024).decode('utf-8')
self.chat_recv_text.config(state = tkinter.NORMAL)
self.chat_recv_text.insert(tkinter.END, data)
self.chat_recv_text.config(state = tkinter.DISABLED)
self.chat_recv_text.see(tkinter.END)
def run(self):
self.check_config()
self.create_menu_idea()
self.create_chat_text()
self.connect_server()
Chat_Client()
使用方法
由于服务端没有图形化所以会比较简洁,在使用本程序时需要创建3个文件放置在服务端,分别是check.log、data.log、loginuser.ini
首先将服务端和客户端开启,客户端左上角有一个链接地址点击它然后设置服务器的地址,如果连接成功便会提示,这里我的服务器就是本机的4001端口
接下里点击登录
进入后会出现一个弹窗,然后把你想要注册的账户名密码填上去然后点击注册
接下来账户密码就会出现在服务端的check.log里面
由于我使用了sha512加密所以很难直接看出密码是啥
接下来只需要把账户名密码复制到loginuser.ini就可以了,然后再去程序里把账号密码填上然后点击登录就OK了,如果显示登录失败或者未连接服务器请重启客户端
接下来用户名会出现在右侧,登录按钮会变得不可用
然后就可以发消息试试啦