GUI 应用:socket 网络聊天室

打印 上一主题 下一主题

主题 844|帖子 844|积分 2532

在这个周末刚刚写出来的python桌面应用--网络聊天室,主要通过pyqt5作为桌面应用框架,socket作为网络编程的框架,从而实现包括客户端和服务端的网络聊天室的GUI应用,希望可以一起学习、一起进步!
应用包括服务端server_ui.py、客户端client_ui.py两个python模块实现,并且在pyqt5的使用过程中都使用QThread多线程应用以及基本的UI页面布局。开始之前通过一个动态图来观察一下socket服务端、socket客户端通信的实现效果。
【阅读全文】


  • socket_ui.py 服务端
1-1. 依赖引用
在socket服务端的实现过程中,除了pyqt5相关的UI界面的引用外,还包括time、threading、sys、socket等辅助模块来一起实现socket服务端的桌面应用程序。
  1. from PyQt5.QtWidgets import *
  2. from PyQt5.QtCore import *
  3. from PyQt5.QtGui import *
  4. import sys
  5. from QCandyUi import CandyWindow
  6. # 导入 socket 通讯模块
  7. import socket
  8. # 导入时间管理模块
  9. import time
  10. # 导入多线程模块
  11. import threading
复制代码
1-2. 实现过程
在服务端的业务实现上面,我们依然是按照之前的GUI实现方式,采用主线程用来实现页面布局,子线程QThread来实现业务逻辑的方式来进行实现的,socket的服务端通信业务都是在子线程ServerThread中编写的。下面是socket服务端桌面应用实现的全部代码块,copy到自己的ide中即可直接启动使用。
  1. class ServerUI(QWidget):
  2.     def __init__(self):
  3.         super(ServerUI, self).__init__()
  4.         self.init_ui()
  5.     def init_ui(self):
  6.         self.setWindowTitle('socket 服务端  公众号:[Python 集中营]')
  7.         self.setWindowIcon(QIcon('hi.ico'))
  8.         self.setFixedSize(500, 300)
  9.         hbox = QHBoxLayout()
  10.         hbox_v1 = QVBoxLayout()
  11.         self.brower = QTextBrowser()
  12.         self.brower.setFont(QFont('宋体', 8))
  13.         self.brower.setReadOnly(True)
  14.         self.brower.setPlaceholderText('消息展示区域...')
  15.         self.brower.ensureCursorVisible()
  16.         hbox_v1.addWidget(self.brower)
  17.         hbox_v2 = QVBoxLayout()
  18.         hbox_v2_f1 = QFormLayout()
  19.         self.ip_label = QLabel()
  20.         self.ip_label.setText('ip地址 ')
  21.         self.ip_txt = QLineEdit()
  22.         self.ip_txt.setPlaceholderText('0.0.0.0')
  23.         self.port_label = QLabel()
  24.         self.port_label.setText('端口 ')
  25.         self.port_txt = QLineEdit()
  26.         self.port_txt.setPlaceholderText('4444')
  27.         self.lis_num_label = QLabel()
  28.         self.lis_num_label.setText('最大监听个数 ')
  29.         self.lis_num_txt = QLineEdit()
  30.         self.lis_num_txt.setPlaceholderText('10')
  31.         self.close_cli_label = QLabel()
  32.         self.close_cli_label.setText('客户端关闭指令 ')
  33.         self.close_cli_txt = QLineEdit()
  34.         self.close_cli_txt.setPlaceholderText('exit,客户端发送相应指令则关闭')
  35.         hbox_v2_f1.addRow(self.ip_label, self.ip_txt)
  36.         hbox_v2_f1.addRow(self.port_label, self.port_txt)
  37.         hbox_v2_f1.addRow(self.lis_num_label, self.lis_num_txt)
  38.         hbox_v2_f1.addRow(self.close_cli_label, self.close_cli_txt)
  39.         self.start_btn = QPushButton()
  40.         self.start_btn.setText('开启服务端')
  41.         self.start_btn.clicked.connect(self.start_btn_clk)
  42.         hbox_v2.addLayout(hbox_v2_f1)
  43.         hbox_v2.addWidget(self.start_btn)
  44.         hbox.addLayout(hbox_v1)
  45.         hbox.addLayout(hbox_v2)
  46.         self.thread_ = ServerThread(self)
  47.         self.thread_.message.connect(self.show_message)
  48.         self.setLayout(hbox)
  49.     def show_message(self, text):
  50.         '''
  51.         槽函数:向文本浏览器中写入内容
  52.         :param text:
  53.         :return:
  54.         '''
  55.         cursor = self.brower.textCursor()
  56.         cursor.movePosition(QTextCursor.End)
  57.         self.brower.append(text)
  58.         self.brower.setTextCursor(cursor)
  59.         self.brower.ensureCursorVisible()
  60.     def start_btn_clk(self):
  61.         self.thread_.start()
  62.         self.start_btn.setEnabled(False)
  63. class ServerThread(QThread):
  64.     message = pyqtSignal(str)
  65.     def __init__(self, parent=None):
  66.         super(ServerThread, self).__init__(parent)
  67.         self.parent = parent
  68.         self.working = True
  69.     def __del__(self):
  70.         self.working = False
  71.         self.wait()
  72.     def run(self):
  73.         self.message.emit('准备启动socket服务端...')
  74.         # 创建服务端 socket
  75.         socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  76.         # 绑定服务地址、端口
  77.         address = (self.parent.ip_txt.text().strip(), int(self.parent.port_txt.text().strip()))
  78.         socket_server.bind(address)
  79.         # 设置监听最大等待数
  80.         socket_server.listen(int(self.parent.lis_num_txt.text().strip()))
  81.         self.message.emit("服务已经启动,正在等待客户端连接...")
  82.         while True:
  83.             # 设置睡眠时间
  84.             time.sleep(0.1)
  85.             # 允许客户端连接
  86.             client, info = socket_server.accept()
  87.             self.client, self.info = client, info
  88.             # 启用新线程调用消息处理
  89.             thread = threading.Thread(target=self.catch_message)
  90.             # 设置为守护线程
  91.             thread.setDaemon(True)
  92.             # 开启线程执行
  93.             thread.start()
  94.     def catch_message(self):
  95.         self.client.send("欢迎来到网络聊天室".encode('utf-8'))
  96.         self.message.emit("客户端信息:" + str(self.info))
  97.         close_cli = self.parent.close_cli_txt.text().strip()
  98.         while True:
  99.             try:
  100.                 #  接收客户端消息、接收最大长度为 1024,并进行 utf-8 解码
  101.                 message = self.client.recv(1024).decode('utf-8')
  102.                 # 校验是否关闭客户端
  103.                 if not message and close_cli == message:
  104.                     self.client.close()
  105.                     self.message.emit("当前客户端已关闭!")
  106.                     break
  107.                 self.message.emit("接收到消息:" + message)
  108.                 # 将消息进行 utf-8 编码后发给客户端
  109.                 rcv = "服务端成功接收消息:" + message
  110.                 self.client.send(rcv.encode('utf-8'))
  111.             except Exception as e:
  112.                 self.client.send("服务端处理消息异常!".encode('utf-8'))
  113.                 break
  114. if __name__ == '__main__':
  115.     app = QApplication(sys.argv)
  116.     w = CandyWindow.createWindow(ServerUI(), theme='blueGreen', title='socket 服务端  公众号:[Python 集中营]',
  117.                                  ico_path='hi.ico')
  118.     w.show()
  119.     sys.exit(app.exec_())
