python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例


Posted in Python onJune 17, 2019

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

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

#!/usr/bin/env python3

import sys
from PyQt5.QtCore import (QByteArray, QDataStream, QDate, QIODevice,
    QRegExp, Qt)
from PyQt5.QtWidgets import (QApplication, QDateEdit, QFrame, QGridLayout,
    QHBoxLayout, QLabel, QLineEdit, QPushButton,
    QWidget)
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtNetwork import (QTcpSocket,)

MAC = True
try:
  from PyQt5.QtGui import qt_mac_set_native_menubar
except ImportError:
  MAC = False

PORT = 9407
SIZEOF_UINT16 = 2


class BuildingServicesClient(QWidget):

  def __init__(self, parent=None):
    super(BuildingServicesClient, self).__init__(parent)

    self.socket = QTcpSocket()
    self.nextBlockSize = 0
    self.request = None

    roomLabel = QLabel("&Room")
    self.roomEdit = QLineEdit()
    roomLabel.setBuddy(self.roomEdit)
    regex = QRegExp(r"[0-9](?:0[1-9]|[12][0-9]|3[0-4])")
    self.roomEdit.setValidator(QRegExpValidator(regex, self))
    self.roomEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
    dateLabel = QLabel("&Date")
    self.dateEdit = QDateEdit()
    dateLabel.setBuddy(self.dateEdit)
    self.dateEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
    self.dateEdit.setDate(QDate.currentDate().addDays(1))
    self.dateEdit.setDisplayFormat("yyyy-MM-dd")
    responseLabel = QLabel("Response")
    self.responseLabel = QLabel()
    self.responseLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken)

    self.bookButton = QPushButton("&Book")
    self.bookButton.setEnabled(False)
    self.unBookButton = QPushButton("&Unbook")
    self.unBookButton.setEnabled(False)
    quitButton = QPushButton("&Quit")
    if not MAC:
      self.bookButton.setFocusPolicy(Qt.NoFocus)
      self.unBookButton.setFocusPolicy(Qt.NoFocus)

    buttonLayout = QHBoxLayout()
    buttonLayout.addWidget(self.bookButton)
    buttonLayout.addWidget(self.unBookButton)
    buttonLayout.addStretch()
    buttonLayout.addWidget(quitButton)
    layout = QGridLayout()
    layout.addWidget(roomLabel, 0, 0)
    layout.addWidget(self.roomEdit, 0, 1)
    layout.addWidget(dateLabel, 0, 2)
    layout.addWidget(self.dateEdit, 0, 3)
    layout.addWidget(responseLabel, 1, 0)
    layout.addWidget(self.responseLabel, 1, 1, 1, 3)
    layout.addLayout(buttonLayout, 2, 1, 1, 4)
    self.setLayout(layout)

    self.socket.connected.connect(self.sendRequest)
    self.socket.readyRead.connect(self.readResponse)
    self.socket.disconnected.connect(self.serverHasStopped)
    #self.connect(self.socket,
    #       SIGNAL("error(QAbstractSocket::SocketError)"),
     #      self.serverHasError)
    self.socket.error.connect(self.serverHasError)
    self.roomEdit.textEdited.connect(self.updateUi)
    self.dateEdit.dateChanged.connect(self.updateUi)

    self.bookButton.clicked.connect(self.book)
    self.unBookButton.clicked.connect(self.unBook)
    quitButton.clicked.connect(self.close)

    self.setWindowTitle("Building Services")


  def updateUi(self):
    enabled = False
    if (self.roomEdit.text() and
      self.dateEdit.date() > QDate.currentDate()):
      enabled = True
    if self.request is not None:
      enabled = False
    self.bookButton.setEnabled(enabled)
    self.unBookButton.setEnabled(enabled)


  def closeEvent(self, event):
    self.socket.close()
    event.accept()


  def book(self):
    self.issueRequest("BOOK", self.roomEdit.text(),
             self.dateEdit.date())


  def unBook(self):
    self.issueRequest("UNBOOK", self.roomEdit.text(),
             self.dateEdit.date())


  def issueRequest(self, action, room, date):
    self.request = QByteArray()
    stream = QDataStream(self.request, QIODevice.WriteOnly)
    stream.setVersion(QDataStream.Qt_5_7)
    stream.writeUInt16(0)
    stream.writeQString(action)
    stream.writeQString(room)
    stream << date
    stream.device().seek(0)
    stream.writeUInt16(self.request.size() - SIZEOF_UINT16)#overwrite seek(0)
    self.updateUi()
    if self.socket.isOpen():
      self.socket.close()
    self.responseLabel.setText("Connecting to server...")
    self.socket.connectToHost("localhost", PORT)


  def sendRequest(self):
    self.responseLabel.setText("Sending request...")
    self.nextBlockSize = 0
    self.socket.write(self.request)
    self.request = None


  def readResponse(self):
    stream = QDataStream(self.socket)
    stream.setVersion(QDataStream.Qt_5_7)

    while True:
      if self.nextBlockSize == 0:
        if self.socket.bytesAvailable() < SIZEOF_UINT16:
          break
        self.nextBlockSize = stream.readUInt16()
      if self.socket.bytesAvailable() < self.nextBlockSize:
        break
      action = ""
      room = ""
      date = QDate()
      #stream >> action >> room
      action=stream.readQString()
      room=stream.readQString()
      if action != "ERROR":
        stream >> date
      if action == "ERROR":
        msg = "Error: {0}".format(room)
      elif action == "BOOK":
        msg = "Booked room {0} for {1}".format(room,date.toString(Qt.ISODate))
      elif action == "UNBOOK":
        msg = "Unbooked room {0} for {1}".format(room,date.toString(Qt.ISODate))
      self.responseLabel.setText(msg)
      self.updateUi()
      self.nextBlockSize = 0


  def serverHasStopped(self):
    self.responseLabel.setText(
        "Error: Connection closed by server")
    self.socket.close()


  def serverHasError(self, error):
    self.responseLabel.setText("Error: {0}".format(self.socket.errorString()))
    self.socket.close()


