经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python3 » 查看文章
python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例
来源:jb51  时间:2019/6/18 8:37:58  对本文有异议

本文在上文的基础上重新实现支持多线程的服务器。

以下为TCP客户端的程序代码:

  1. #!/usr/bin/env python3
  2.  
  3. import sys
  4. from PyQt5.QtCore import (QByteArray, QDataStream, QDate, QIODevice,
  5. QRegExp, Qt)
  6. from PyQt5.QtWidgets import (QApplication, QDateEdit, QFrame, QGridLayout,
  7. QHBoxLayout, QLabel, QLineEdit, QPushButton,
  8. QWidget)
  9. from PyQt5.QtGui import QRegExpValidator
  10. from PyQt5.QtNetwork import (QTcpSocket,)
  11.  
  12. MAC = True
  13. try:
  14. from PyQt5.QtGui import qt_mac_set_native_menubar
  15. except ImportError:
  16. MAC = False
  17.  
  18. PORT = 9407
  19. SIZEOF_UINT16 = 2
  20.  
  21.  
  22. class BuildingServicesClient(QWidget):
  23.  
  24. def __init__(self, parent=None):
  25. super(BuildingServicesClient, self).__init__(parent)
  26.  
  27. self.socket = QTcpSocket()
  28. self.nextBlockSize = 0
  29. self.request = None
  30.  
  31. roomLabel = QLabel("&Room")
  32. self.roomEdit = QLineEdit()
  33. roomLabel.setBuddy(self.roomEdit)
  34. regex = QRegExp(r"[0-9](?:0[1-9]|[12][0-9]|3[0-4])")
  35. self.roomEdit.setValidator(QRegExpValidator(regex, self))
  36. self.roomEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
  37. dateLabel = QLabel("&Date")
  38. self.dateEdit = QDateEdit()
  39. dateLabel.setBuddy(self.dateEdit)
  40. self.dateEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
  41. self.dateEdit.setDate(QDate.currentDate().addDays(1))
  42. self.dateEdit.setDisplayFormat("yyyy-MM-dd")
  43. responseLabel = QLabel("Response")
  44. self.responseLabel = QLabel()
  45. self.responseLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken)
  46.  
  47. self.bookButton = QPushButton("&Book")
  48. self.bookButton.setEnabled(False)
  49. self.unBookButton = QPushButton("&Unbook")
  50. self.unBookButton.setEnabled(False)
  51. quitButton = QPushButton("&Quit")
  52. if not MAC:
  53. self.bookButton.setFocusPolicy(Qt.NoFocus)
  54. self.unBookButton.setFocusPolicy(Qt.NoFocus)
  55.  
  56. buttonLayout = QHBoxLayout()
  57. buttonLayout.addWidget(self.bookButton)
  58. buttonLayout.addWidget(self.unBookButton)
  59. buttonLayout.addStretch()
  60. buttonLayout.addWidget(quitButton)
  61. layout = QGridLayout()
  62. layout.addWidget(roomLabel, 0, 0)
  63. layout.addWidget(self.roomEdit, 0, 1)
  64. layout.addWidget(dateLabel, 0, 2)
  65. layout.addWidget(self.dateEdit, 0, 3)
  66. layout.addWidget(responseLabel, 1, 0)
  67. layout.addWidget(self.responseLabel, 1, 1, 1, 3)
  68. layout.addLayout(buttonLayout, 2, 1, 1, 4)
  69. self.setLayout(layout)
  70.  
  71. self.socket.connected.connect(self.sendRequest)
  72. self.socket.readyRead.connect(self.readResponse)
  73. self.socket.disconnected.connect(self.serverHasStopped)
  74. #self.connect(self.socket,
  75. # SIGNAL("error(QAbstractSocket::SocketError)"),
  76. # self.serverHasError)
  77. self.socket.error.connect(self.serverHasError)
  78. self.roomEdit.textEdited.connect(self.updateUi)
  79. self.dateEdit.dateChanged.connect(self.updateUi)
  80.  
  81. self.bookButton.clicked.connect(self.book)
  82. self.unBookButton.clicked.connect(self.unBook)
  83. quitButton.clicked.connect(self.close)
  84.  
  85. self.setWindowTitle("Building Services")
  86.  
  87.  
  88. def updateUi(self):
  89. enabled = False
  90. if (self.roomEdit.text() and
  91. self.dateEdit.date() > QDate.currentDate()):
  92. enabled = True
  93. if self.request is not None:
  94. enabled = False
  95. self.bookButton.setEnabled(enabled)
  96. self.unBookButton.setEnabled(enabled)
  97.  
  98.  
  99. def closeEvent(self, event):
  100. self.socket.close()
  101. event.accept()
  102.  
  103.  
  104. def book(self):
  105. self.issueRequest("BOOK", self.roomEdit.text(),
  106. self.dateEdit.date())
  107.  
  108.  
  109. def unBook(self):
  110. self.issueRequest("UNBOOK", self.roomEdit.text(),
  111. self.dateEdit.date())
  112.  
  113.  
  114. def issueRequest(self, action, room, date):
  115. self.request = QByteArray()
  116. stream = QDataStream(self.request, QIODevice.WriteOnly)
  117. stream.setVersion(QDataStream.Qt_5_7)
  118. stream.writeUInt16(0)
  119. stream.writeQString(action)
  120. stream.writeQString(room)
  121. stream << date
  122. stream.device().seek(0)
  123. stream.writeUInt16(self.request.size() - SIZEOF_UINT16)#overwrite seek(0)
  124. self.updateUi()
  125. if self.socket.isOpen():
  126. self.socket.close()
  127. self.responseLabel.setText("Connecting to server...")
  128. self.socket.connectToHost("localhost", PORT)
  129.  
  130.  
  131. def sendRequest(self):
  132. self.responseLabel.setText("Sending request...")
  133. self.nextBlockSize = 0
  134. self.socket.write(self.request)
  135. self.request = None
  136.  
  137.  
  138. def readResponse(self):
  139. stream = QDataStream(self.socket)
  140. stream.setVersion(QDataStream.Qt_5_7)
  141.  
  142. while True:
  143. if self.nextBlockSize == 0:
  144. if self.socket.bytesAvailable() < SIZEOF_UINT16:
  145. break
  146. self.nextBlockSize = stream.readUInt16()
  147. if self.socket.bytesAvailable() < self.nextBlockSize:
  148. break
  149. action = ""
  150. room = ""
  151. date = QDate()
  152. #stream >> action >> room
  153. action=stream.readQString()
  154. room=stream.readQString()
  155. if action != "ERROR":
  156. stream >> date
  157. if action == "ERROR":
  158. msg = "Error: {0}".format(room)
  159. elif action == "BOOK":
  160. msg = "Booked room {0} for {1}".format(room,date.toString(Qt.ISODate))
  161. elif action == "UNBOOK":
  162. msg = "Unbooked room {0} for {1}".format(room,date.toString(Qt.ISODate))
  163. self.responseLabel.setText(msg)
  164. self.updateUi()
  165. self.nextBlockSize = 0
  166.  
  167.  
  168. def serverHasStopped(self):
  169. self.responseLabel.setText(
  170. "Error: Connection closed by server")
  171. self.socket.close()
  172.  
  173.  
  174. def serverHasError(self, error):
  175. self.responseLabel.setText("Error: {0}".format(self.socket.errorString()))
  176. self.socket.close()
  177.  
  178.  
  179. app = QApplication(sys.argv)
  180. form = BuildingServicesClient()
  181. form.show()
  182. app.exec_()