复制代码
1-3. 实现效果


  • client_ui.py 客户端
2-1. 依赖引用
在socket客户端的实现过程中,除了pyqt5相关的UI界面的引用外,还包括sys、socket等辅助模块来一起实现socket服务端的桌面应用程序,相比服务端来说,客户端并没有使用多线程threading模块。
  1. from PyQt5.QtWidgets import *
  2. from PyQt5.QtCore import *
  3. from PyQt5.QtGui import *
  4. import sys
  5. from QCandyUi import CandyWindow
  6. # 导入socket 通信模块
  7. import socket
复制代码
2-2. 实现过程
客户端的实现过程和服务端server_ui.py实现是基本相似的,同样也使用到了pyqt5的QThread的子线程应用,唯一不同的是socket客户端通信方式跟服务端不大相同,同样将下面的代码块copy到自己的ide中直接使用即可。
  1. class ClientUI(QWidget):
  2.     def __init__(self):
  3.         super(ClientUI, self).__init__()
  4.         self.init_ui()
  5.     def init_ui(self):
  6.         self.setWindowTitle('socket 客户端  公众号:[Python 集中营]')
  7.         self.setWindowIcon(QIcon('hi.ico'))
  8.         self.setFixedSize(500, 300)
  9.         hbox = QHBoxLayout()
  10.         hbox_v1 = QVBoxLayout()
  11.         self.brower = QTextBrowser()
  12.         self.brower.setFont(QFont('宋体', 8))
  13.         self.brower.setReadOnly(True)
  14.         self.brower.setPlaceholderText('消息展示区域...')
  15.         self.brower.ensureCursorVisible()
  16.         hbox_v1.addWidget(self.brower)
  17.         hbox_v2 = QVBoxLayout()
  18.         hbox_v2_g1 = QGridLayout()
  19.         self.ip_label = QLabel()
  20.         self.ip_label.setText('ip地址 ')
  21.         self.ip_txt = QLineEdit()
  22.         self.ip_txt.setPlaceholderText('0.0.0.0')
  23.         self.port_label = QLabel()
  24.         self.port_label.setText('端口 ')
  25.         self.port_txt = QLineEdit()
  26.         self.port_txt.setPlaceholderText('4444')
  27.         self.message = QTextEdit()
  28.         self.message.setPlaceholderText('发送消息内容...')
  29.         hbox_v2_g1.addWidget(self.ip_label, 0, 0, 1, 1)
  30.         hbox_v2_g1.addWidget(self.ip_txt, 0, 1, 1, 1)
  31.         hbox_v2_g1.addWidget(self.port_label, 1, 0, 1, 1)
  32.         hbox_v2_g1.addWidget(self.port_txt, 1, 1, 1, 1)
  33.         hbox_v2_g1.addWidget(self.message, 2, 0, 1, 2)
  34.         self.start_btn = QPushButton()
  35.         self.start_btn.setText('发送消息')
  36.         self.start_btn.clicked.connect(self.start_btn_clk)
  37.         hbox_v2.addLayout(hbox_v2_g1)
  38.         hbox_v2.addWidget(self.start_btn)
  39.         hbox.addLayout(hbox_v1)
  40.         hbox.addLayout(hbox_v2)
  41.         self.thread_ = ClientThread(self)
  42.         self.thread_.message.connect(self.show_message)
  43.         self.setLayout(hbox)
  44.     def show_message(self, text):
  45.         '''
  46.         槽函数:向文本浏览器中写入内容
  47.         :param text:
  48.         :return:
  49.         '''
  50.         cursor = self.brower.textCursor()
  51.         cursor.movePosition(QTextCursor.End)
  52.         self.brower.append(text)
  53.         self.brower.setTextCursor(cursor)
  54.         self.brower.ensureCursorVisible()
  55.     def start_btn_clk(self):
  56.         self.thread_.start()
  57. class ClientThread(QThread):
  58.     message = pyqtSignal(str)
  59.     def __init__(self, parent=None):
  60.         super(ClientThread, self).__init__(parent)
  61.         self.parent = parent
  62.         self.working = True
  63.         self.is_connect = False
  64.     def __del__(self):
  65.         self.working = False
  66.         self.wait()
  67.     def run(self):
  68.         try:
  69.             if self.is_connect is False:
  70.                 self.connect_serv()
  71.             # 将控制台输入消息进行 utf-8 编码后发送
  72.             self.socket_client.send(self.parent.message.toPlainText().strip().encode('utf-8'))
  73.             self.message.emit(self.socket_client.recv(1024).decode('utf-8'))
  74.         except Exception as e:
  75.             self.message.emit("发送消息异常:" + str(e))
  76.     def connect_serv(self):
  77.         try:
  78.             self.message.emit("正在创建客户端socket...")
  79.             # 创建客户端 socket
  80.             self.socket_client = socket.socket()
  81.             # 连接服务端
  82.             address = (self.parent.ip_txt.text().strip(), int(self.parent.port_txt.text().strip()))
  83.             self.socket_client.connect(address)
  84.             self.message.emit("服务端连接成功...")
  85.             # 接收服务端消息并进行 utf-8 解码
  86.             self.message.emit(self.socket_client.recv(1024).decode())
  87.             self.is_connect = True
  88.         except:
  89.             self.is_connect = False
  90. if __name__ == '__main__':
  91.     app = QApplication(sys.argv)
  92.     w = CandyWindow.createWindow(ClientUI(), theme='blueGreen', title='socket 客户端  公众号:[Python 集中营]',
  93.                                  ico_path='hi.ico')
  94.     w.show()
  95.     sys.exit(app.exec_())
复制代码
2-3. 实现效果

【往期精彩】

零配置python日志,安装即用!
英语没学好到底能不能做coder,别再纠结了先学起来...
数据清洗工具flashtext,效率直接提升了几十倍数!
一个help函数解决了python的所有文档信息查看...
python 自定义异常/raise关键字抛出异常

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表