经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
基于Python socket实现简易网络聊天室
来源:jb51  时间:2022/7/4 10:09:08  对本文有异议

在这个周末刚刚写出来的python桌面应用--网络聊天室,主要通过pyqt5作为桌面应用框架,socket作为网络编程的框架,从而实现包括客户端和服务端的网络聊天室的GUI应用,希望可以一起学习、一起进步!

应用包括服务端server_ui.py、客户端client_ui.py两个python模块实现,并且在pyqt5的使用过程中都使用QThread多线程应用以及基本的UI页面布局。开始之前通过一个动态图来观察一下socket服务端、socket客户端通信的实现效果。

1.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.  
  6. from QCandyUi import CandyWindow
  7.  
  8. # 导入 socket 通讯模块
  9. import socket
  10. # 导入时间管理模块
  11. import time
  12. # 导入多线程模块
  13. 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.  
  6. def init_ui(self):
  7. self.setWindowTitle('socket 服务端 公众号:[Python 集中营]')
  8. self.setWindowIcon(QIcon('hi.ico'))
  9. self.setFixedSize(500, 300)
  10.  
  11. hbox = QHBoxLayout()
  12.  
  13. hbox_v1 = QVBoxLayout()
  14. self.brower = QTextBrowser()
  15. self.brower.setFont(QFont('宋体', 8))
  16. self.brower.setReadOnly(True)
  17. self.brower.setPlaceholderText('消息展示区域...')
  18. self.brower.ensureCursorVisible()
  19. hbox_v1.addWidget(self.brower)
  20.  
  21. hbox_v2 = QVBoxLayout()
  22.  
  23. hbox_v2_f1 = QFormLayout()
  24. self.ip_label = QLabel()
  25. self.ip_label.setText('ip地址 ')
  26. self.ip_txt = QLineEdit()
  27. self.ip_txt.setPlaceholderText('0.0.0.0')
  28.  
  29. self.port_label = QLabel()
  30. self.port_label.setText('端口 ')
  31. self.port_txt = QLineEdit()
  32. self.port_txt.setPlaceholderText('4444')
  33.  
  34. self.lis_num_label = QLabel()
  35. self.lis_num_label.setText('最大监听个数 ')
  36. self.lis_num_txt = QLineEdit()
  37. self.lis_num_txt.setPlaceholderText('10')
  38.  
  39. self.close_cli_label = QLabel()
  40. self.close_cli_label.setText('客户端关闭指令 ')
  41. self.close_cli_txt = QLineEdit()
  42. self.close_cli_txt.setPlaceholderText('exit,客户端发送相应指令则关闭')
  43.  
  44. hbox_v2_f1.addRow(self.ip_label, self.ip_txt)
  45. hbox_v2_f1.addRow(self.port_label, self.port_txt)
  46. hbox_v2_f1.addRow(self.lis_num_label, self.lis_num_txt)
  47. hbox_v2_f1.addRow(self.close_cli_label, self.close_cli_txt)
  48.  
  49. self.start_btn = QPushButton()
  50. self.start_btn.setText('开启服务端')
  51. self.start_btn.clicked.connect(self.start_btn_clk)
  52.  
  53. hbox_v2.addLayout(hbox_v2_f1)
  54. hbox_v2.addWidget(self.start_btn)
  55.  
  56. hbox.addLayout(hbox_v1)
  57. hbox.addLayout(hbox_v2)
  58.  
  59. self.thread_ = ServerThread(self)
  60. self.thread_.message.connect(self.show_message)
  61.  
  62. self.setLayout(hbox)
  63.  
  64. def show_message(self, text):
  65. '''
  66. 槽函数:向文本浏览器中写入内容
  67. :param text:
  68. :return:
  69. '''
  70. cursor = self.brower.textCursor()
  71. cursor.movePosition(QTextCursor.End)
  72. self.brower.append(text)
  73. self.brower.setTextCursor(cursor)
  74. self.brower.ensureCursorVisible()
  75.  
  76. def start_btn_clk(self):
  77. self.thread_.start()
  78. self.start_btn.setEnabled(False)
  79.  
  80.  
  81. class ServerThread(QThread):
  82. message = pyqtSignal(str)
  83.  
  84. def __init__(self, parent=None):
  85. super(ServerThread, self).__init__(parent)
  86. self.parent = parent
  87. self.working = True
  88.  
  89. def __del__(self):
  90. self.working = False
  91. self.wait()
  92.  
  93. def run(self):
  94. self.message.emit('准备启动socket服务端...')
  95. # 创建服务端 socket
  96. socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  97. # 绑定服务地址、端口
  98. address = (self.parent.ip_txt.text().strip(), int(self.parent.port_txt.text().strip()))
  99. socket_server.bind(address)
  100. # 设置监听最大等待数
  101. socket_server.listen(int(self.parent.lis_num_txt.text().strip()))
  102. self.message.emit("服务已经启动,正在等待客户端连接...")
  103. while True:
  104. # 设置睡眠时间
  105. time.sleep(0.1)
  106. # 允许客户端连接
  107. client, info = socket_server.accept()
  108. self.client, self.info = client, info
  109. # 启用新线程调用消息处理
  110. thread = threading.Thread(target=self.catch_message)
  111. # 设置为守护线程
  112. thread.setDaemon(True)
  113. # 开启线程执行
  114. thread.start()
  115.  
  116. def catch_message(self):
  117. self.client.send("欢迎来到网络聊天室".encode('utf-8'))
  118. self.message.emit("客户端信息:" + str(self.info))
  119. close_cli = self.parent.close_cli_txt.text().strip()
  120. while True:
  121. try:
  122. # 接收客户端消息、接收最大长度为 1024,并进行 utf-8 解码
  123. message = self.client.recv(1024).decode('utf-8')
  124. # 校验是否关闭客户端
  125. if not message and close_cli == message:
  126. self.client.close()
  127. self.message.emit("当前客户端已关闭!")
  128. break
  129. self.message.emit("接收到消息:" + message)
  130. # 将消息进行 utf-8 编码后发给客户端
  131. rcv = "服务端成功接收消息:" + message
  132. self.client.send(rcv.encode('utf-8'))
  133. except Exception as e:
  134. self.client.send("服务端处理消息异常!".encode('utf-8'))
  135. break
  136.  
  137.  
  138. if __name__ == '__main__':
  139. app = QApplication(sys.argv)
  140. w = CandyWindow.createWindow(ServerUI(), theme='blueGreen', title='socket 服务端 公众号:[Python 集中营]',
  141. ico_path='hi.ico')
  142. w.show()
  143. sys.exit(app.exec_())