以下为TCP服务端的程序代码:

  1. #!/usr/bin/env python3
  2. import bisect
  3. import collections
  4. import sys
  5. from PyQt5.QtCore import (QByteArray, QDataStream, QDate, QReadWriteLock, QThread,QIODevice, Qt)
  6. from PyQt5.QtWidgets import (QApplication, QMessageBox, QPushButton)
  7. from PyQt5.QtNetwork import (QAbstractSocket,QHostAddress, QTcpServer, QTcpSocket)
  8.  
  9. PORT = 9407
  10. SIZEOF_UINT16 = 2
  11. MAX_BOOKINGS_PER_DAY = 5
  12.  
  13. # Key = date, value = list of room IDs
  14. Bookings = collections.defaultdict(list)
  15.  
  16.  
  17. def printBookings():
  18. for key in sorted(Bookings):
  19. print(key, Bookings[key])
  20. print()
  21.  
  22.  
  23. class Thread(QThread):
  24.  
  25. lock = QReadWriteLock()
  26.  
  27. def __init__(self, socketId, parent):
  28. super(Thread, self).__init__(parent)
  29. self.socketId = socketId
  30.  
  31.  
  32. def run(self):
  33. socket = QTcpSocket()
  34. if not socket.setSocketDescriptor(self.socketId):
  35. #self.emit(SIGNAL("error(int)"), socket.error())
  36. self.error.connect(socket.error)
  37. return
  38. while socket.state() == QAbstractSocket.ConnectedState:
  39. nextBlockSize = 0
  40. stream = QDataStream(socket)
  41. stream.setVersion(QDataStream.Qt_5_7)
  42. if (socket.waitForReadyRead() and
  43. socket.bytesAvailable() >= SIZEOF_UINT16):
  44. nextBlockSize = stream.readUInt16()
  45. else:
  46. self.sendError(socket, "Cannot read client request")
  47. return
  48. if socket.bytesAvailable() < nextBlockSize:
  49. if (not socket.waitForReadyRead(60000) or
  50. socket.bytesAvailable() < nextBlockSize):
  51. self.sendError(socket, "Cannot read client data")
  52. return
  53. action = ""
  54. room = ""
  55. date = QDate()
  56. action=stream.readQString()
  57. if action in ("BOOK", "UNBOOK"):
  58. room=stream.readQString()
  59. stream >> date
  60. try:
  61. Thread.lock.lockForRead()
  62. bookings = Bookings.get(date.toPyDate())
  63. finally:
  64. Thread.lock.unlock()
  65. uroom = str(room)
  66. if action == "BOOK":
  67. newlist = False
  68. try:
  69. Thread.lock.lockForRead()
  70. if bookings is None:
  71. newlist = True
  72. finally:
  73. Thread.lock.unlock()
  74. if newlist:
  75. try:
  76. Thread.lock.lockForWrite()
  77. bookings = Bookings[date.toPyDate()]
  78. finally:
  79. Thread.lock.unlock()
  80. error = None
  81. insert = False
  82. try:
  83. Thread.lock.lockForRead()
  84. if len(bookings) < MAX_BOOKINGS_PER_DAY:
  85. if uroom in bookings:
  86. error = "Cannot accept duplicate booking"
  87. else:
  88. insert = True
  89. else:
  90. error = "{0} is fully booked".format(date.toString(Qt.ISODate))
  91. finally:
  92. Thread.lock.unlock()
  93. if insert:
  94. try:
  95. Thread.lock.lockForWrite()
  96. bisect.insort(bookings, uroom)
  97. finally:
  98. Thread.lock.unlock()
  99. self.sendReply(socket, action, room, date)
  100. else:
  101. self.sendError(socket, error)
  102. elif action == "UNBOOK":
  103. error = None
  104. remove = False
  105. try:
  106. Thread.lock.lockForRead()
  107. if bookings is None or uroom not in bookings:
  108. error = "Cannot unbook nonexistent booking"
  109. else:
  110. remove = True
  111. finally:
  112. Thread.lock.unlock()
  113. if remove:
  114. try:
  115. Thread.lock.lockForWrite()
  116. bookings.remove(uroom)
  117. finally:
  118. Thread.lock.unlock()
  119. self.sendReply(socket, action, room, date)
  120. else:
  121. self.sendError(socket, error)
  122. else:
  123. self.sendError(socket, "Unrecognized request")
  124. socket.waitForDisconnected()
  125. try:
  126. Thread.lock.lockForRead()
  127. printBookings()
  128. finally:
  129. Thread.lock.unlock()
  130.  
  131.  
  132. def sendError(self, socket, msg):
  133. reply = QByteArray()
  134. stream = QDataStream(reply, QIODevice.WriteOnly)
  135. stream.setVersion(QDataStream.Qt_5_7)
  136. stream.writeUInt16(0)
  137. stream.writeQString("ERROR")
  138. stream.writeQString(msg)
  139. stream.device().seek(0)
  140. stream.writeUInt16(reply.size() - SIZEOF_UINT16)
  141. socket.write(reply)
  142.  
  143. def sendReply(self, socket, action, room, date):
  144. reply = QByteArray()
  145. stream = QDataStream(reply, QIODevice.WriteOnly)
  146. stream.setVersion(QDataStream.Qt_5_7)
  147. stream.writeUInt16(0)
  148. stream.writeQString(action)
  149. stream.writeQString(room)
  150. stream<<date
  151. stream.device().seek(0)
  152. stream.writeUInt16(reply.size() - SIZEOF_UINT16)
  153. socket.write(reply)
  154.  
  155. class TcpServer(QTcpServer):
  156.  
  157. def __init__(self, parent=None):
  158. super(TcpServer, self).__init__(parent)
  159.  
  160.  
  161. def incomingConnection(self, socketId):
  162. thread = Thread(socketId, self)
  163. #self.connect(thread, SIGNAL("finished()"),
  164. # thread, SLOT("deleteLater()"))
  165. thread.finished.connect(thread.deleteLater)
  166. thread.start()
  167.  
  168.  
  169. class BuildingServicesDlg(QPushButton):
  170.  
  171. def __init__(self, parent=None):
  172. super(BuildingServicesDlg, self).__init__(
  173. "&Close Server", parent)
  174. self.setWindowFlags(Qt.WindowStaysOnTopHint)
  175.  
  176. self.loadBookings()
  177. self.tcpServer = TcpServer(self)
  178. if not self.tcpServer.listen(QHostAddress("0.0.0.0"), PORT):
  179. QMessageBox.critical(self, "Building Services Server","Failed to start server: {0}".format(self.tcpServer.errorString()))
  180. self.close()
  181. return
  182.  
  183. self.clicked.connect(self.close)
  184. font = self.font()
  185. font.setPointSize(24)
  186. self.setFont(font)
  187. self.setWindowTitle("Building Services Server")
  188.  
  189.  
  190. def loadBookings(self):
  191. # Generate fake data
  192. import random
  193.  
  194. today = QDate.currentDate()
  195. for i in range(10):
  196. date = today.addDays(random.randint(7, 60))
  197. for j in range(random.randint(1, MAX_BOOKINGS_PER_DAY)):
  198. # Rooms are 001..534 excl. 100, 200, ..., 500
  199. floor = random.randint(0, 5)
  200. room = random.randint(1, 34)
  201. bookings = Bookings[date.toPyDate()]
  202. if len(bookings) >= MAX_BOOKINGS_PER_DAY:
  203. continue
  204. bisect.insort(bookings, "{0:1d}{1:02d}".format(
  205. floor, room))
  206. printBookings()
  207.  
  208.  
  209. app = QApplication(sys.argv)
  210. form = BuildingServicesDlg()
  211. form.show()
  212. form.move(0, 0)
  213. app.exec_()

以上这篇python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持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号