app = QApplication(sys.argv)
form = BuildingServicesClient()
form.show()
app.exec_()

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

#!/usr/bin/env python3
import bisect
import collections
import sys
from PyQt5.QtCore import (QByteArray, QDataStream, QDate, QReadWriteLock, QThread,QIODevice, Qt)
from PyQt5.QtWidgets import (QApplication, QMessageBox, QPushButton)
from PyQt5.QtNetwork import (QAbstractSocket,QHostAddress, QTcpServer, QTcpSocket)

PORT = 9407
SIZEOF_UINT16 = 2
MAX_BOOKINGS_PER_DAY = 5

# Key = date, value = list of room IDs
Bookings = collections.defaultdict(list)


def printBookings():
  for key in sorted(Bookings):
    print(key, Bookings[key])
  print()


class Thread(QThread):

  lock = QReadWriteLock()

  def __init__(self, socketId, parent):
    super(Thread, self).__init__(parent)
    self.socketId = socketId


  def run(self):
    socket = QTcpSocket()
    if not socket.setSocketDescriptor(self.socketId):
      #self.emit(SIGNAL("error(int)"), socket.error())
      self.error.connect(socket.error)
      return
    while socket.state() == QAbstractSocket.ConnectedState:
      nextBlockSize = 0
      stream = QDataStream(socket)
      stream.setVersion(QDataStream.Qt_5_7)
      if (socket.waitForReadyRead() and
        socket.bytesAvailable() >= SIZEOF_UINT16):
        nextBlockSize = stream.readUInt16()
      else:
        self.sendError(socket, "Cannot read client request")
        return
      if socket.bytesAvailable() < nextBlockSize:
        if (not socket.waitForReadyRead(60000) or
          socket.bytesAvailable() < nextBlockSize):
          self.sendError(socket, "Cannot read client data")
          return
      action = ""
      room = ""
      date = QDate()
      action=stream.readQString()
      if action in ("BOOK", "UNBOOK"):
        room=stream.readQString()
        stream >> date
        try:
          Thread.lock.lockForRead()
          bookings = Bookings.get(date.toPyDate())
        finally:
          Thread.lock.unlock()
        uroom = str(room)
      if action == "BOOK":
        newlist = False
        try:
          Thread.lock.lockForRead()
          if bookings is None:
            newlist = True
        finally:
          Thread.lock.unlock()
        if newlist:
          try:
            Thread.lock.lockForWrite()
            bookings = Bookings[date.toPyDate()]
          finally:
            Thread.lock.unlock()
        error = None
        insert = False
        try:
          Thread.lock.lockForRead()
          if len(bookings) < MAX_BOOKINGS_PER_DAY:
            if uroom in bookings:
              error = "Cannot accept duplicate booking"
            else:
              insert = True
          else:
            error = "{0} is fully booked".format(date.toString(Qt.ISODate))
        finally:
          Thread.lock.unlock()
        if insert:
          try:
            Thread.lock.lockForWrite()
            bisect.insort(bookings, uroom)
          finally:
            Thread.lock.unlock()
          self.sendReply(socket, action, room, date)
        else:
          self.sendError(socket, error)
      elif action == "UNBOOK":
        error = None
        remove = False
        try:
          Thread.lock.lockForRead()
          if bookings is None or uroom not in bookings:
            error = "Cannot unbook nonexistent booking"
          else:
            remove = True
        finally:
          Thread.lock.unlock()
        if remove:
          try:
            Thread.lock.lockForWrite()
            bookings.remove(uroom)
          finally:
            Thread.lock.unlock()
          self.sendReply(socket, action, room, date)
        else:
          self.sendError(socket, error)
      else:
        self.sendError(socket, "Unrecognized request")
      socket.waitForDisconnected()
      try:
        Thread.lock.lockForRead()
        printBookings()
      finally:
        Thread.lock.unlock()


  def sendError(self, socket, msg):
    reply = QByteArray()
    stream = QDataStream(reply, QIODevice.WriteOnly)
    stream.setVersion(QDataStream.Qt_5_7)
    stream.writeUInt16(0)
    stream.writeQString("ERROR")
    stream.writeQString(msg)
    stream.device().seek(0)
    stream.writeUInt16(reply.size() - SIZEOF_UINT16)
    socket.write(reply)

  def sendReply(self, socket, action, room, date):
    reply = QByteArray()
    stream = QDataStream(reply, QIODevice.WriteOnly)
    stream.setVersion(QDataStream.Qt_5_7)
    stream.writeUInt16(0)
    stream.writeQString(action)
    stream.writeQString(room)
    stream<<date
    stream.device().seek(0)
    stream.writeUInt16(reply.size() - SIZEOF_UINT16)
    socket.write(reply)

