-
目录
-
设计目的
1)理解和掌握计算机网络和基于Python的网络编程相关基础知识。
2)掌握Socket编程的基本原理和方法,实现一个基本的即时通信软件。
3)设计并使用GUI编程工具,实现软件界面。
4)实现用户注册登录功能,聊天界面能体现对话双方的用户名、时间和聊天内容。
-
功能分析
-
注册与登录功能
-
用户首次使用该通信系统,需先进行注册,依次输入账号、密码、昵称(不得为空),若账号不存在,则显示注册成功;若账号已存在,则直接进行登录验证。登录时进行账号查询,若未查询到数据,则显示账号未注册;若密码错误,则重新登录;若密码正确,则登录成功进入聊天界面。其注册登录逻辑图如图1所示:
-
-
即时通信功能
-
登录成功后进入聊天,聊天窗口提供与用户进行即时通信的显示视图。通过双向Socket通信方式,允许客户端之间通过服务器进行实时通信。在客户端可以通过聊天窗口接收消息并输入文字信息,将其发送给其他在线用户。
-
-
数据库功能
-
运用MySQL数据库管理系统来存储用户的相关信息,例如用户名、密码等。这些信息用于用户的身份验证、权限控制等。
-
方案设计
-
Socket简介
-
Socket是用于连接通信的双方的开发技术,使其能够构建通信的桥梁,进而进行信息的传递,当一方连接另一方时要确认对方的IP与Port,而服务器端则需要打开相应的端口号等待客户端进行连接。通过Socket这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
具体来说在运行该系统时,需要先启动服务器端,以打开服务器端的连接端口并进行监听,等待其他的用户连接,当其他用户的连接信息传来时,该用户就会被赋予相应的端口号,然后与服务端进行连接,服务器端与客户端之间的通信渠道而后被建立,构建成功的通信渠道,可以通过编写Socket来相互通信,若有新的客户端请求来连接,则服务器端自身则需要获得一个新的端口号来执行被占用的旧的端口号的监听工作,以便于应答新的客户端请求,真正实现多客户的服务功能,使多个客户可以通过服务器作为媒介从而进行轻松且简便的聊天与各种功能。Socket信息交互原理图如图2所示:
-
-
服务器与客户端模块设计
-
服务器
- 服务器首先创建一个Socket,并指定一个端口号来监听客户端的连接请求。当客户端尝试连接到服务器时,服务器端的Socket会捕获到这个连接请求然后建立一个连接,之后客户端和服务器开始交换数据。服务器接受了第一个客户端的连接后还可以继续接受其他客户端的连接。服务器需要管理所有已建立的连接,并确保它可以同时处理多个客户端。服务器通过已建立的Socket连接从客户端接收数据,并可以向客户端发送数据。这种通信可以是双向的,允许服务器和客户端之间进行实时通信。服务器接收到数据后解析这些数据并根据应用逻辑执行相应的操作。客户端关闭Socket连接后,服务器也会关闭连接从而结束通信。
-
-
-
客户端
- 客户端在开始时创建一个Socket,连接到服务器,并与服务器交换数据。客户端使用创建的Socket连接到服务器。服务器的IP地址和端口号一旦连接成功,客户端和服务器就可以开始通信。客户端和服务器可以使用它们已建立的Socket连接来发送和接收数据。当通信完成后,客户端可以关闭其Socket连接。这将通知服务器关闭连接从而结束通信。
-
-
-
服务器与客户端的交互
- 服务器与客户端的基本交互流程为:服务器首先设置一个Socket,指定要监听的端口并开始监听连接请求;客户端尝试连接到服务器,向指定的端口发送连接请求;服务器接收到客户端的连接请求后创建一个新的Socket来与客户端进行通信;一旦连接建立,服务器和客户端就可以通过Socket进行双向的数据交换。服务器可以发数据交换:送数据给客户端,客户端也可以发送数据给服务器。当通信完成后,服务器和客户端可以关闭各自的Socket连接。在交互过程中,服务器主要负责监听和响应客户端的请求,而客户端则负责发送请求和接收服务器的响应。这种基于Socket的通信方式允许服务器和客户端之间进行实时、双向的数据交换。其整体流程图如图5所示:
-
-
-
-
数据库
数据库是按照数据结构来组织、存储和管理数据的仓库。每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据。我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢。
在本次课程设计中我们运用MySQL创建数据库用来存储用户注册后的信息,MySQL是一个关系型数据库管理系统,是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。相对于其他数据库,比如Oracle之类来说,MySQL的规模是偏小的,但同时它也是较轻便的,它对计算机资源占用较少,确能提供用户和一些规模不大的企业足够的功能,所以MySQL是足够我们使用的同时减轻了我们的成本。
数据库主要用于存储用户的相关信息,例如用户名、密码等。这些信息用于用户的身份验证、权限控制等。当用户发送或接收消息时,这些消息会被存储在数据库中。为了实现实时通信,数据库需要存储和管理用户的在线状态。包括跟踪哪些用户当前在线、他们所处的聊天室等。通过定期更新数据库中的状态信息,服务器可以确保将消息发送给正确的接收者。在即时通信软件中,数据库需要支持事务处理,以确保数据的完整性和一致性。例如在处理用户消息发送时,数据库需要确保这些操作成功。为了防止数据丢失,数据库需要定期备份。此外,当数据出现问题时,需要能够快速恢复到正常状态。
-
系统总体结构描述
本系统是即时通信系统,采用的是三层式C/S构架来保证网络通信的安全机制:客户端、服务器端、数据服务器。整个系统的通信模型采用P2P与C/S混合的通信结构。客户端利用TCP通信协议,并通过基于UDP通信协议的P2P技术与其他客户端进行交流;服务器处理客户端发送来的信息,并利用TCP通信协议将信息反馈回客户端以及保存用户的在线与离线状态。服务数据库主要负责用户注册信息保存,用户聊天记录的保存。
-
系统测试
-
附录:代码块
- 服务器
-
#import ... from twisted.internet.protocol import Factory #导入twisted.internet.protocol中的factory模块 from twisted.protocols.basic import LineReceiver # 事件处理器 from twisted.internet import reactor#twisted 是用Python实现的基于事件驱动的网络引擎框架, # 支持常见的传输及应用层协议,包括TCP\UDP。对于所有的协议都带有客户端和服务器实现。 #from model import login_model,register_model,chat_model import json class Chat(LineReceiver): def __init__(self, users):#self可以理解为一个字典变量,内部存的是对象的数据属性{‘name’:'zhang','age':'18'} self.users = users# 实现对象属性解释器和对象属性查询器,可以查询使用。 def connectionLost(self, reason):#断开连接时候自动触发,从users字典去掉连接对象 if self in self.users.keys():#连接断开情况 #print("%s断开连接" %self.users[self]) del self.users[self] def dataReceived(self, data): # 对返回内容开始做处理,只要收到客户端消息,自动触发此方法 data = data.decode('utf-8')#数据接收 data_dict = json.loads(data) #根据type字段的值,进入对应的逻辑 if data_dict["type"] == "login": login_model.login(self ,data_dict) elif data_dict["type"] == "register": register_model.register(self ,data_dict) elif data_dict["type"] == "chat": chat_model.chat(self ,data_dict) class ChatFactory(Factory):#实现了一个工厂类,只会实例化一次 def __init__(self): self.users = {} # 昵称映射到聊天实例,key-->登录成功的连接对象 value:昵称 def buildProtocol(self, addr):#此方法必须要实现 print(addr) return Chat(self.users)#返回一个处理具体业务请求的对象,参数传递了字典:存所有登录成功的连接对象 if __name__ == '__main__':#设定监听端口和对象 reactor.listenTCP(1200, ChatFactory())#使用Tcp协议,实例化ChatFactory print ("开始进入监听状态...") print("等待客户端发起连接") reactor.run()#开始监听
客户端
-
import tkinter from tkinter import messagebox import json,time import threading import select from socket import * #from client import client_draw class ChatRoom(object): def connect(self): #配置连接 self.s = socket(AF_INET, SOCK_STREAM) remote_host = gethostname() #获取计算机名称 port = 1200 #设置端口号 self.s.connect((remote_host, port)) # 发起连接 print("从%s成功连接到%s" %(self.s.getsockname(),self.s.getpeername())) return self.s def recive(self,s): # 监听消息 self.my = [s] while True: rs, ws, es = select.select(self.my, [], []) if s in rs: try: data = s.recv(1024).decode('utf-8') data_dict = json.loads(data) type = data_dict["type"] # 根据服务端返回的type值,进入不同逻辑 if type == "login": # 登录逻辑 if "000000" == data_dict["code"]: #code返回000000,跳转聊天页面 nickname = data_dict["nickname"] self.chat_interface(nickname) else: tkinter.messagebox.showinfo(title='登录提示', message=data_dict["msg"]) elif type == "register": # 注册逻辑 if "000000" == data_dict["code"]: #code返回000000,跳转聊天页面 nickname = data_dict["nickname"] tkinter.messagebox.showinfo(title='进入聊天室', message=data_dict["msg"]) self.chat_interface(nickname) else: tkinter.messagebox.showinfo(title='注册提示', message=data_dict["msg"]) elif type == "chat": # 聊天逻辑 message = data_dict["message"] nickname = data_dict["nickname"] isMy = data_dict["isMy"] times = " "+ nickname + "\t" + time.strftime("%H:%M:%S",time.localtime())+ '\n' self.txtMsgList.insert(tkinter.END, times,"DimGray") # 聊天页面,发送人以及发送时间展示 if "yes" == isMy: # 如果是自己发的消息,字体使用'DarkTurquoise',如果是别人发的消息,字体使用'Black' self.txtMsgList.insert(tkinter.END, " "+ message + "\n\n",'DarkTurquoise') else: self.txtMsgList.insert(tkinter.END, " " + message + "\n\n", 'Black') self.txtMsgList.see(tkinter.END) # 插入消息时,自动滚动到底部 except Exception as e: print(e) exit() def register_interface(self): # 进入注册界面 client_draw.draw_register(self) def chat_interface(self,nickname): #进入聊天页面 client_draw.draw_chat(self,nickname) def return_login_interface(self): #返回登录页面 self.label_nickname.destroy() #将不需要的label_nickname控件先销毁 self.input_nickname.destroy() #将不需要的input_nickname控件先销毁 self.label_password.destroy() #将不需要的label_password控件先销毁 self.input_password.destroy() #将不需要的input_password控件先销毁 client_draw.draw_login(self) def verify_register(self): # 获取输入框内容,进行注册验证 account = self.input_account.get() password = self.input_password.get() nickname = self.input_nickname.get() try: register_data = {} register_data["type"] = "register" register_data["account"] = account register_data["password"] = password register_data["nickname"] = nickname data = json.dumps(register_data) #将register_data由dict格式转为json字符串,便于网络传输 self.s.send(data.encode('utf-8')) except Exception as e: print(e) def verify_login(self): # 获取输入框内容,进行登录信息验证 account = self.input_account.get() password = self.input_password.get() try: login_data = {} login_data["type"] = "login" login_data["account"] = account login_data["password"] = password data = json.dumps(login_data) #将login_data由dict格式转为json字符串,便于网络传输 self.s.send(data.encode('utf-8')) except Exception as e: print(e) def sendMsg(self):#获取输入框内容,发送消息 message = self.txtMsg.get('0.0', tkinter.END).strip() if not message: tkinter.messagebox.showinfo(title='发送提示', message="发送内容不能为空,请重新输入") return self.txtMsg.delete('0.0', tkinter.END) try: chat_data = {} chat_data["type"] = "chat" chat_data["message"] = message data = json.dumps(chat_data) #将chat_data由dict格式转为json字符串,便于网络传输 self.s.send(data.encode('utf-8')) except Exception as e: print(e) def sendMsgEvent(self,event):#发送消息事件 if event.keysym =='Return': #如果捕捉到键盘的回车按键,触发消息发送 self.sendMsg() def on_closing(self): # 聊天页面,点击右上角退出时执行 if messagebox.askokcancel("退出提示", "是否离开聊天室?"): self.root.destroy() def main(): chatRoom = ChatRoom() client = chatRoom.connect() t = threading.Thread(target=chatRoom.recive, args=(client,)) # 创建一个线程,监听消息 t.start() chatRoom.root = tkinter.Tk() # 创建主窗口,用于容纳其它组件 client_draw.draw_login(chatRoom) # 登录界面控件创建、布局 tkinter.mainloop() # 进入事件(消息)循环 if __name__ == '__main__': main()
-
客户端2(局域网内两个主机通信)
-
import tkinter from tkinter import messagebox import json,time import threading import select from socket import * from client import client_draw class ChatRoom(object): def connect(self): #配置连接 self.s = socket(AF_INET, SOCK_STREAM) remote_host = gethostname() #获取计算机名称 port = 1200 #设置端口号 self.s.connect((remote_host, port)) # 发起连接 print("从%s成功连接到%s" %(self.s.getsockname(),self.s.getpeername())) return self.s def recive(self,s): # 监听消息 self.my = [s] while True: rs, ws, es = select.select(self.my, [], []) if s in rs: try: data = s.recv(1024).decode('utf-8') data_dict = json.loads(data) type = data_dict["type"] # 根据服务端返回的type值,进入不同逻辑 if type == "login": # 登录逻辑 if "000000" == data_dict["code"]: #code返回000000,跳转聊天页面 nickname = data_dict["nickname"] self.chat_interface(nickname) else: tkinter.messagebox.showinfo(title='登录提示', message=data_dict["msg"]) elif type == "register": # 注册逻辑 if "000000" == data_dict["code"]: #code返回000000,跳转聊天页面 nickname = data_dict["nickname"] tkinter.messagebox.showinfo(title='进入聊天室', message=data_dict["msg"]) self.chat_interface(nickname) else: tkinter.messagebox.showinfo(title='注册提示', message=data_dict["msg"]) elif type == "chat": # 聊天逻辑 message = data_dict["message"] nickname = data_dict["nickname"] isMy = data_dict["isMy"] times = " "+ nickname + "\t" + time.strftime("%H:%M:%S",time.localtime())+ '\n' self.txtMsgList.insert(tkinter.END, times,"DimGray") # 聊天页面,发送人以及发送时间展示 if "yes" == isMy: # 如果是自己发的消息,字体使用'DarkTurquoise',如果是别人发的消息,字体使用'Black' self.txtMsgList.insert(tkinter.END, " "+ message + "\n\n",'DarkTurquoise') else: self.txtMsgList.insert(tkinter.END, " " + message + "\n\n", 'Black') self.txtMsgList.see(tkinter.END) # 插入消息时,自动滚动到底部 except Exception as e: print(e) exit() def register_interface(self): # 进入注册界面 client_draw.draw_register(self) def chat_interface(self,nickname): #进入聊天页面 client_draw.draw_chat(self,nickname) def return_login_interface(self): #返回登录页面 self.label_nickname.destroy() #将不需要的label_nickname控件先销毁 self.input_nickname.destroy() #将不需要的input_nickname控件先销毁 self.label_password.destroy() #将不需要的label_password控件先销毁 self.input_password.destroy() #将不需要的input_password控件先销毁 client_draw.draw_login(self) def verify_register(self): # 获取输入框内容,进行注册验证 account = self.input_account.get() password = self.input_password.get() nickname = self.input_nickname.get() try: register_data = {} register_data["type"] = "register" register_data["account"] = account register_data["password"] = password register_data["nickname"] = nickname data = json.dumps(register_data) #将register_data由dict格式转为json字符串,便于网络传输 self.s.send(data.encode('utf-8')) except Exception as e: print(e) def verify_login(self): # 获取输入框内容,进行登录信息验证 account = self.input_account.get() password = self.input_password.get() try: login_data = {} login_data["type"] = "login" login_data["account"] = account login_data["password"] = password data = json.dumps(login_data) #将login_data由dict格式转为json字符串,便于网络传输 self.s.send(data.encode('utf-8')) except Exception as e: print(e) def sendMsg(self):#获取输入框内容,发送消息 message = self.txtMsg.get('0.0', tkinter.END).strip() if not message: tkinter.messagebox.showinfo(title='发送提示', message="发送内容不能为空,请重新输入") return self.txtMsg.delete('0.0', tkinter.END) try: chat_data = {} chat_data["type"] = "chat" chat_data["message"] = message data = json.dumps(chat_data) #将chat_data由dict格式转为json字符串,便于网络传输 self.s.send(data.encode('utf-8')) except Exception as e: print(e) def sendMsgEvent(self,event):#发送消息事件 if event.keysym =='Return': #如果捕捉到键盘的回车按键,触发消息发送 self.sendMsg() def on_closing(self): # 聊天页面,点击右上角退出时执行 if messagebox.askokcancel("退出提示", "是否离开聊天室?"): self.root.destroy() def main(): chatRoom = ChatRoom() client = chatRoom.connect() t = threading.Thread(target=chatRoom.recive, args=(client,)) # 创建一个线程,监听消息 t.start() chatRoom.root = tkinter.Tk() # 创建主窗口,用于容纳其它组件 client_draw.draw_login(chatRoom) # 登录界面控件创建、布局 tkinter.mainloop() # 进入事件(消息)循环 if __name__ == '__main__': main()
聊天室界面设计:
-
import tkinter def draw_login(self): #登录页面 self.root.title("聊天室登录页面") # 给主窗口设置标题内容 self.root.geometry('450x300') # 设置主窗口大小 self.canvas = tkinter.Canvas(self.root, height=200, width=500) # 创建画布 self.label_account = tkinter.Label(self.root, text='账 号') # 创建一个`Label`名为`账 号: ` self.label_password = tkinter.Label(self.root, text='密 码') # 创建一个`Label`名为`密 码: ` self.input_account = tkinter.Entry(self.root, width=30) # 创建一个账号输入框,并设置尺寸 self.input_password = tkinter.Entry(self.root, show='*', width=30) # 创建一个密码输入框,并设置尺寸 self.login_button = tkinter.Button(self.root, command=self.verify_login, text="登 录", width=10) #登录按钮 self.register_button = tkinter.Button(self.root, command=self.register_interface, text="注 册", width=10) #注册按钮 # 登录页面各个控件进行布局 self.label_account.place(x=90, y=70) self.label_password.place(x=90, y=150) self.input_account.place(x=135, y=70) self.input_password.place(x=135, y=150) self.login_button.place(x=120, y=235) self.register_button.place(x=250, y=235) def draw_register(self): #注册页面控件创 self.login_button.destroy() self.register_button.destroy() self.root.title("聊天室注册页面") self.root.geometry('450x300') # 设置主窗口大小 self.canvas = tkinter.Canvas(self.root, height=200, width=500) # 创建画布 self.label_nickname = tkinter.Label(self.root, text='昵 称') # 创建一个"Label",名为:"昵 称" self.input_nickname = tkinter.Entry(self.root, width=30) # 创建一个昵称输入框,并设置尺寸 self.register_submit_button = tkinter.Button(self.root, command=self.verify_register, text="提交注册", width=10) #创建注册按钮 self.return_login_button = tkinter.Button(self.root, command=self.return_login_interface, text="返回登录",width=10) # 创建注册按钮 # 注册页面各个控件进行布局 self.label_account.place(x=90, y=70) self.label_password.place(x=90, y=130) self.input_account.place(x=135, y=70) self.input_password.place(x=135, y=130) self.label_nickname.place(x=90, y=190) self.input_nickname.place(x=135, y=190) self.register_submit_button.place(x=120, y=235) self.return_login_button.place(x=250, y=235) def draw_chat(self,nickname): self.root.title("【%s】的聊天室页面" %nickname) # 给主窗口设置标题内容 self.root.geometry('520x560') # 创建frame容器 self.frmLT = tkinter.Frame(width=500, height=320) self.frmLC = tkinter.Frame(width=500, height=150) self.frmLB = tkinter.Frame(width=500, height=30) self.txtMsgList = tkinter.Text(self.frmLT) self.txtMsgList.tag_config('DimGray', foreground='#696969',font=("current_datetime", "11")) #设置消息时间字体样式 self.txtMsgList.tag_config('DarkTurquoise', foreground='#00dd2f', font=("Message", "13"),spacing2=5) #设置自己的消息字体样式 self.txtMsgList.tag_config('Black', foreground='#000000', font=("Message", "13"), spacing2=5) # 设置其它人的消息字体样式 self.txtMsg = tkinter.Text(self.frmLC) self.txtMsg.bind("<KeyPress-Return>", self.sendMsgEvent) # 触发键盘的回车按键事件,发送消息 self.btnSend = tkinter.Button(self.frmLB, text='发送', width=12, command=self.sendMsg) self.labSend = tkinter.Label(self.frmLB, width=55) #创建空的Label在左边占个位置,便于发送按钮靠右 # 窗口布局 self.frmLT.grid(row=0, column=0, columnspan=2, padx=10, pady=10) self.frmLC.grid(row=1, column=0, columnspan=2, padx=10, pady=10) self.frmLB.grid(row=2, column=0, columnspan=2, padx=10, pady=10) # 固定大小 self.frmLT.grid_propagate(0) self.frmLC.grid_propagate(0) self.frmLB.grid_propagate(0) self.labSend.grid(row=0, column=0) self.btnSend.grid(row=0, column=1) #发送按钮布局 self.txtMsgList.grid() self.txtMsg.grid() # WM_DELETE_WINDOW 不能改变,这是捕获命令 self.root.protocol('WM_DELETE_WINDOW', self.on_closing)
数据库配置
-
import pymysql class MySQL(): def __init__(self): # 第一步:打开数据库连接 self.conn = pymysql.connect( host="192.168.204.72", port=3306, user="root", passwd="123456", db="aa", charset="utf8") self.conn.select_db("chat")#建立连接 def insert(self ,SQL):#声明插入游标|||||#声明游标→打开游标→读取数据→关闭游标→删除游标 cursor = self.conn.cursor() #第二步,获取游标 try:#第三步:执行SQL cursor.execute(SQL) cursor.close() #第四步:关闭游标 self.conn.commit() #第五步:提交事务 # print("数据入库成功") except pymysql.Error as e: print("Mysql Error %d: %s" % (e.args[0], e.args[1])) def select(self ,SQL):#声明筛选游标 cursor = self.conn.cursor() #第二步,获取游标 try:#第三步:执行SQL cursor.execute(SQL) # 读取所有user表中的数据,默认存cursor中 resSet = cursor.fetchall() # 获取表中的全部数据 cursor.close() #第四步:关闭游标 self.conn.commit() #第五步:提交事务 return resSet except pymysql.Error as e: print("Mysql Error %d: %s" % (e.args[0], e.args[1])) def conn_close(self): self.conn.close() # 第六步:关闭连接 if __name__ == '__main__': mysql = MySQL() sql = "SELECT nickname FROM user " resSet = mysql.select(sql) #print(resSet[0][0]) print("成功连接到数据库")#主函数,如果筛选到昵称,打印数据库第一个数据,并提示成功连接到数据库。
数据库用户配置
-
from db import handle_mysql def checkUser(account): # 通过account查询用户信息 mysql = handle_mysql.MySQL() sql_select_account = "SELECT account,password,nickname FROM user WHERE account = '%s'" %account userInfo = mysql.select(sql_select_account) mysql.conn_close() return userInfo def insertUser(account, password, nickname): # 插入用户信息 mysql = handle_mysql.MySQL() sql_insert = "INSERT INTO user(account,password,nickname) VALUES('%s','%s','%s')" % (account, password, nickname) mysql.insert(sql_insert) mysql.conn_close()
聊天逻辑
-
import json def chat(self, data_dict): # 聊天逻辑 message = data_dict["message"].strip() for user in self.users.keys(): #遍历所有的连接对象 data = {} data["type"] = "chat" current_nickname = self.users[self] data["nickname"] = "%s" % current_nickname #获取当前发送消息客户端的昵称 data["isMy"] = "no" #isMy字段默认为no if user == self: #如果遍历的对象与发消息客户端是同一个,则将isMy字段设为yes,便于前端用来判断展示不同的字体样式 data["isMy"] = "yes" data["message"] = message data = json.dumps(data) user.sendLine(data.encode("utf-8")) #遍历将消息发送给每一个连接对象
登录逻辑
-
from db import user_db import json def login(self,data_dict):#登录逻辑 account = data_dict["account"].strip() password = data_dict["password"].strip() data = {} if account and password: #账号密码均不为空,才进入登录验证逻辑 code,msg,nickname = login_check(account, password) elif not account: code = "700001" msg = "登录账号不能为空" elif not password: code = "700002" msg = "登录密码不能为空" if code == "000000": # code为000000时,表示登录成功,将连接对象以及昵称,加到users里,便于后续遍历发送消息 self.users[self] = nickname data["nickname"] = nickname data["type"] = "login" data["code"] = code data["msg"] = msg data = json.dumps(data) self.sendLine(data.encode("utf-8")) def login_check(account, password): #登录校验 userInfo = user_db.checkUser(account) #通过账号查询数据库,获取账号、密码、昵称 if len(userInfo) == 0: #未查询出数据,表示该账号未注册 data = ("700003", "账号【%s】未注册,请注册后再登录!" % account, None) elif password != userInfo[0][1]: #查询出的密码与接收到的不致,表示密码不正确 data = ("700004", "密码错误,请重新输入", None) else: nickname = userInfo[0][2] #登录成功,获取昵称 data = ("000000", "账号【%s】登录成功" % account, nickname) return data
-
注册逻辑
-
from db import user_db import json def register(self, data_dict): # 注册逻辑 account = data_dict["account"].strip() password = data_dict["password"].strip() nickname = data_dict["nickname"].strip() data = {} if account and password and nickname: #account、password、nickname均不为空,才走注册验证逻辑 code, msg = register_check(account, password, nickname) elif not account: code = "600002" msg = "注册账号不能为空" elif not password: code = "600003" msg = "注册密码不能为空" elif not nickname: code = "600004" msg = "注册昵称不能为空" if code == "000000": self.users[self] = nickname data["nickname"] = nickname data["type"] = "register" data["code"] = code data["msg"] = msg data = json.dumps(data) self.sendLine(data.encode("utf-8")) def register_check(account,password,nickname): #注册验证逻辑 checkUser = user_db.checkUser(account) #通过账号查询用户信息 if len(checkUser) > 0: #如果查询到用户信息,表示该账号已经注册 data = ("600005","账号【%s】已注册" %account) else: user_db.insertUser(account,password,nickname) #将用户信息插入数据库 data = ("000000","账号【%s】注册成功,点击“确定”进入聊天页面" %account) return data
以上代码可以在局域网内,两台主机上运行,需要配置IP地址和数据库,具体信息在代码中已经给出。
-
参考文献
-
[1] 佘其炯.数字通信世界.即时通信系统的现状与发展趋势[J].2007,06:41-43
[2] 李远杰等.主流即时通软件通信协议分析术[J].计算机应用研究,2005,07(15):4-6
[3] 王宇,卢昱.计算机工程.信息网络的通信安全控制[J],2006(32卷、12期):173-175
[4] 谢希仁.计算机网络(第五版)[M].北京电子工业出版社,2010.184-188
[5] 何进,谢松巍.基于Socket的TCP/IP网络通讯模式研究[J].计算机应用研究;2001年08期
[6] 周坤,傅德胜.计算机工程与设计.基于Windows Socket 的网络数据传输及其安全[J],2007(28):5381-5388
[7] 戴大蒙.基于非阻塞式Winsock的多线程网络通信机制[J].计算机工程,2006,6:25-27
[8] 宋国龙,浅谈即时通信软件的开发技术.山西电子技术,2003,(2):13-15
[9] 才科扎西.C/S体系结构的探讨.西北民族大学学报,2008,29(71):6-7
[10] 陈妹,方滨兴,周勇林.P2P技术的研究与应用.计算机工程与应用,2002,13(10):34-36
[11] Fulvio Risso, Loris Degioanni. An Architecture for High Performance Network Analysis. Proceedings of the 6th IEEE Symposium on Computers and Communications (ISCC 2001), Hammamet, Tunisia, 2008
[12] Raymond B. Jennings III, Erich M. Nahum, David P. Olshefski. A Study of Internet Instant Messaging and Chat Protocols[R]. Texas: IBM T.J. Watson Research Center, 2007,90-98
[13] Gerhard Fasol.imode: The Benchmark for Wireless Internet.Eurotechnoloy Report,2006
[14] Li Yuanjie, Liu Weifeng, Analysis of Mainstream Instant Messaging Software Communication Protocol J. Computer Application Research, 2005 (7).