基于TCP协议网上聊天程序(python)带界面
1 设计目标
基于TCP协议网上聊天程序
实现一简单的聊天程序实现网上聊天,包括服务器和客户端。
(1)支持多人聊天;
(2)客户端具有图形化用户界面;
(3)客户端之间具有群发和私聊的功能。
2 用python实现的TCP通信模型
TCP客户端:通过connect((ip, port))来请求连接,send()进行发送消息,recv()进行接受消息。
TCP服务器: 通过bind((ip, port))绑定ip和端口,ip不写则代表默认本机所有地址,listen()控制客服端的连接数量,accept()等待阻塞,一直到客户端的到来,recv()接受消息,send()发送消息。
具体过程如下:
3 实现了简单的TCP聊天程序实现网上聊天,通过对服务器和端口的输入,可以在不同的主机上运行并将其服务器与客户端连接,用多线程可以支持多人同时聊天,客户端具有图形化用户界面,客户端之间具有群发和私聊的功能并且能知道实时的用户在线名单,界面显示效果良好,整体完成效果良好。
客户端client:
# -*- coding: utf-8 -*-
"""
Created on Tue May 12 23:49:20 2020
@author: LENOVO
"""
import tkinter
import socket
import threading
import time
win = tkinter.Tk()
win.title("客户端")
win.geometry("400x300+300+200")
ck = None
def getInfo():
while True:
data = ck.recv(1024)#用于接受服务其发送的信息
#接收消息时同步获取系统时间并显示在消息显示框上
text.insert(tkinter.INSERT, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n')
text.insert(tkinter.INSERT, data.decode("utf-8"))
def connectServer():
global ck#全局
ipStr = eip.get()
portStr = eport.get()
userStr = euser.get()
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#socked所准守ipv4相关协议
client.connect((ipStr, int(portStr)))#连接服务器
client.send(userStr.encode("utf-8"))#将自己的登录名发送给服务器,函数会附带自己的IP信息
ck = client
t = threading.Thread(target=getInfo)
t.start()
def sendMail():
friend = None
friend = efriend.get()
sendStr = esend.get()
#自己发出的消息服务器不会重发会给自己,所以在客户端定义界面显示自己发送的消息
if friend !="":
text.insert(tkinter.INSERT, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n'+'我对'+friend+'说:' + sendStr+'\n')
else:
text.insert(tkinter.INSERT, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n'+'我(群发)说:' + sendStr+'\n')
#将消息发给服务器,添加“:”分割是要方便服务器端用正则表达式分出要发送的用户名和要发送的消息
sendStr = friend + ":" + sendStr+"\n"
ck.send(sendStr.encode("utf-8"))
def Exit():
#我在服务器端定义了接收到“exit”就判定该用户下线,并删掉该用户的资料
sendStr = "exit" + ":" + ""
ck.send(sendStr.encode("utf-8"))
text.insert(tkinter.INSERT, "您已下线,如需接收信息请重新登录。\n")
#下面是界面
labelUse = tkinter.Label(win, text="userName").grid(row=0, column=0)
euser = tkinter.Variable()
entryUser = tkinter.Entry(win, textvariable=euser).grid(row=0, column=1)
labelIp = tkinter.Label(win, text="服务器ip").grid(row=1, column=0)
eip = tkinter.Variable()
entryIp = tkinter.Entry(win, textvariable=eip).grid(row=1, column=1)
labelPort = tkinter.Label(win, text="port").grid(row=2, column=0)
eport = tkinter.Variable()
entryPort = tkinter.Entry(win, textvariable=eport).grid(row=2, column=1)
button = tkinter.Button(win, text="登录", command=connectServer).grid(row=0, column=2)
text = tkinter.Text(win, height=10, width=40)
labeltext= tkinter.Label(win, text="显示消息").grid(row=4, column=0)
text.grid(row=4, column=1)
esend = tkinter.Variable()
labelesend = tkinter.Label(win, text="发送的消息").grid(row=5, column=0)
entrySend = tkinter.Entry(win, textvariable=esend).grid(row=5, column=1)
efriend = tkinter.Variable()
labelefriend= tkinter.Label(win, text="发给谁").grid(row=6, column=0)
entryFriend = tkinter.Entry(win, textvariable=efriend).grid(row=6, column=1)
button2 = tkinter.Button(win, text="发送", command=sendMail).grid(row=6, column=2)
button2 = tkinter.Button(win, text="下线", command=Exit).grid(row=2, column=2)
win.mainloop()
服务器server:
# -*- coding: utf-8 -*-
"""
Created on Tue May 12 23:46:03 2020
@author: LENOVO
"""
import tkinter
import socket, threading
win = tkinter.Tk() # 创建主窗口
win.title('服务器')
win.geometry("400x300+200+20")
users = {}#用户字典,也可以连接数据库
def run(connect, addrss):
#接收客户端登陆的信息
userName = connect.recv(1024)
#解码并储存用户的信息
users[userName.decode("utf-8")] = connect
#在连接显示框中显示是否连接成功
printStr = "" + userName.decode("utf-8") + "连接\n"
text.insert(tkinter.INSERT, printStr)
#向当前登录的客户端反馈登录信息并提供在线用户列表
printStr ="登录成功!\n"+"当前在线的好友有:"+str(list(users.keys()))+"\n"
connect.send(printStr.encode())
#向所有在线的客户端反馈新的好友登录信息并提供在线用户列表
printStr = userName.decode("utf-8") + "已上线\n"+"当前在线的好友有:"+str(list(users.keys()))+"\n"
for key in users:
if key !=userName.decode("utf-8"):
users[key].send(printStr.encode())
#接受客户端发送的信息
while True:
rData = connect.recv(1024)
dataStr = rData.decode("utf-8")
#分割字符串得到所要发送的用户名和客户端所发送的信息
infolist = dataStr.split(":")
#通过客户端要发送的信息中是否指定要发送到的用户,如果没有选择要发送的用户,则默认为群发消息
if infolist[0] == "" :
for key in users:
if key !=userName.decode("utf-8"):
users[key].send((userName.decode("utf-8") + "说(群发):" + infolist[1]).encode("utf"))
#如果接收到的消息为客户端退出函数发送的“exit”则删掉该用户在users字典中的信息,并通知其他用户该用户已下线
elif infolist[0] == "exit":
del users[userName.decode("utf-8")]#删掉该用户信息
printStr = "" + userName.decode("utf-8") + "下线\n"
text.insert(tkinter.INSERT, printStr)
for key in users:
printStr = userName.decode("utf-8") + "已下线\n"+"当前在线的好友有:"+str(list(users.keys()))+"\n"
users[key].send(printStr.encode())
#要发送信息的客户端向目标客户端发送信息
else:
if infolist[0] in users:
users[infolist[0]].send((userName.decode("utf-8") + "说(私聊):" + infolist[1]).encode("utf"))
else:
printStr =infolist[0]+"不在线,上条消息未发出"+"\n"
connect.send(printStr.encode())
#界面启动按钮连接的函数
def startSever():
#启用一个线程开启服务器
s = threading.Thread(target=start)
s.start()
#开启线程
def start():
#从输入端中获取ip和端口号
ipStr = eip.get()
portStr = eport.get()
# socket嵌套字TCP的ipv4和相关协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#绑定ip和端口号
server.bind((ipStr, int(portStr)))
#设置监听和连接的最大的数量
server.listen(10)
#服务器启动信息显示在信息窗口中
printStr = "服务器启动成功!\n"
text.insert(tkinter.INSERT, printStr)
#模拟服务器要一直运行所以使用死循环
while True:
#接受所连接的客户端的信息
connect, addrss = server.accept()
#每连接一个客户端就开启一个线程
t = threading.Thread(target=run, args=(connect, addrss))
t.start()
#下面是关于界面的操作
labelIp = tkinter.Label(win, text='ip').grid(row=0, column=0)
eip = tkinter.Variable()
labelPort = tkinter.Label(win, text='port').grid(row=1, column=0)
eport = tkinter.Variable()
entryIp = tkinter.Entry(win, textvariable=eip).grid(row=0, column=1)
entryPort = tkinter.Entry(win, textvariable=eport).grid(row=1, column=1)
button = tkinter.Button(win, text="启动", command=startSever).grid(row=1, column=2)
text = tkinter.Text(win, height=15, width=40)
labeltext = tkinter.Label(win, text='连接消息').grid(row=3, column=0)
text.grid(row=3, column=1)
win.mainloop()
开启后界面效果:(这里开启一个服务器和三个客户端)
登陆
群聊
私聊
用户下线