class TcpServer(QTcpServer):

  def __init__(self, parent=None):
    super(TcpServer, self).__init__(parent)


  def incomingConnection(self, socketId):
    thread = Thread(socketId, self)
    #self.connect(thread, SIGNAL("finished()"),
    #       thread, SLOT("deleteLater()"))
    thread.finished.connect(thread.deleteLater)
    thread.start()


class BuildingServicesDlg(QPushButton):

  def __init__(self, parent=None):
    super(BuildingServicesDlg, self).__init__(
        "&Close Server", parent)
    self.setWindowFlags(Qt.WindowStaysOnTopHint)

    self.loadBookings()
    self.tcpServer = TcpServer(self)
    if not self.tcpServer.listen(QHostAddress("0.0.0.0"), PORT):
      QMessageBox.critical(self, "Building Services Server","Failed to start server: {0}".format(self.tcpServer.errorString()))
      self.close()
      return

    self.clicked.connect(self.close)
    font = self.font()
    font.setPointSize(24)
    self.setFont(font)
    self.setWindowTitle("Building Services Server")


  def loadBookings(self):
    # Generate fake data
    import random

    today = QDate.currentDate()
    for i in range(10):
      date = today.addDays(random.randint(7, 60))
      for j in range(random.randint(1, MAX_BOOKINGS_PER_DAY)):
        # Rooms are 001..534 excl. 100, 200, ..., 500
        floor = random.randint(0, 5)
        room = random.randint(1, 34)
        bookings = Bookings[date.toPyDate()]
        if len(bookings) >= MAX_BOOKINGS_PER_DAY:
          continue
        bisect.insort(bookings, "{0:1d}{1:02d}".format(
               floor, room))
    printBookings()


