Python实现网络图形化界面多人聊天室 - Linux
网络图形化界面多人聊天室 - Linux
Windows版本:https://www.cnblogs.com/noonjuan/p/12078524.html
在Python实现网络多人聊天室基础上,添加图形化界面,实现网络图形化界面多人聊天室。
代码结构:
chatroom
├── client.py
├── server.py
└── settings.py
思路:
server.py
首先,在主进程(__main__)中启动两个进程,一个处理与客户端的连接和消息接收以及和图形化界面的信息传输,在终端中打印运行日记;另一个进程处理图形化界面,在这个进程中,新开一个线程,监听Pipe管道,实现与终端进程的信息交流。
client.py
结构与server.py相似,有两个进程——终端进程和图形化界面进程,但是新增了客户登录输入用户名的窗口,从这个窗口中获取用户名,使用管道将用户名传输给终端进程,终端进程再将用户名传给服务器等待登录请求验证,获得服务器发来的登录请求验证成功信息后,通过管道发送给图形化进程中的管道监听线程,使得图形化界面进程获得登录成功信息,进入聊天室。
注意:
本项目运行环境为Ubuntu 16.04,可以运行。我尝试了一下在Windows 10下运行,发现需要将__main__主进程下的全局变量作为参数发给进程,而且在windows下运行会报AttributeError: module 'signal' has no attribute 'SIGKILL'错误,具体原因:https://blog.csdn.net/polyhedronx/article/details/81988335。我将SIGKILL改为了SIGTERM,运行中在关闭窗口时却会报PermissionError: [WinError 5] 拒绝访问错误。除此之外,还有许多的地方会报错,具体原因:https://segmentfault.com/a/1190000013681586。
运行截图:
settings.py:
# settings.py HOST = 'localhost'
PORT = 5555
buffersize = 1024
ADDR = HOST, PORT login_code = ''
for i in [bin(ord(i)) for i in 'login']:
login_code += i logout_code = ''
for i in [bin(ord(i)) for i in 'logout']:
logout_code += i exit_code = ''
for i in [bin(ord(i)) for i in 'exit']:
exit_code += i if __name__ == '__main__':
for v in dir():
if not v.startswith('__'):
print(v, eval(v))
server.py
# server.py import os
import signal
from socket import *
from tkinter import *
from settings import *
from select import select
from threading import Thread
from time import ctime, sleep
from tkinter.scrolledtext import ScrolledText
from multiprocessing import Process, Pipe, Value def terminal():
'实现终端操作'
shm_terminal_pid.value = os.getpid()
# 开启服务器
s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
try:
s.bind(ADDR)
except:
# 如果端口已经被占用
print('Port is already in use now!')
os.kill(shm_gui_pid.value, signal.SIGKILL)
os._exit(0)
s.listen() # IO多路复用,监听客户端连接通信以及保持与gui的通信
rlist = [s, pipe_terminal]
wlist = []
xlist = [] while True:
# 阻塞等待IO事件
print('Waiting for connection...')
try:
rs, ws, xs = select(rlist, wlist, xlist)
except KeyboardInterrupt:
# 如果服务器退出了,通知所有客户端并退出
for c in rlist[2:]:
c.send(exit_code.encode())
for r in rlist:
r.close()
break for r in rs:
if r is s:
# 接收客户端连接
print('IO: server')
conn, addr = s.accept()
print('Connected from', addr)
rlist.append(conn)
elif r is pipe_terminal:
# 接收与pipe_gui的信息
print('IO: pipe_terminal')
data = pipe_terminal.recv()
# 将接收到的信息发送给所有客户端
print(data)
for c in rlist[2:]:
c.send(data.encode())
else:
# 接收客户端信息
print('IO: client')
data = r.recv(buffersize)
if not data:
# 如果客户端退出了,关闭与客户端的连接,并告知其他客户端
print('客户端退出了')
r.close()
rlist.remove(r)
else:
# 如果发来的是登录验证信息
if data.decode().startswith(login_code):
print(data.decode())
username = data.decode().split(' ')[1]
if username not in users:
# 验证成功,将成功信息发送给客户端,并告知其他客户端新用户加入
data = login_code + ' Success'
r.send(data.encode())
users.append(username)
data = ctime() + '\n' + username + ': ' + '加入了聊天室\n'
pipe_terminal.send(data)
for c in rlist[2:]:
if c is not r:
c.send(data.encode())
else:
data = login_code + ' Failure'
r.send(data.encode())
elif data.decode().startswith(logout_code):
print(data.decode())
username = data.decode().split(' ')[1]
users.remove(username)
else:
# 将客户端发送的信息群发给其他客户端
for c in rlist[2:]:
if c is not r:
c.send(data)
print(data.decode())
# 并将信息发送给pipe_gui以显示在gui上
pipe_terminal.send(data.decode()) def gui():
'实现图形化界面操作'
# 设置共享内存
shm_gui_pid.value = os.getpid() main = Tk()
main.title('Chatroom - Administrator') ent = Entry(main, width=100)
cnt = ScrolledText(main) cnt.pack(expand=True, fill=BOTH)
ent.pack(expand=True, fill=BOTH) ent.focus()
main.bind('<Return>', lambda event: write(widgets)) widgets = {}
widgets['ent'] = ent
widgets['cnt'] = cnt thread_bridge = Thread(target=bridge, args=(widgets, ))
thread_bridge.start() main.protocol('WM_DELETE_WINDOW', exit) mainloop()
pipe_gui.close() def exit():
print('Exit!')
pipe_gui.send(exit_code)
sleep(0.1)
os.kill(shm_terminal_pid.value, signal.SIGKILL)
os._exit(0) def bridge(widgets):
# 监听与pipe_terminal的通信,将获得的信息显示在gui上
while True:
print('IO: pipe_gui')
data = pipe_gui.recv()
print(data)
widgets['cnt'].insert(END, data) def write(widgets):
print('Gui <Return> Event')
# 打印ent文本到cnt文本框中去
data = ctime() + '\n' + 'Administrator: ' + widgets['ent'].get() + '\n'
widgets['cnt'].insert(END, data)
widgets['ent'].delete(0, END)
# 将信息发送给pipe_terminal
pipe_gui.send(data) if __name__ == '__main__':
# 创建用户信息
users = [] # 共享内存,保存pid
shm_gui_pid = Value('i', 0)
shm_terminal_pid = Value('i', 0) # 创建管道,实现终端与图形化界面的通信
pipe_terminal, pipe_gui = Pipe() # 创建两个进程,分别实现终端和图形化界面操作
process_terminal = Process(target=terminal)
process_gui = Process(target=gui) # 开始进程
process_terminal.start()
process_gui.start() # 回收进程
process_terminal.join()
process_gui.join()
client.py
# client.py import os
import signal
from socket import *
from tkinter import *
from settings import *
from select import select
from threading import Thread
from time import ctime, sleep
from tkinter.scrolledtext import ScrolledText
from multiprocessing import Process, Pipe, Value
from tkinter.messagebox import showerror, showinfo def terminal():
'实现终端操作'
shm_terminal_pid.value = os.getpid()
# 开启客户端连接
c = socket()
try:
c.connect(ADDR)
except:
# 如果无法连接到客户端
os.kill(shm_gui_pid.value, signal.SIGKILL)
print('Failed to connect to server')
os._exit(0)
print('Connected to', ADDR) # IO多路复用,监听服务端通信以及保持与gui的通信
rlist = [c, pipe_terminal, pipe_validate_terminal]
wlist = []
xlist = [] # 服务器关闭信号
flag = False while True:
if flag:
break # 阻塞等待IO事件
try:
rs, ws, xs = select(rlist, wlist, xlist)
except KeyboardInterrupt:
# 如果客户端ctrl-c退出程序
for r in rlist:
r.close()
break for r in rs:
if r is c:
# 接收服务端的信息
print('IO: client')
data = c.recv(buffersize)
if not data:
print('服务器关闭了')
for r in rlist:
r.close()
flag = True
else:
# 如果发来的是登录验证结果信息
if data.decode().startswith(login_code):
print(data.decode())
status_code = data.decode().split(' ')[1]
if status_code == 'Success':
pipe_validate_terminal.send(login_code)
else:
pipe_validate_terminal.send('bad')
# 如果发来的消息是服务器退出消息
elif data.decode() == exit_code:
pipe_gui.send('管理员关闭了服务器')
os.kill(shm_gui_pid.value, signal.SIGKILL)
os._exit(0)
else:
print(data.decode())
# 将信息发送给pipe_gui
pipe_terminal.send(data.decode())
elif r is pipe_terminal:
# 接收pipe_gui的信息
print('IO: pipe_terminal')
data = pipe_terminal.recv()
# 并把消息发送给服务端
c.send(data.encode())
elif r is pipe_validate_terminal:
# 验证管道
data = pipe_validate_terminal.recv()
c.send(data.encode()) def gui():
'实现图形化界面操作'
shm_gui_pid.value = os.getpid()
# 登录界面
login() main = Tk()
main.title('Chatroom - ' + curuser) ent = Entry(main, width=100)
cnt = ScrolledText(main) cnt.pack(expand=True, fill=BOTH)
ent.pack(expand=True, fill=BOTH) ent.focus()
main.bind('<Return>', lambda event: write(widgets)) widgets = {}
widgets['ent'] = ent
widgets['cnt'] = cnt # 开启一个线程,监听pipe_terminal传过来的信息
thread_bridge = Thread(target=bridge, args=(widgets, ))
thread_bridge.start() main.protocol('WM_DELETE_WINDOW', exit_main)
mainloop()
pipe_gui.close()
thread_bridge.join() def exit_main():
data = ctime() + '\n' + curuser + ': ' + '退出了聊天室\n'
pipe_gui.send(data)
print(data)
data = logout_code + ' ' + curuser
pipe_validate_gui.send(data)
print(data)
sleep(0.1)
os.kill(shm_terminal_pid.value, signal.SIGKILL)
os._exit(0) def bridge(widgets):
# 监听与pipe_terminal的通信,将获得的信息显示在gui上
while True:
print('IO: pipe_gui')
data = pipe_gui.recv()
print(data)
widgets['cnt'].insert(END, data) def write(widgets):
print('Gui <Return> Event')
# 打印ent文本到cnt文本框中去
data = ctime() + '\n' + curuser + ': ' + widgets['ent'].get() + '\n'
widgets['cnt'].insert(END, data)
widgets['ent'].delete(0, END)
# 将信息发送给pipe_terminal
pipe_gui.send(data) def login():
top = Tk()
top.title('chatroom - login') Label(top, text='username:').grid(row=0, column=0)
ent = Entry(top)
ent.grid(row=0, column=1) ent.focus() btn = Button(top, text='confirm', command=lambda: validate(top, ent))
btn.grid(row=1, columnspan=2) top.bind('<Return>', lambda event: validate(top, ent)) top.protocol('WM_DELETE_WINDOW', exit_login) mainloop() def validate(top, ent):
print('validate')
if not ent.get():
showerror('Login Error', 'Empty Username!')
else:
username = ent.get()
# 将用户名发送给terminal,再发送给服务器以验证
pipe_validate_gui.send(login_code + ' ' + username)
data = pipe_validate_gui.recv()
if data == login_code:
global curuser
curuser = username
showinfo('Login Successful', 'Welcome to Internet Chatroom.')
top.destroy()
else:
showerror('Login Failure', 'Username already exists!')
ent.delete(0, END) def exit_login():
os._exit(0) if __name__ == '__main__':
# 当前用户名
curuser = '' # 共享内存
shm_gui_pid = Value('i', 0)
shm_terminal_pid = Value('i', 0) # 创建管道,实现终端与图形化界面的通信
pipe_terminal, pipe_gui = Pipe() # 创建管道,实现login->client->server的登录验证
pipe_validate_gui, pipe_validate_terminal = Pipe() # 创建两个进程,分别实现终端和图形化界面操作
process_terminal = Process(target=terminal)
process_gui = Process(target=gui) # 开始进程
process_terminal.start()
process_gui.start() # 回收进程
process_terminal.join()
process_gui.join()
Python实现网络图形化界面多人聊天室 - Linux的更多相关文章
- Python实现网络图形化界面多人聊天室 - Windows
Python实现网络图形化界面多人聊天室 - Windows 项目名称:网络多人聊天室图形界面版本 项目思路: server.py 服务端文件,主进程中,创建图形化界面,询问地址(主机名,端口),点击 ...
- Python基于Socket实现简易多人聊天室
前言 套接字(Sockets)是双向通信信道的端点. 套接字可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器. 套接字可以通过多种不同的 ...
- Python实现网络多人聊天室
网络多人聊天室 文件结构: chatroom ├── client.py # 客户端代码 ├── language.py # 语言文件 ├── server.py # 服务端代码 └── set ...
- Python实现网络多人聊天室 - Windows
项目名称:多人聊天室项目结构: client.py server.py settings.py项目思路:服务端接收客户端连接,客户端发送信息给服务端,服务端将信息发送给所有客户端.项目实现:主进程负责 ...
- java socket之多人聊天室Demo
一.功能介绍 该功能实现了一个类似QQ的最简单多人聊天室,如下图所示. 二.目录结构 三.服务端 1)SocketServer类,该类是服务端的主类,主要负责创建聊天窗口,创建监听客户端的线程: pa ...
- java小程序---简陋版多人聊天室
功能需求: 1 每运行一次主函数,创建一个客户端聊天界面; 2 客户端界面分三块,公屏(显示所有客户端发送的信息),私屏(用于输入个人想要发送的信息),发送按钮(点击一次,将客户端信息发送到服务端) ...
- Apache MiNa 实现多人聊天室
Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...
- 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室
原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...
- 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室
原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...
随机推荐
- No package python-pip available. 解决方法
问题描述: No package python-pip available. 解决办法: rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/ep ...
- crushmap磁盘智能分组
目录 简介 配置crush class 1. 创建ssd class 2. 创建基于ssd的class rule 3. 创建基于ssd_rule规则的存储池 4. 测试基于ssd的池 简介 ceph从 ...
- Linux 安装Redis4.0.8【yum安装】
.下载yum源 yum install epel-release2.安装redisyum install redis3.启动redis # 启动redis service redis start # ...
- 干货最新版 Spring Boot2.1.5 教程+案例合集
最近发了一系列的 Spring Boot 教程,但是发的时候没有顺序,有小伙伴反映不知道该从哪篇文章开始看起,刚好最近工作告一个小小段落,松哥就把这些资料按照学习顺序重新整理了一遍,给大家做一个索引, ...
- python调用MySQL数据库
在Python中访问mysql数据库中的数据需要三步骤: 1,建立连接 2,操作数据库 3,连接关闭
- redis 缓存问题汇总
前言:在使用redis的时候,特别是大型应用,会碰到不少问题,下面就来总结一下使用redis时的常见问题 一.redis为缓存的问题 1.缓存和数据库双写一致性问题 分析:一致性问题是分布式常见问题, ...
- 使用RunTime.getRunTime().addShutdownHook优雅关闭线程池
有时候我们用到的程序不一定总是在JVM里面驻守,可能调用完就不用了,释放资源. RunTime.getRunTime().addShutdownHook的作用就是在JVM销毁前执行的一个线程.当然这个 ...
- Application类-欢迎页(初始界面)
在程序界面显示前,如果我们处理了很多耗时操作,这个时候给用户提示一个欢迎页便是十分友好的.WPF为我们提供了这个特性: 第一种方法:通过设置图片资源的生成操作 粘贴一个图片到我们的项目中 在解决方案管 ...
- SQL 复制表到另一个表
SqlServer 复制表结构和表数据 复制表数据到已存在的表 INSERT INTO targetTableName SELECT COLUMNS FROM sourceTableName; 复制表 ...
- Go函数篇
1 定义格式 函数构成代码执行的逻辑结构.在Go语言中,函数的基本组成为:关键字func.函数名.参数列表.返回值.函数体和返回语句. Go 语言函数定义格式如下: func FuncName(/*参 ...