1-3. 实现效果

2.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.  
  6. from QCandyUi import CandyWindow
  7.  
  8. # 导入socket 通信模块
  9. import socket
  10.  

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.  
  6. def init_ui(self):
  7. self.setWindowTitle('socket 客户端 公众号:[Python 集中营]')
  8. self.setWindowIcon(QIcon('hi.ico'))
  9. self.setFixedSize(500, 300)
  10.  
  11. hbox = QHBoxLayout()
  12.  
  13. hbox_v1 = QVBoxLayout()
  14. self.brower = QTextBrowser()
  15. self.brower.setFont(QFont('宋体', 8))
  16. self.brower.setReadOnly(True)
  17. self.brower.setPlaceholderText('消息展示区域...')
  18. self.brower.ensureCursorVisible()
  19. hbox_v1.addWidget(self.brower)
  20.  
  21. hbox_v2 = QVBoxLayout()
  22.  
  23. hbox_v2_g1 = QGridLayout()
  24. self.ip_label = QLabel()
  25. self.ip_label.setText('ip地址 ')
  26. self.ip_txt = QLineEdit()
  27. self.ip_txt.setPlaceholderText('0.0.0.0')
  28.  
  29. self.port_label = QLabel()
  30. self.port_label.setText('端口 ')
  31. self.port_txt = QLineEdit()
  32. self.port_txt.setPlaceholderText('4444')
  33.  
  34. self.message = QTextEdit()
  35. self.message.setPlaceholderText('发送消息内容...')
  36.  
  37. hbox_v2_g1.addWidget(self.ip_label, 0, 0, 1, 1)
  38. hbox_v2_g1.addWidget(self.ip_txt, 0, 1, 1, 1)
  39.  
  40. hbox_v2_g1.addWidget(self.port_label, 1, 0, 1, 1)
  41. hbox_v2_g1.addWidget(self.port_txt, 1, 1, 1, 1)
  42.  
  43. hbox_v2_g1.addWidget(self.message, 2, 0, 1, 2)
  44.  
  45. self.start_btn = QPushButton()
  46. self.start_btn.setText('发送消息')
  47. self.start_btn.clicked.connect(self.start_btn_clk)
  48.  
  49. hbox_v2.addLayout(hbox_v2_g1)
  50. hbox_v2.addWidget(self.start_btn)
  51.  
  52. hbox.addLayout(hbox_v1)
  53. hbox.addLayout(hbox_v2)
  54.  
  55. self.thread_ = ClientThread(self)
  56. self.thread_.message.connect(self.show_message)
  57.  
  58. self.setLayout(hbox)
  59.  
  60. def show_message(self, text):
  61. '''
  62. 槽函数:向文本浏览器中写入内容
  63. :param text:
  64. :return:
  65. '''
  66. cursor = self.brower.textCursor()
  67. cursor.movePosition(QTextCursor.End)
  68. self.brower.append(text)
  69. self.brower.setTextCursor(cursor)
  70. self.brower.ensureCursorVisible()
  71.  
  72. def start_btn_clk(self):
  73. self.thread_.start()
  74.  
  75.  
  76. class ClientThread(QThread):
  77. message = pyqtSignal(str)
  78.  
  79. def __init__(self, parent=None):
  80. super(ClientThread, self).__init__(parent)
  81. self.parent = parent
  82. self.working = True
  83. self.is_connect = False
  84.  
  85. def __del__(self):
  86. self.working = False
  87. self.wait()
  88.  
  89. def run(self):
  90. try:
  91. if self.is_connect is False:
  92. self.connect_serv()
  93. # 将控制台输入消息进行 utf-8 编码后发送
  94. self.socket_client.send(self.parent.message.toPlainText().strip().encode('utf-8'))
  95. self.message.emit(self.socket_client.recv(1024).decode('utf-8'))
  96. except Exception as e:
  97. self.message.emit("发送消息异常:" + str(e))
  98.  
  99. def connect_serv(self):
  100. try:
  101. self.message.emit("正在创建客户端socket...")
  102. # 创建客户端 socket
  103. self.socket_client = socket.socket()
  104. # 连接服务端
  105. address = (self.parent.ip_txt.text().strip(), int(self.parent.port_txt.text().strip()))
  106. self.socket_client.connect(address)
  107. self.message.emit("服务端连接成功...")
  108. # 接收服务端消息并进行 utf-8 解码
  109. self.message.emit(self.socket_client.recv(1024).decode())
  110. self.is_connect = True
  111. except:
  112. self.is_connect = False
  113.  
  114.  
  115. if __name__ == '__main__':
  116. app = QApplication(sys.argv)
  117. w = CandyWindow.createWindow(ClientUI(), theme='blueGreen', title='socket 客户端 公众号:[Python 集中营]',
  118. ico_path='hi.ico')
  119. w.show()
  120. sys.exit(app.exec_())

2-3. 实现效果

以上就是基于Python socket实现简易网络聊天室的详细内容,更多关于Python socket网络聊天室的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号