网络图形化界面多人聊天室 - 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的更多相关文章

  1. Python实现网络图形化界面多人聊天室 - Windows

    Python实现网络图形化界面多人聊天室 - Windows 项目名称:网络多人聊天室图形界面版本 项目思路: server.py 服务端文件,主进程中,创建图形化界面,询问地址(主机名,端口),点击 ...

  2. Python基于Socket实现简易多人聊天室

    前言 套接字(Sockets)是双向通信信道的端点. 套接字可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器. 套接字可以通过多种不同的 ...

  3. Python实现网络多人聊天室

    网络多人聊天室 文件结构: chatroom ├── client.py  # 客户端代码 ├── language.py  # 语言文件 ├── server.py  # 服务端代码 └── set ...

  4. Python实现网络多人聊天室 - Windows

    项目名称:多人聊天室项目结构: client.py server.py settings.py项目思路:服务端接收客户端连接,客户端发送信息给服务端,服务端将信息发送给所有客户端.项目实现:主进程负责 ...

  5. java socket之多人聊天室Demo

    一.功能介绍 该功能实现了一个类似QQ的最简单多人聊天室,如下图所示. 二.目录结构 三.服务端 1)SocketServer类,该类是服务端的主类,主要负责创建聊天窗口,创建监听客户端的线程: pa ...

  6. java小程序---简陋版多人聊天室

    功能需求: 1 每运行一次主函数,创建一个客户端聊天界面; 2 客户端界面分三块,公屏(显示所有客户端发送的信息),私屏(用于输入个人想要发送的信息),发送按钮(点击一次,将客户端信息发送到服务端) ...

  7. Apache MiNa 实现多人聊天室

    Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...

  8. 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室

    原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  9. 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室

    原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

随机推荐

  1. SpringBoot第十篇:thymeleaf详解

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10931435.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言   Sprin ...

  2. 明解C语言 入门篇 第九章答案

    练习9-1 /* 将字符串存储在数组中并显示(其2:初始化) */ #include <stdio.h> int main(void) { char str[] = "ABC\0 ...

  3. c++小学期大作业攻略(一)环境配置

    UPDATE at 2019/07/20 20:21 更新了Qt连接mysql的方法,但是是自己仿照连VS的方法摸索出来的,简单测试了一下能work但是不保证后期不会出问题.如果你在尝试过程中出现了任 ...

  4. Rider 中无法显示DataTable,VS2019的.netCore才有DataTable可视化工具(4)

    如下图在vs2017中是这样的 在2019中是可以直接看的 在Rider中无论什么项目都不支持.

  5. Xamarin vs React Native vs Ionic vs NativeScript: Cross-platform Mobile Frameworks Comparison

    CONTENTS Reading time: 14 minutes Cross-platform mobile development has long been a viable alternati ...

  6. 1小时搞定vuepress快速制作vue文档/博客+免费部署预览

    先来一下演示效果.和vue的官方文档几乎是一致的,页面内容都可自定义. 此教程部署后的效果预览. 在你跟着教程搭建好项目之后,你会收获: 快速搭建一个文档/博客,后期只需要修改markdown内容和导 ...

  7. SpringBoot热部署(实战)详解

    热部署是什么 大家都知道在项目开发过程中,常常会改动页面数据或者修改数据结构,为了显示改动效果,往往需要重启应用查看改变效果,其实就是重新编译生成了新的 Class 文件,这个文件里记录着和代码等对应 ...

  8. 在IIS下发布.Net Core MVC项目

    1. 默认你已经安装了IIS,并且创建了一个.Net Core 项目 2. 发布.NET Core项目 在vs中右键点击MVC项目,点击"发布"按钮,选择"文件系统&qu ...

  9. Sqlserver表值函数来获取逗号分隔的ID

    其功能为: 将字符串如'1,2,3,4,5,6' 拼接成SQL里面的id 1:使用: select * from Student where id IN( SELECT * FROM dbo.F_SP ...

  10. WPF样式与触发器(3)

    WPF中的各类控件元素, 都可以自由的设置其样式. 诸如: 字体(FontFamily) 字体大小(FontSize) 背景颜色(Background) 字体颜色(Foreground) 边距(Mar ...