app = QApplication(sys.argv)
form = BuildingServicesDlg()
form.show()
form.move(0, 0)
app.exec_()

以上这篇python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python程序设计入门(1)基本语法简介
Jun 13 Python
Python xlrd读取excel日期类型的2种方法
Apr 28 Python
python框架django基础指南
Sep 08 Python
python取代netcat过程分析
Feb 10 Python
python 读入多行数据的实例
Apr 19 Python
Python3实现的简单验证码识别功能示例
May 02 Python
Python requests库用法实例详解
Aug 14 Python
对numpy中向量式三目运算符详解
Oct 31 Python
Python基于聚类算法实现密度聚类(DBSCAN)计算【测试可用】
Dec 26 Python
python字符串中匹配数字的正则表达式
Jul 03 Python
pytorch神经网络之卷积层与全连接层参数的设置方法
Aug 18 Python
Python实现数值积分方式
Nov 20 Python
python 应用之Pycharm 新建模板默认添加编码格式-作者-时间等信息【推荐】
Jun 17 #Python
python3+PyQt5 使用三种不同的简便项窗口部件显示数据的方法
Jun 17 #Python
对PyQt5中树结构的实现方法详解
Jun 17 #Python
PyQT实现菜单中的复制,全选和清空的功能的方法
Jun 17 #Python
使用python接入微信聊天机器人
Mar 31 #Python
基于树莓派的语音对话机器人
Jun 17 #Python
PyQt5 QListWidget选择多项并返回的实例
Jun 17 #Python
You might like
php木马攻击防御之道
2008/03/24 PHP
PHP strtr() 函数使用说明
2008/11/21 PHP
五款常用mysql slow log分析工具的比较分析
2011/05/22 PHP
ThinkPHP分组下自定义标签库实例
2014/11/01 PHP
33道php常见面试题及答案
2015/07/06 PHP
PHP之多条件混合筛选功能的实现方法
2019/10/09 PHP
laravel框架学习笔记之组件化开发实现方法
2020/02/01 PHP
关于jQuery object and DOM element
2013/04/15 Javascript
JS 删除字符串最后一个字符的实现代码
2014/02/20 Javascript
JavaScript保留两位小数的2个自定义函数
2014/05/05 Javascript
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
js中的如何定位固定层的位置
2014/06/15 Javascript
js中回调函数的学习笔记
2014/07/31 Javascript
jQuery实现鼠标跟随提示层效果代码(可显示文本,Div,Table,Html等)
2016/04/18 Javascript
第七篇Bootstrap表单布局实例代码详解(三种表单布局)
2016/06/21 Javascript
Laydate时间组件在火狐浏览器下有多时间输入框时只能给第一个输入框赋值的解决方法
2016/08/18 Javascript
layer弹出层中H5播放器全屏出错的解决方法
2017/02/21 Javascript
AngularJS中table表格基本操作示例
2017/10/10 Javascript
详解如何在react中搭建d3力导向图
2018/01/12 Javascript
Express下采用bcryptjs进行密码加密的方法
2018/02/07 Javascript
Vue中v-for的数据分组实例
2018/03/07 Javascript
微信小程序基于canvas渐变实现的彩虹效果示例
2019/05/03 Javascript
Python3指定路径寻找符合匹配模式文件
2015/05/22 Python
Python爬虫爬取Bilibili弹幕过程解析
2019/10/10 Python
澳大利亚儿童鞋在线:The Trybe
2019/07/16 全球购物
现代化办公人员工作的自我评价
2013/10/16 职场文书
共产党员公开承诺书范文
2014/03/28 职场文书
消防标语大全
2014/06/07 职场文书
学校宣传标语
2014/06/18 职场文书
文秘应届生求职信
2014/07/05 职场文书
公司新人试用期自我评价
2014/09/17 职场文书
单位单身证明样本
2014/10/11 职场文书
2014社区健康教育工作总结
2014/12/16 职场文书
无保留意见审计报告
2015/06/05 职场文书
只用Python就可以制作的简单词云
2021/06/07 Python
详解Flutter网络请求Dio库的使用及封装
2022/04/14 Java/Android