class ServerThread(QThread): def __init__(self, ip, port, console): super().__init__() self.ip = ip self.port = port self.tcpServer = QTcpServer() self.clientList = [] address = QHostAddress(self.ip) if not self.tcpServer.listen(address, self.port): print("cant listen!") self.tcpServer.close() return self.tcpServer.newConnection.connect(self.addConnection) self.receviedDataSignal = pyqtSignal(str) # This generate error Attribue No attribute connect ! # self.receviedDataSignal.connect(console.receivePacket) def run(self): self.tcpServer.moveToThread(self.thread()) while True: for conn in self.clientList: self.echoConnection(conn) self.closeAllTCPClient() def addConnection(self): connectionToAdd = self.tcpServer.nextPendingConnection() self.clientList.append(connectionToAdd) def echoConnection(self, clientConnection): readData = self.readFromTCPSocket(clientConnection) print("[HOST] Received from ", clientConnection.localAddress().toString(), clientConnection.localPort()) print(readData) # Add write to our console # self.receviedDataSignal.emit(readData) def readFromTCPSocket(self, tcpSocket): tcpSocket.waitForReadyRead() data = tcpSocket.readAll() return data def closeAllTCPClient(self): for conn in self.clientList: conn.disconnectFromHost() def readNewConnection(self): conn = self.tcpServer.nextPendingConnection() self.echoConnection(conn)
class TeacherSessionManager(SessionManager): def __init__(self, gui): SessionManager.__init__(self, gui) self.session_type = SessionType.TEACHER self.gui = gui self.session_ticker = Ticker(self.tickSessionOnce, parent=gui) self.simulation_paused_at = None # pause time if session is paused; None otherwise self.server = QTcpServer(gui) self.student_socket = None self.server.newConnection.connect(self.studentConnects) self.aircraft_list = [] # ControlledAircraft list self.current_local_weather = None # initialised by the teaching console on sessionStarted self.noACK_traffic_count = 0 def start(self): self.aircraft_list.clear() self.simulation_paused_at = None self.session_ticker.start_stopOnZero(teacher_ticker_interval) self.server.listen(port=settings.teaching_service_port) print('Teaching server ready on port %d' % settings.teaching_service_port) signals.specialTool.connect(self.createNewTraffic) signals.kbdPTT.connect(self.sendPTT) signals.sessionStarted.emit() def stop(self): if self.isRunning(): self.session_ticker.stop() if self.studentConnected(): self.shutdownStudentConnection() signals.specialTool.disconnect(self.createNewTraffic) signals.kbdPTT.disconnect(self.sendPTT) self.server.close() self.aircraft_list.clear() signals.sessionEnded.emit() def studentConnected(self): return self.student_socket != None def isRunning(self): return self.session_ticker.isActive( ) or self.simulation_paused_at != None def myCallsign(self): return teacher_callsign def getAircraft(self): return self.aircraft_list[:] def getWeather(self, station): return self.current_local_weather if station == settings.primary_METAR_station else None def postRadioChatMsg(self, msg): raise ValueError( 'Public radio chat panel reserved for monitoring read-backs in teacher sessions. ' 'Use the ATC text chat system to communicate with the student.') def postAtcChatMsg(self, msg): if self.studentConnected(): if msg.isPrivate(): payload = '%s\n%s' % (msg.sender(), msg.txtOnly()) self.student.sendMessage( TeachingMsg(TeachingMsg.ATC_TEXT_CHAT, data=payload)) else: raise ValueError( 'Only private messaging is enabled in tutoring sessions.') else: raise ValueError('No student connected.') ## CONNECTION MANAGEMENT def studentConnects(self): new_connection = self.server.nextPendingConnection() if new_connection: peer_address = new_connection.peerAddress().toString() print('Contacted by %s' % peer_address) if self.studentConnected(): new_connection.disconnectFromHost() print('Client rejected. Student already connected.') else: self.student_socket = new_connection self.student_socket.disconnected.connect( self.studentDisconnects) self.student_socket.disconnected.connect( self.student_socket.deleteLater) self.student = TeachingSessionWire(self.student_socket) self.student.messageArrived.connect(self.receiveMsgFromStudent) env.ATCs.updateATC(student_callsign, None, None, None) self.noACK_traffic_count = 0 self.sendWeather() self.sendATCs() self.tickSessionOnce() if self.simulation_paused_at != None: self.student.sendMessage( TeachingMsg(TeachingMsg.SIM_PAUSED)) QMessageBox.information( self.gui, 'Student connection', 'Student accepted from %s' % peer_address) else: print('WARNING: Connection attempt failed.') def studentDisconnects(self): self.shutdownStudentConnection() QMessageBox.information(self.gui, 'Student disconnection', 'Your student has disconnected.') def shutdownStudentConnection(self): self.student_socket.disconnected.disconnect(self.studentDisconnects) env.cpdlc.endAllDataLinks() env.ATCs.removeATC(student_callsign) self.student.messageArrived.disconnect(self.receiveMsgFromStudent) self.student_socket.disconnectFromHost() self.student_socket = None ## TEACHER MANAGEMENT def instructAircraftByCallsign(self, callsign, instr): try: acft = next(acft for acft in self.aircraft_list if acft.identifier == callsign) except StopIteration: print('ERROR: Teacher aircraft not found: %s' % callsign) return try: acft.instruct([instr]) acft.readBack([instr]) except Instruction.Error as err: QMessageBox.critical(self.gui, 'Instruction error', speech_str2txt(str(err))) def createNewTraffic(self, spawn_coords, spawn_hdg): dialog = CreateTrafficDialog(spawn_coords, spawn_hdg, parent=self.gui) dialog.exec() if dialog.result() > 0: params = dialog.acftInitParams() params.XPDR_mode = new_traffic_XPDR_mode acft = ControlledAircraft(dialog.acftCallsign(), dialog.acftType(), params, None) acft.spawned = False acft.frozen = dialog.startFrozen() acft.tickOnce() self.aircraft_list.append(acft) if dialog.createStrip(): strip = Strip() strip.writeDetail(FPL.CALLSIGN, acft.identifier) strip.writeDetail(FPL.ACFT_TYPE, acft.aircraft_type) strip.writeDetail(FPL.WTC, wake_turb_cat(acft.aircraft_type)) strip.linkAircraft(acft) signals.receiveStrip.emit(strip) selection.selectAircraft(acft) def killAircraft(self, acft): if env.cpdlc.isConnected(acft.identifier): env.cpdlc.endDataLink(acft.identifier) pop_all(self.aircraft_list, lambda a: a is acft) if acft.spawned and self.studentConnected(): self.student.sendMessage( TeachingMsg(TeachingMsg.ACFT_KILLED, data=acft.identifier)) signals.aircraftKilled.emit(acft) def sendATCs(self): if self.studentConnected(): msg = TeachingMsg(TeachingMsg.SX_LIST) for atc in env.ATCs.knownATCs( lambda atc: atc.callsign != student_callsign): try: frq = env.ATCs.getATC( atc ).frequency # instance of CommFrequency class, or None except KeyError: frq = None msg.appendData(atc if frq == None else '%s\t%s' % (atc, frq)) msg.appendData('\n') self.student.sendMessage(msg) def setWeather(self, weather): # assumed at primary location and newer self.current_local_weather = weather signals.newWeather.emit(settings.primary_METAR_station, self.current_local_weather) self.sendWeather() def sendWeather(self): if self.studentConnected() and self.current_local_weather != None: self.student.sendMessage( TeachingMsg(TeachingMsg.WEATHER, data=self.current_local_weather.METAR())) def sendPTT(self, ignore_button, on_off): if selection.acft != None and selection.acft.spawned: if on_off: env.rdf.receiveSignal( selection.acft.identifier, lambda acft=selection.acft: acft.coords()) else: env.rdf.dieSignal(selection.acft.identifier) if self.studentConnected(): str_data = '%d %s' % (on_off, selection.acft.identifier) self.student.sendMessage( TeachingMsg(TeachingMsg.PTT, data=str_data)) def requestCpdlcConnection( self, callsign): # NOTE: student must confirm data link if self.studentConnected(): self.student.sendMessage( TeachingMsg(TeachingMsg.CPDLC, data=('%s\n1' % callsign))) def transferCpdlcAuthority( self, acft_callsign, atc_callsign ): # for teacher, ATC here is who to transfer *from* to student if self.studentConnected(): self.student.sendMessage(TeachingMsg(TeachingMsg.CPDLC, \ data=('%s\n%s%s' % (acft_callsign, CPDLC_transfer_cmd_prefix, atc_callsign)))) # NOTE: student must confirm data link def disconnectCpdlc(self, callsign): env.cpdlc.endDataLink(callsign) if self.studentConnected(): self.student.sendMessage( TeachingMsg(TeachingMsg.CPDLC, data=('%s\n0' % callsign))) def sendCpdlcMsg(self, callsign, msg): link = env.cpdlc.currentDataLink(callsign) if link == None: return if msg.type() == CpdlcMessage.ACK and link.msgCount() > 0: last_msg = link.lastMsg() if not last_msg.isFromMe() and last_msg.type() == CpdlcMessage.INSTR \ and yesNo_question(self.gui, 'ACK after received INSTR', last_msg.contents(), 'Execute instruction?'): try: instr = Instruction.fromEncodedStr(last_msg.contents()) self.instructAircraftByCallsign(callsign, instr) except ValueError: # raised by Instruction.fromEncodedStr if not yesNo_question(self.gui, 'CPDLC comm error', \ 'Unable to decode instruction.', 'Send ACK and perform manually?'): return # cancel sending any message except Instruction.Error as err: # raised by TeacherSessionManager.instructAircraftByCallsign if not yesNo_question(self.gui, 'CPDLC instruction error', \ 'Unable to perform instruction: %s' % err, 'Send ACK anyway?'): return # cancel sending any message else: # no problem executing instruction selection.writeStripAssignment(instr) if self.studentConnected() and link != None: link.appendMessage(msg) self.student.sendMessage( TeachingMsg( TeachingMsg.CPDLC, data=('%s\n%s%s' % (callsign, CPDLC_message_cmd_prefix, msg.text())))) def pauseSession(self): self.session_ticker.stop() self.simulation_paused_at = now() signals.sessionPaused.emit() if self.studentConnected(): self.student.sendMessage(TeachingMsg(TeachingMsg.SIM_PAUSED)) def resumeSession(self): pause_delay = now() - self.simulation_paused_at for acft in self.aircraft_list: acft.moveHistoryTimesForward(pause_delay) self.simulation_paused_at = None self.session_ticker.start_stopOnZero(teacher_ticker_interval) signals.sessionResumed.emit() if self.studentConnected(): self.student.sendMessage(TeachingMsg(TeachingMsg.SIM_RESUMED)) ## MESSAGES FROM STUDENT def receiveMsgFromStudent(self, msg): #DEBUG if msg.type != TeachingMsg.TRAFFIC: #DEBUG print('=== TEACHERS RECEIVES ===\n%s\n=== End ===' % msg.data) if msg.type == TeachingMsg.ATC_TEXT_CHAT: lines = msg.strData().split('\n') if len(lines) == 2: signals.incomingAtcTextMsg.emit( ChatMessage(student_callsign, lines[1], recipient=lines[0], private=True)) else: print( 'ERROR: Invalid format in received ATC text chat from student.' ) elif msg.type == TeachingMsg.STRIP_EXCHANGE: line_sep = msg.strData().split('\n', maxsplit=1) toATC = line_sep[0] strip = Strip.fromEncodedDetails( '' if len(line_sep) < 2 else line_sep[1]) strip.writeDetail(received_from_detail, student_callsign) if toATC != teacher_callsign: strip.writeDetail(sent_to_detail, toATC) signals.receiveStrip.emit(strip) elif msg.type == TeachingMsg.WEATHER: # requesting weather information if msg.strData() == settings.primary_METAR_station: self.sendWeather() elif msg.type == TeachingMsg.TRAFFIC: # acknowledging a traffic message if self.noACK_traffic_count > 0: self.noACK_traffic_count -= 1 else: print('ERROR: Student acknowledging unsent traffic?!') elif msg.type == TeachingMsg.CPDLC: # Msg format in 2 lines, first being ACFT callsign, second is either of the following: # - connect/disconnect: "0" or "1" # - data authority transfer: CPDLC_transfer_cmd_prefix + ATC callsign transferring to/from # - other: CPDLC_message_cmd_prefix + encoded message string try: acft_callsign, line2 = msg.strData().split('\n', maxsplit=1) if line2 == '0': # ACFT disconnected by student if env.cpdlc.isConnected(acft_callsign): env.cpdlc.endDataLink(acft_callsign) else: # student is rejecting a connection (unable CPDLC) QMessageBox.warning( self.gui, 'CPDLC connection failed', 'Student is not accepting CPDLC connections.') elif line2 == '1': # student confirming ACFT log-on env.cpdlc.beginDataLink(acft_callsign, student_callsign) elif line2.startswith( CPDLC_transfer_cmd_prefix ): # student transferring or confirming transfer atc = line2[len(CPDLC_transfer_cmd_prefix):] if env.cpdlc.isConnected( acft_callsign ): # student initiating transfer to next ATC env.cpdlc.endDataLink(acft_callsign, transferTo=atc) else: # student confirming proposed transfer env.cpdlc.beginDataLink(acft_callsign, student_callsign, transferFrom=atc) elif line2.startswith(CPDLC_message_cmd_prefix ): # student ATC sent a message encoded_msg = line2[len(CPDLC_message_cmd_prefix):] link = env.cpdlc.currentDataLink(acft_callsign) if link == None: print( 'Ignored CPDLC message sent to %s while not connected.' % acft_callsign) else: link.appendMessage( CpdlcMessage.fromText(False, encoded_msg)) else: print('Error decoding CPDLC command from student:', line2) except (IndexError, ValueError): print('Error decoding CPDLC message value from student') else: print('ERROR: Unhandled message type from student: %s' % msg.type) ## TICK def tickSessionOnce(self): pop_all(self.aircraft_list, lambda a: not env.pointInRadarRange(a.params.position)) send_traffic_this_tick = self.studentConnected( ) and self.noACK_traffic_count < max_noACK_traffic for acft in self.aircraft_list: acft.tickOnce() fgms_packet = acft.fgmsLivePositionPacket() send_packet_to_views(fgms_packet) if send_traffic_this_tick and acft.spawned: self.student.sendMessage( TeachingMsg(TeachingMsg.TRAFFIC, data=fgms_packet)) self.noACK_traffic_count += 1 ## STRIP EXCHANGE def stripDroppedOnATC(self, strip, sendto): if sendto == student_callsign: items = [teacher_callsign] + env.ATCs.knownATCs( lambda atc: atc.callsign != student_callsign) sender, ok = QInputDialog.getItem(self.gui, 'Send strip to student', 'Hand over strip from:', items, editable=False) if ok and self.studentConnected(): msg_data = sender + '\n' + strip.encodeDetails( handover_details) self.student.sendMessage( TeachingMsg(TeachingMsg.STRIP_EXCHANGE, data=msg_data)) else: raise HandoverBlocked('Cancelled by teacher.', silent=True) else: raise HandoverBlocked('Strips can only be sent to the student!') ## SNAPSHOTTING def situationSnapshot(self): return [acft.statusSnapshot() for acft in self.aircraft_list] def restoreSituation(self, situation_snapshot): while self.aircraft_list != []: self.killAircraft(self.aircraft_list[0]) for acft_snapshot in situation_snapshot: self.aircraft_list.append( ControlledAircraft.fromStatusSnapshot(acft_snapshot)) self.tickSessionOnce()
class TcpS(QDialog, Ui_TcpServer): """ 文件传输服务器 """ sendFileName = pyqtSignal(str) def __init__(self, parent=None): """ 一些初始设置 """ super(TcpS, self).__init__(parent) self.setupUi(self) self.payloadSize = 64 * 1024 # 读取数据64KB self.totalBytes = 0 # 总大小 self.bytesWritten = 0 # 保存的数据 self.bytesToWrite = 0 # 每次减少连接写的数据量大小 self.theFileName = "" # 文件名(不含路径) self.fileName = "" # 文件全名 self.localFile = QFile() self.outBlock = QByteArray() # QByteArray()的对象,即字节数组 self.time = QTime() self.initServer() def initServer(self): """ 网络设置初始化 """ self.tcpPort = 7788 # 指定了TCP端口为7788 self.tcpServer = QTcpServer(self) self.clientConnection = QTcpSocket(self) # 创建一个Tcp服务器和一个Tcp套接字 self.tcpServer.newConnection.connect(self.sendMessage) # 当有新的连接来的时候发出newConnection信号,我们连接到sendMessage()函数。 self.serverStatuslabel.setText("请选择要传送的文件") self.progressBar.reset() self.serverOpenBtn.setEnabled(True) self.serverSendBtn.setEnabled(False) self.tcpServer.close() # 显示我们开始创建的对话框,打开按钮是可用的,发送按钮是不可用的,进度条复位,先关闭服务器。 def refused(self): """ 对端拒绝接收文件,主程序会调用服务器的refused()函数,关闭服务器。 """ self.tcpServer.close() self.serverStatuslabel.setText("对方拒绝接收") def closeEvent(self, event): """ 关闭事件 """ self.on_serverCloseBtn_clicked() # 产生关闭事件,直接调用关闭窗口按钮函数。 def sendMessage(self): """ 发送文件 """ self.serverSendBtn.setEnabled(False) # 发送按钮不可用 self.clientConnection = self.tcpServer.nextPendingConnection() # self.clientConnection作为连接的QTcpSocket对象返回下一个挂起的连接。 self.clientConnection.bytesWritten.connect(self.updateClientProgress) # 当连接中每次将数据有效载荷写入设备的当前写通道时,都会发出此信号。在此有效负载中写入的数据量为字节数。 self.serverStatuslabel.setText("开始传送文件 {} !".format(self.theFileName)) self.localFile = QFile(self.fileName) if not (self.localFile.open(QFile.ReadOnly)): errorMsg = "无法读取文件 {}:\n {}".format(self.fileName, self.localFile.errorString()) QMessageBox.warning(self, "应用程序", errorMsg) return # 尝试打开文件,要是存在问题就报错。 self.serverCloseBtn.setText("取消") self.totalBytes = self.localFile.size() # 记录一下需要传输的文件大小。单位:字节 sendOut = QDataStream(self.outBlock, QIODevice.WriteOnly) # 这里的self.outBlock是QByteArray()的对象,即字节数组;QIODevice的模式为WriteOnly sendOut.setVersion(QDataStream.Qt_5_4) # 设定QDataStream的版本为Qt_5_4 self.time.start() # 开始计时 currentFile = self.fileName.split("/")[-1] # 传输的文件名 sendOut.writeInt64(0) sendOut.writeInt64(0) sendOut.writeQString(currentFile) self.totalBytes += self.outBlock.size() # 在sendOut中写入文件名以及文件名和文件的大小,大小都是以字节为单位的。 sendOut.device().seek(0) sendOut.writeInt64(self.totalBytes) sendOut.writeInt64(self.outBlock.size() - 2) # QIODevice读写位置移动到0。然后分别写入总的大小和文件名大小。 self.bytesToWrite = self.totalBytes - self.clientConnection.write( self.outBlock) # 待传输文件的大小。 self.outBlock.resize(0) # outBlock清零。 def updateClientProgress(self, numBytes): """ 发送进度显示 """ qApp.processEvents() # 长时间工作用,以免窗口假死 self.bytesWritten += numBytes if self.bytesWritten > 0: self.block = self.localFile.read( min(self.bytesToWrite, self.payloadSize)) self.bytesToWrite -= self.clientConnection.write(self.block) else: self.localFile.close() # 当我们待写入的字节数大于0时,我们每次读取的数据都是小于等于self.payloadSize的,这个self.payloadSize我们定义是64KB。 # self.bytesToWrite每次减少连接写的数据量大小。 # 要是待写入的字节数小于等于0,则关闭文件。 byteSent = self.bytesWritten / (1024 * 1024) # 已经写了多少文件 useTime = self.time.elapsed() / 1000 # 传输用了多长时间 speed = self.bytesWritten / useTime / (1024 * 1024) # 传输速度 total = self.totalBytes / (1024 * 1024) # 总大小 left = (total - byteSent) / speed # 表示剩余时间 if byteSent < 0.01: byteSent = self.bytesWritten / 1024 speed = self.bytesWritten / useTime / 1024 total = self.totalBytes / 1024 if left > 0: sendInfo = "已发送 {0:.2f}KB({1:.2f}KB/s)\n共{2:.2f}KB 已用时:{3:.1f}秒\n 估计剩余时间:{4:.1f}秒".format( byteSent, speed, total, useTime, left) else: sendInfo = "已发送 {0:.2f}KB({1:.2f}KB/s)\n共{2:.2f}KB 用时:{3:.1f}秒\n".format( byteSent, speed, total, useTime) else: if left > 0: sendInfo = "已发送 {0:.2f}MB({1:.2f}MB/s)\n共{2:.2f}MB 已用时:{3:.1f}秒\n 估计剩余时间:{4:.1f}秒".format( byteSent, speed, total, useTime, left) else: sendInfo = "已发送 {0:.2f}MB({1:.2f}MB/s)\n共{2:.2f}MB 用时:{3:.1f}秒\n".format( byteSent, speed, total, useTime) self.progressBar.setMaximum(total) self.progressBar.setValue(byteSent) if self.bytesWritten == self.totalBytes: self.serverCloseBtn.setText("关闭") # 进度条显示的方式,以及当传输的字节数等于总的字节数的时候,按钮就显示关闭。 self.serverStatuslabel.setText(sendInfo) @pyqtSlot() def on_serverOpenBtn_clicked(self): """ 打开文件准备发送 """ self.fileName = QFileDialog.getOpenFileName(self, '打开文件', './')[0] if self.fileName: self.theFileName = self.fileName.split("/")[-1] self.serverStatuslabel.setText("要传送的文件为:{}".format( self.theFileName)) self.serverSendBtn.setEnabled(True) self.serverOpenBtn.setEnabled(False) @pyqtSlot() def on_serverSendBtn_clicked(self): """ 发送文件,等待接收 """ if not (self.tcpServer.listen(QHostAddress.Any, self.tcpPort)): errorMsg = self.tcpServer.errorString() QMessageBox.warning(self, "错误", "发送失败:\n {}".format(errorMsg)) self.TcpServer.close() return self.serverStatuslabel.setText("等待对方接收... ...") self.serverSendBtn.setEnabled(False) self.sendFileName.emit(self.theFileName) @pyqtSlot() def on_serverCloseBtn_clicked(self): """ 取消或者关闭 """ if self.tcpServer.isListening(): self.tcpServer.close() if self.localFile.isOpen(): self.localFile.close() self.clientConnection.abort() if self.serverCloseBtn.text() == "取消": self.serverCloseBtn.setText("关闭") else: self.close() self.serverOpenBtn.setEnabled(True) self.serverSendBtn.setEnabled(False) self.progressBar.reset() self.totalBytes = 0 self.bytesWritten = 0 self.bytesToWrite = 0 self.serverStatuslabel.setText("请选择要传送的文件")
class Server(QWidget): def __init__(self): QWidget.__init__(self) self.serverOpen = False self.tcpServer = None self.tcpServerConnection = None self.textEdit = None self.lineEdit = None self.create_widgets() def create_widgets(self): self.tcpServer = QTcpServer() self.textEdit = QTextEdit() self.textEdit.setReadOnly(True) self.lineEdit = QLineEdit() self.lineEdit.setPlaceholderText("Enter response") self.lineEdit.setMaxLength(50) self.lineEdit.setEnabled(True) vBox = QVBoxLayout() vBox.addWidget(self.textEdit) vBox.addWidget(self.lineEdit) self.setLayout(vBox) self.tcpServer.newConnection.connect(self.accept_connection) def add_message(self, message): self.textEdit.moveCursor(QTextCursor.End) self.textEdit.insertPlainText(message) self.textEdit.moveCursor(QTextCursor.End) def open_server(self): if (len(self.lineEdit.text()) == 0): self.add_message("You must enter a response\n") QMessageBox.critical(self, "Error", "You must enter a response") return False self.tcpServer.listen(QHostAddress(__serverUrl__), 9000) self.serverOpen = self.tcpServer.isListening() if (self.serverOpen): serverAddress = self.tcpServer.serverAddress().toString() serverPort = self.tcpServer.serverPort() self.add_message("Server opened on %s on port %d\n" % (serverAddress, serverPort)) self.lineEdit.setEnabled(False) else: self.add_message(self.tcpServer.errorString()) QMessageBox.critical(self, "Error", self.tcpServer.errorString()) return True def accept_connection(self): self.tcpServerConnection = self.tcpServer.nextPendingConnection() self.tcpServerConnection.readyRead.connect(self.receive_data) self.tcpServerConnection.disconnected.connect(self.socket_disconnected) peerAddress = self.tcpServerConnection.peerAddress().toString() peerPort = self.tcpServerConnection.peerPort() self.add_message("Connected to %s on port %d\n" % (peerAddress, peerPort)) def receive_data(self): rxData = self.tcpServerConnection.readAll() self.add_message("Received '" + rxData.data().decode('utf8') + "'\n") txString = self.lineEdit.text() self.tcpServerConnection.write(txString.encode()) self.add_message("Wrote '" + txString + "'\n") def socket_disconnected(self): self.add_message("Disconnected from client\n") def close_server(self): if (self.serverOpen): self.tcpServer.close() self.lineEdit.setEnabled(True)
class Dialog(QDialog): TotalBytes = 50 * 1024 * 1024 PayloadSize = 65536 def __init__(self, parent=None): super(Dialog, self).__init__(parent) self.tcpServer = QTcpServer() self.tcpClient = QTcpSocket() self.bytesToWrite = 0 self.bytesWritten = 0 self.bytesReceived = 0 self.clientProgressBar = QProgressBar() self.clientStatusLabel = QLabel("Client ready") self.serverProgressBar = QProgressBar() self.serverStatusLabel = QLabel("Server ready") self.startButton = QPushButton("&Start") self.quitButton = QPushButton("&Quit") buttonBox = QDialogButtonBox() buttonBox.addButton(self.startButton, QDialogButtonBox.ActionRole) buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole) self.startButton.clicked.connect(self.start) self.quitButton.clicked.connect(self.close) self.tcpServer.newConnection.connect(self.acceptConnection) self.tcpClient.connected.connect(self.startTransfer) self.tcpClient.bytesWritten.connect(self.updateClientProgress) self.tcpClient.error.connect(self.displayError) mainLayout = QVBoxLayout() mainLayout.addWidget(self.clientProgressBar) mainLayout.addWidget(self.clientStatusLabel) mainLayout.addWidget(self.serverProgressBar) mainLayout.addWidget(self.serverStatusLabel) mainLayout.addStretch(1) mainLayout.addSpacing(10) mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) self.setWindowTitle("Loopback") def start(self): self.startButton.setEnabled(False) QApplication.setOverrideCursor(Qt.WaitCursor) self.bytesWritten = 0 self.bytesReceived = 0 while not self.tcpServer.isListening() and not self.tcpServer.listen(): ret = QMessageBox.critical( self, "Loopback", "Unable to start the test: %s." % self.tcpServer.errorString(), QMessageBox.Retry | QMessageBox.Cancel, ) if ret == QMessageBox.Cancel: return self.serverStatusLabel.setText("Listening") self.clientStatusLabel.setText("Connecting") self.tcpClient.connectToHost(QHostAddress(QHostAddress.LocalHost), self.tcpServer.serverPort()) def acceptConnection(self): self.tcpServerConnection = self.tcpServer.nextPendingConnection() self.tcpServerConnection.readyRead.connect(self.updateServerProgress) self.tcpServerConnection.error.connect(self.displayError) self.serverStatusLabel.setText("Accepted connection") self.tcpServer.close() def startTransfer(self): self.bytesToWrite = Dialog.TotalBytes - self.tcpClient.write( QByteArray(Dialog.PayloadSize, "@")) self.clientStatusLabel.setText("Connected") def updateServerProgress(self): self.bytesReceived += self.tcpServerConnection.bytesAvailable() self.tcpServerConnection.readAll() self.serverProgressBar.setMaximum(Dialog.TotalBytes) self.serverProgressBar.setValue(self.bytesReceived) self.serverStatusLabel.setText("Received %dMB" % (self.bytesReceived / (1024 * 1024))) if self.bytesReceived == Dialog.TotalBytes: self.tcpServerConnection.close() self.startButton.setEnabled(True) QApplication.restoreOverrideCursor() def updateClientProgress(self, numBytes): self.bytesWritten += numBytes if self.bytesToWrite > 0: self.bytesToWrite -= self.tcpClient.write( QByteArray(min(self.bytesToWrite, Dialog.PayloadSize), "@")) self.clientProgressBar.setMaximum(Dialog.TotalBytes) self.clientProgressBar.setValue(self.bytesWritten) self.clientStatusLabel.setText("Sent %dMB" % (self.bytesWritten / (1024 * 1024))) def displayError(self, socketError): if socketError == QTcpSocket.RemoteHostClosedError: return QMessageBox.information( self, "Network error", "The following error occured: %s." % self.tcpClient.errorString(), ) self.tcpClient.close() self.tcpServer.close() self.clientProgressBar.reset() self.serverProgressBar.reset() self.clientStatusLabel.setText("Client ready") self.serverStatusLabel.setText("Server ready") self.startButton.setEnabled(True) QApplication.restoreOverrideCursor()
class Window(QWidget, Ui_FormFrameworkTools): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.setupUi(self) # 初始化server self._server = QTcpServer(self) self._server.newConnection.connect(self.onNewConnection) self._server.listen(QHostAddress.LocalHost, 49496) self._getPsPath() self._initCodeEdit() # 设置默认代码和参数 self.argsEdit.setPlainText('testColor("{}")'.format( os.path.abspath('Resources/GM6C6860.jpg').replace('\\', '/'))) self.codeEdit.setText(self._formatArgs(Template)) def _formatArgs(self, code): code = code.replace( '#1#', os.path.abspath('Resources/ProgressBar.jsx').replace('\\', '/')) code = code.replace( '#2#', os.path.abspath('Resources/Core.jsx').replace('\\', '/')) code = code.replace( '#3#', os.path.abspath('Resources/test.jsx').replace('\\', '/')) return code def _initCodeEdit(self): # 初始化编辑器的工作 self.codeEdit.setUtf8(True) self.codeEdit.linesChanged.connect(self.onLinesChanged) # 行改变 # 代码高亮 self.codeEdit.setLexer(QsciLexerJavaScript(self)) # 自动折叠 self.codeEdit.setMarginType(3, QsciScintilla.SymbolMargin) self.codeEdit.setMarginLineNumbers(3, False) self.codeEdit.setMarginWidth(3, 15) self.codeEdit.setMarginSensitivity(3, True) # 显示行号 #self.codeEdit.setMarginType(0, QsciScintilla.NumberMargin) self.codeEdit.setMarginLineNumbers(0, True) self.onLinesChanged() # 代码提示 sciApi = QsciAPIs(self.codeEdit.lexer()) sciApi.prepare() self.codeEdit.setAutoCompletionSource(QsciScintilla.AcsAll) # 设置源 self.codeEdit.setAutoCompletionCaseSensitivity(True) # 设置自动补全大小写敏感 self.codeEdit.setAutoCompletionThreshold(1) # 设置每输入一个字符就会出现自动补全的提示 # 设置字体 self.codeEdit.setFont(QFont('Consolas', 16)) self.codeEdit.setMarginsFont(self.codeEdit.font()) # 设置编码 self.codeEdit.SendScintilla(QsciScintilla.SCI_SETCODEPAGE, QsciScintilla.SC_CP_UTF8) self.codeEdit.setBraceMatching(QsciScintilla.StrictBraceMatch) # 设置当前行高亮 self.codeEdit.setCaretLineVisible(True) self.codeEdit.setCaretLineBackgroundColor(Qt.lightGray) self.codeEdit.setCaretForegroundColor(Qt.white) # tab # table relative self.codeEdit.setIndentationsUseTabs(True) self.codeEdit.setIndentationWidth(4) self.codeEdit.setTabIndents(True) self.codeEdit.setAutoIndent(True) self.codeEdit.setBackspaceUnindents(True) self.codeEdit.setTabWidth(4) # indentation guides self.codeEdit.setIndentationGuides(True) # folding margin self.codeEdit.setFolding(QsciScintilla.PlainFoldStyle) self.codeEdit.setMarginWidth(2, 12) # 自动换行 self.codeEdit.setWrapMode(QsciScintilla.WrapWord) def onLinesChanged(self): # 动态设置左边的边距 self.codeEdit.setMarginWidth( 0, self.codeEdit.fontMetrics().width(str(self.codeEdit.lines())) + 5) def _getPsPath(self): # 获取ps的路径 settings = QSettings('FrameworkTools', 'Settings') psPath = settings.value('path', '') if not psPath: # 如果没有找到自己保存的路径则去找系统安装的路径 settings = QSettings( 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Photoshop.exe', QSettings.NativeFormat) psPath = settings.value('.', '') self.pathEdit.setText(psPath) def onNewConnection(self): # 当有新的连接来时 while self._server.hasPendingConnections(): socket = self._server.nextPendingConnection() socket.readyRead.connect(self.onReadyRead) if socket.bytesAvailable() > 0: # 如果已有数据发送过来 self.doRecv(socket) def onReadyRead(self): # 准备接收数据 socket = self.sender() if socket.bytesAvailable() > 0: self.doRecv(socket) def doRecv(self, socket): # 接收数据 try: data = socket.readAll().data().decode() # 对数据解密 data = xxtea.decryptFromBase64(data, '0123456789abcdef') method, args = data.split('|') self.resultEdit.append('被调用函数: {}, 参数: {}'.format(method, args)) try: args = eval(args) except Exception as e: args = [] # 动态执行函数 if hasattr(self, method): getattr(self, method)(socket, *args) except Exception as e: self.resultEdit.append(str(e)) def getCode(self, socket): # 传递参数的函数 args = self.argsEdit.toPlainText().strip() args = xxtea.encryptToBase64(args, '0123456789abcdef') + '\n' print('发送加密数据: ', args) socket.write(args.encode()) socket.flush() def showError(self, _, message): # 显示错误消息 if message: QMessageBox.critical(self, '错误', message) @pyqtSlot() def on_selectButton_clicked(self): # 手动选择路径 path, _ = QFileDialog.getOpenFileName(self, '选择Ps路径', '', 'Photoshop.exe') if path: self.pathEdit.setText(path) settings = QSettings('FrameworkTools', 'Settings') settings.setValue('path', path) settings.sync() @pyqtSlot() def on_runButton_clicked(self): # 运行按钮 code = self.codeEdit.text().strip() if not code: return if not code.startswith('#target'): code = '#target photoshop\n' + code path = tempfile.mktemp('.jsx') open(path, 'wb').write(code.encode('utf-8')) subprocess.call([self.pathEdit.text().strip(), path]) def closeEvent(self, event): if self._server.isListening(): self._server.close() self._server.deleteLater() super(Window, self).closeEvent(event)
class ServerSocket(QObject): """Class defining a socket for the server.""" listening = pyqtSignal(tuple) message_received = pyqtSignal(tuple) client_disconnected = pyqtSignal(str) def __init__(self): """Constructor.""" QObject.__init__(self) self.__tcpServer = QTcpServer() self.__active_sockets = [] self.__tcpServer.newConnection.connect(self.__new_connection) def __new_connection(self): """Method handling a new connection to the server.""" # Connecting the new client client = self.__tcpServer.nextPendingConnection() client.readyRead.connect(self.read_message) client.disconnected.connect(self.__find_disconnected_client) client.disconnected.connect(client.deleteLater) # Not sure self.__active_sockets.append(client) # Hyper important !!! print("A new client connected from address '{}'.".format( client.peerAddress().toString())) def __find_disconnected_client(self): """Method trying to find the disconnected client.""" self.client_disconnected.emit(self.sender().localAddress().toString()) self.__active_sockets = [] for socket in self.__tcpServer.children()[1:]: if int(socket.socketDescriptor()) <= 10000: # Socket is active self.__active_sockets.append(socket) def get_available_ips(self): """Method returning the list of available ips.""" return QNetworkInterface.allAddresses() def listen(self, host=None, port=3000): """Method making the server listening to a given address.""" # If no address was specified: trying to give the best one if host is None: for ip in self.get_available_ips(): if ip != QHostAddress.LocalHost and ip.toIPv4Address(): host = ip if host is None: # No suitable address was found host = QHostAddress.LocalHost # The address was given as string if type(host) == str: host = QHostAddress(host) # For printing... address = "{}:{}".format(host.toString(), port) # Launching server if not self.__tcpServer.listen(host, port): self.__tcpServer.close() print("Unable to listen on address '{}': {}.".format( address, self.__tcpServer.errorString())) raise ConnectionError(self.__tcpServer.errorString()) else: print("Server is listening on address '{}'.".format(address)) self.listening.emit((host, port)) def read_message(self): """Method handling the messages.""" for socket in self.__active_sockets: instr = socket.readAll() message = str(instr, encoding="utf8") if message: self.message_received.emit((message, socket)) def __send_msg(self, message, socket): """Method effectively sending a message.""" socket.write(bytes(message, encoding="utf8")) socket.flush() def send_message(self, message, recipient=None): """Method used to send a message to the clients.""" if recipient: # A single recipient was provided if type(recipient) is not str: # Given socket directly self.__send_msg(message, recipient) else: # Given ip address for socket in self.__active_sockets: if socket.peerAddress().toString() == recipient: self.__send_msg(message, socket) else: # Send for everyone for socket in self.__active_sockets: self.__send_msg(message, socket) # block = QByteArray() # out = QDataStream(block, QIODevice.ReadWrite) # out.setVersion(QDataStream.Qt_5_0) # out.writeUInt16(0) # message = bytes(message, encoding="utf8") # out.writeString(message) # out.device().seek(0) # out.writeUInt16(block.size() - 2) # socket.write(block) def get_address(self): """Method returning the adress.""" return self.__tcpServer.serverAddress() def get_port(self): """Method returning the port.""" return self.__tcpServer.serverPort()
class TcpS(QDialog, Ui_TcpServer): """ Class documentation goes here. """ sendFileName = pyqtSignal(str) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(TcpS, self).__init__(parent) self.setupUi(self) self.payloadSize = 64 * 1024 self.totalBytes = 0 self.bytesWritten = 0 self.bytesToWrite = 0 self.theFileName = "" self.fileName = "" self.localFile = QFile() self.outBlock = QByteArray() self.time = QTime() self.initServer() def initServer(self): """ 网络设置初始化 """ self.tcpPort = 7788 self.tcpServer = QTcpServer(self) self.clientConnection = QTcpSocket(self) self.tcpServer.newConnection.connect(self.sendMessage) self.serverStatuslabel.setText("请选择要传送的文件") self.progressBar.reset() self.serverOpenBtn.setEnabled(True) self.serverSendBtn.setEnabled(False) self.tcpServer.close() def refused(self): """ 对端拒绝接收文件 """ self.tcpServer.close() self.serverStatuslabel.setText("对方拒绝接收") def closeEvent(self, event): """ 关闭事件 """ self.on_serverCloseBtn_clicked() def sendMessage(self): """ 发送文件 """ self.serverSendBtn.setEnabled(False) self.clientConnection = self.tcpServer.nextPendingConnection() self.clientConnection.bytesWritten.connect(self.updateClientProgress) self.serverStatuslabel.setText("开始传送文件 {} !".format(self.theFileName)) self.localFile = QFile(self.fileName) if not (self.localFile.open(QFile.ReadOnly)): errorMsg = "无法读取文件 {}:\n {}".format(self.fileName, self.localFile.errorString()) QMessageBox.warning(self, "应用程序", errorMsg) return self.serverCloseBtn.setText("取消") self.totalBytes = self.localFile.size() #单位:字节 sendOut = QDataStream(self.outBlock, QIODevice.WriteOnly) sendOut.setVersion(QDataStream.Qt_5_4) self.time.start() currentFile = self.fileName.split("/")[-1] sendOut.writeInt64(0) sendOut.writeInt64(0) sendOut.writeQString(currentFile) self.totalBytes += self.outBlock.size() sendOut.device().seek(0) sendOut.writeInt64(self.totalBytes) sendOut.writeInt64(self.outBlock.size() - 2) self.bytesToWrite = self.totalBytes - self.clientConnection.write( self.outBlock) self.outBlock.resize(0) def updateClientProgress(self, numBytes): """ 发送进度显示 """ qApp.processEvents() self.bytesWritten += numBytes if self.bytesWritten > 0: self.block = self.localFile.read( min(self.bytesToWrite, self.payloadSize)) self.bytesToWrite -= self.clientConnection.write(self.block) else: self.localFile.close() byteSent = self.bytesWritten / (1024 * 1024) useTime = self.time.elapsed() / 1000 speed = self.bytesWritten / useTime / (1024 * 1024) total = self.totalBytes / (1024 * 1024) left = (total - byteSent) / speed if byteSent < 0.01: byteSent = self.bytesWritten / 1024 speed = self.bytesWritten / useTime / 1024 total = self.totalBytes / 1024 if left > 0: sendInfo = "已发送 {0:.2f}KB({1:.2f}KB/s)\n共{2:.2f}KB 已用时:{3:.1f}秒\n 估计剩余时间:{4:.1f}秒".format( byteSent, speed, total, useTime, left) else: sendInfo = "已发送 {0:.2f}KB({1:.2f}KB/s)\n共{2:.2f}KB 用时:{3:.1f}秒\n".format( byteSent, speed, total, useTime) else: if left > 0: sendInfo = "已发送 {0:.2f}MB({1:.2f}MB/s)\n共{2:.2f}MB 已用时:{3:.1f}秒\n 估计剩余时间:{4:.1f}秒".format( byteSent, speed, total, useTime, left) else: sendInfo = "已发送 {0:.2f}MB({1:.2f}MB/s)\n共{2:.2f}MB 用时:{3:.1f}秒\n".format( byteSent, speed, total, useTime) self.progressBar.setMaximum(total) self.progressBar.setValue(byteSent) if self.bytesWritten == self.totalBytes: self.serverCloseBtn.setText("关闭") self.serverStatuslabel.setText(sendInfo) @pyqtSlot() def on_serverOpenBtn_clicked(self): """ 打开文件 """ self.fileName = QFileDialog.getOpenFileName(self, '打开文件', './')[0] if self.fileName: self.theFileName = self.fileName.split("/")[-1] self.serverStatuslabel.setText("要传送的文件为:{}".format( self.theFileName)) self.serverSendBtn.setEnabled(True) self.serverOpenBtn.setEnabled(False) @pyqtSlot() def on_serverSendBtn_clicked(self): """ 发送文件 """ if not (self.tcpServer.listen(QHostAddress.Any, self.tcpPort)): errorMsg = self.tcpServer.errorString() QMessageBox.warning(self, "错误", "发送失败:\n {}".format(errorMsg)) self.TcpServer.close() return self.serverStatuslabel.setText("等待对方接收... ...") self.serverSendBtn.setEnabled(False) self.sendFileName.emit(self.theFileName) @pyqtSlot() def on_serverCloseBtn_clicked(self): """ 取消或者关闭 """ if self.tcpServer.isListening(): self.tcpServer.close() if self.localFile.isOpen(): self.localFile.close() self.clientConnection.abort() if self.serverCloseBtn.text() == "取消": self.serverCloseBtn.setText("关闭") else: self.close() self.serverOpenBtn.setEnabled(True) self.serverSendBtn.setEnabled(False) self.progressBar.reset() self.totalBytes = 0 self.bytesWritten = 0 self.bytesToWrite = 0 self.serverStatuslabel.setText("请选择要传送的文件")
class Server(QObject): debug = True log = sys.stderr username = '******' password = '******' stopServing = pyqtSlot() def __init__(self, parent=None): super(Server, self).__init__(parent) self.proxy_server = QTcpServer(self) self.proxy_server.listen(QHostAddress.Any, 8000) self.proxy_server.newConnection.connect(self.manage_request) if self.debug: self.log.write('Server running on localhost port %s\n\n' % self.port()) def port(self): return self.proxy_server.serverPort() def addr(self): return self.proxy_server.serverAddress().toString() def stopServing(self): if self.debug: self.log.write('Service is stopping...\n\n') # does not "close" the server, just stop listening... self.proxy_server.close() if self.proxy_server.hasPendingConnections(): socket = self.proxy_server.nextPendingConnection() while socket: socket.abort() socket = self.proxy_server.nextPendingConnection() def manage_request(self): proxy_server = self.sender() # Qt docs says that the caller of nextPendingConnection() # is the parent of the socket socket = proxy_server.nextPendingConnection() socket.readyRead.connect(self.process_request) socket.disconnected.connect(socket.deleteLater) def authorize_request(self, request_data): return True header = QHttpRequestHeader(QString(request_data)) if self.debug: self.log.write(header.toString()) auth = header.value('Proxy-Authorization') if not auth: return False challenge = base64.b64encode(self.username + ':' + self.password) return challenge == str(auth).split()[1] def process_request(self): socket = self.sender() request_data = socket.readAll() if not self.authorize_request(request_data): socket.write('HTTP/1.1 407 Proxy Authentication Required\r\n') if self.debug: self.log.write('407 Proxy Authentication Required\n\n') socket.write('Proxy-Authenticate: Basic realm="test"\r\n') socket.write('\r\n') socket.disconnectFromHost() return else: # remove Proxy-Authorization header start = request_data.indexOf('Proxy-Authorization:') end = request_data.lastIndexOf('\r\n') request_data.remove(start, end) request_data.append('\r\n') pos = request_data.indexOf('\r\n') request_line = request_data.left(pos) request_data.remove(0, pos + 2) entries = request_line.split(' ') method = entries[0] address = entries[1] version = entries[2] port = '80' if address.count(':') > 1: protocol, host, port = address.split(':') else: protocol, host = address.split(':') print('address' + str(address)) #url = QUrl( protocol + host ) url = QUrl.fromEncoded(address) #url.setHost( host ) #url.setPort( int(port) ) if not url.isValid(): if self.debug: self.log.write('Invalid URL: %s\n\n', url) socket.disconnectFromHost() return host = url.host() port = 80 if (url.port() < 0) else url.port() req = url.encodedPath() if url.hasQuery(): req.append('?').append(url.encodedQuery()) request_line = method + ' ' + req + ' ' + version + '\r\n' request_data.prepend(request_line) if self.debug: self.log.write(method + ' ' + address + ' ' + version + '\n\n') key = host + ':' + QString.number(port) proxy_socket = socket.findChild(QTcpSocket, key) if proxy_socket: proxy_socket.setObjectName(key) proxy_socket.setProperty('url', url) proxy_socket.setProperty('request_data', request_data) proxy_socket.write(request_data) else: proxy_socket = QTcpSocket(socket) proxy_socket.setObjectName(key) proxy_socket.setProperty('url', url) proxy_socket.setProperty('request_data', request_data) proxy_socket.connected.connect(self.send_request) proxy_socket.readyRead.connect(self.transfer_data) proxy_socket.disconnected.connect(self.close_connection) proxy_socket.error.connect(self.close_connection) proxy_socket.connectToHost(host, port) def send_request(self): proxy_socket = self.sender() request_data = proxy_socket.property('request_data').toByteArray() proxy_socket.write(request_data) def transfer_data(self): proxy_socket = self.sender() socket = proxy_socket.parent() socket.write(proxy_socket.readAll()) def close_connection(self): proxy_socket = self.sender() if proxy_socket: socket = proxy_socket.parent() if isinstance(socket, QTcpSocket) and socket: socket.disconnectFromHost() if proxy_socket.error() != QTcpSocket.RemoteHostClosedError: url = proxy_socket.property('url').toUrl() error_string = proxy_socket.errorString() if self.debug: self.log.write('Error for %s %s\n\n' % (url, error_string)) proxy_socket.deleteLater()
class TcpServer(QObject): ''' TCP/IP server class. Usage: import sys import tcp from PyQt4.QtCore import QCoreApplication from PyQt4.QtNetwork import QHostAddress def main(): application = QCoreApplication(sys.argv) tcpServer = tcp.TcpServer() def onConnected(descriptor): print("Client [%s] connected. Now total: %s" % (descriptor, len(tcpServer.descriptors()))) tcpServer.write(descriptor, "Hello dude") def onDisconnected(descriptor): print("Client [%s] disconnected. Now total: %s" % (descriptor, len(tcpServer.descriptors()))) def onError(descriptor, error): print("Error [%s]: ", descriptor, error) def onRead(descriptor, data): print("Read from [%s] %s byte(s): %s" % (descriptor, len(data), data)) tcpServer.onConnected = onConnected tcpServer.onDisconnected = onDisconnected tcpServer.onError = onError tcpServer.onRead = onRead tcpServer.listen(QHostAddress.Any, 9000) sys.exit(application.exec_()) if __name__ == "__main__": main() ''' def __init__(self, parent=None): QObject.__init__(self, parent) self.__server = QTcpServer(parent) self.__clients = dict() # All active clients # Callbacks self.__on_connected = None self.__on_disconnected = None self.__on_error = None self.__on_read = None # Signals and slots connections self.__server.newConnection.connect(self.__onNewConnection) # Slots @pyqtSlot() def __onNewConnection(self): ''' The slot is called every time a client connects to the server. The client's socket(QTcpSocket) <socket> and descriptor(int) <descriptor> are defined. The socket is appended to the dictionary of connected clients(key=client's descriptor, value=client's socket). Then, socket's descriptor is passed to __onConnected method. :return: None ''' socket = self.__server.nextPendingConnection() socket.disconnected.connect(self.__onDisconnected) socket.error.connect(self.__onError) socket.readyRead.connect(self.__onReadyRead) descriptor = id(socket) self.__clients[descriptor] = socket self.__onConnected(descriptor) @pyqtSlot() def __onConnected(self, descriptor): ''' The method is called by __onNewConnection every time a client connects to the server. If __on_connected callback is set, it is executed with socket's descriptor(int) <descriptor> argument. :param descriptor(int): a newly connected client's socket descriptor, the socket itself is in self.__clients dictionary(key=client's descriptor, value=client's socket); :return: None ''' if self.__on_connected: self.__on_connected(descriptor) @pyqtSlot() def __onDisconnected(self): ''' The slot is called when the connected client disconnects from the server. The client's socket(QTcpSocket) <socket> and its descriptor(int) <descriptor> are defined, and then the descriptor is passed to __on_disconnected callback. :return: None ''' if self.__on_disconnected: socket = self.sender() descriptor = id(socket) self.__clients.pop(descriptor, None) self.__on_disconnected(descriptor) @pyqtSlot() def __onError(self, socketError): ''' The slot is called when an error(PyQt4.QtNetwork.SocketError) <socketError> occurs. The socket's descriptor(int) <descriptor> with error code(int) <errorCode> and description(str) <errorDescription> are defined and passed to __on_error callback. :param error(QAbstractSocket.SocketError): socket error; :return: None ''' if self.__on_error: socket = self.__server.sender() descriptor = id(socket) errorCode = int(socketError) errorDescription = str(self.__server.errorString()) error = (errorCode, errorDescription) self.__on_error(descriptor, error) @pyqtSlot() def __onReadyRead(self): ''' The slot is called when new data is available for reading from the client. The client's socket descriptor(int) <descriptor> is defined, and all available data(str) <data> is read. Then, the socket descriptor and the data are passed to __on_read callback. :return: None ''' if self.__on_read: socket = self.sender() descriptor = id(socket) data = socket.readAll().data() self.__on_read(descriptor, data) # Setters def __connected(self, callback): self.__on_connected = callback def __disconnected(self, callback): self.__on_disconnected = callback def __error(self, callback): self.__on_error = callback def __read(self, callback): self.__on_read = callback # Methods def isListening(self): ''' Return True if the server is currently listening for incoming connections, otherwise returns False. :return: bool ''' return self.__server.isListening() def listen(self, address=None, port=0): ''' Tell the server to listen for incoming connections on address(QHostAddress) <address> and port(int) <port>. If port is 0, a port is chosen automatically. If address is QHostAddress.Any, the server will listen on all network interfaces. :param address(QHosAddress): address to listen to; :param port(int): the port to listen to; :return: bool, return True on success, otherwise return False ''' self.__server.listen(address, port) def socket(self, descriptor): ''' Return socket(QTcpSocket) with the given descriptor(int) <descriptor> or None if it is not found. :param descriptor(int): socket descriptor; :return: socket(QTcpSocket) or None ''' return self.__clients.get(descriptor, None) def descriptors(self): ''' Return all sockets' descriptors available. :return list of int ''' return sorted(self.__clients.keys()) def write(self, descriptor, data): ''' Write data(str) <data> to the client's socket with the given descriptor(int) <descriptor>. :param descriptor(int): client's socket descriptor; :param data(str), data to be written; :return: int, the number of bytes that were actually written, or -1 if an error occurred ''' socket = self.socket(descriptor) if socket: return socket.write(data) else: return -1 def disconnect(self, descriptor): ''' Disconnect the client socket with the given descriptor(int) <descriptor>. :param descriptor(int): client's socket descriptor; :return: None ''' socket = self.socket(descriptor) if socket: socket.close() def close(self): ''' Close the server. :return: None ''' for socket in self.__clients.values(): socket.close() self.__server.close() # Properties onConnected = property(fset=__connected) onDisconnected = property(fset=__disconnected) onError = property(fset=__error) onRead = property(fset=__read)
def isAvailable(port): server = QTcpServer() result = server.listen(QHostAddress("127.0.0.1"), port) server.close() return result
class Dialog(QDialog): TotalBytes = 50 * 1024 * 1024 PayloadSize = 65536 def __init__(self, parent=None): super(Dialog, self).__init__(parent) self.tcpServer = QTcpServer() self.tcpClient = QTcpSocket() self.bytesToWrite = 0 self.bytesWritten = 0 self.bytesReceived = 0 self.clientProgressBar = QProgressBar() self.clientStatusLabel = QLabel("Client ready") self.serverProgressBar = QProgressBar() self.serverStatusLabel = QLabel("Server ready") self.startButton = QPushButton("&Start") self.quitButton = QPushButton("&Quit") buttonBox = QDialogButtonBox() buttonBox.addButton(self.startButton, QDialogButtonBox.ActionRole) buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole) self.startButton.clicked.connect(self.start) self.quitButton.clicked.connect(self.close) self.tcpServer.newConnection.connect(self.acceptConnection) self.tcpClient.connected.connect(self.startTransfer) self.tcpClient.bytesWritten.connect(self.updateClientProgress) self.tcpClient.error.connect(self.displayError) mainLayout = QVBoxLayout() mainLayout.addWidget(self.clientProgressBar) mainLayout.addWidget(self.clientStatusLabel) mainLayout.addWidget(self.serverProgressBar) mainLayout.addWidget(self.serverStatusLabel) mainLayout.addStretch(1) mainLayout.addSpacing(10) mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) self.setWindowTitle("Loopback") def start(self): self.startButton.setEnabled(False) QApplication.setOverrideCursor(Qt.WaitCursor) self.bytesWritten = 0 self.bytesReceived = 0 while not self.tcpServer.isListening() and not self.tcpServer.listen(): ret = QMessageBox.critical(self, "Loopback", "Unable to start the test: %s." % self.tcpServer.errorString(), QMessageBox.Retry | QMessageBox.Cancel) if ret == QMessageBox.Cancel: return self.serverStatusLabel.setText("Listening") self.clientStatusLabel.setText("Connecting") self.tcpClient.connectToHost(QHostAddress(QHostAddress.LocalHost), self.tcpServer.serverPort()) def acceptConnection(self): self.tcpServerConnection = self.tcpServer.nextPendingConnection() self.tcpServerConnection.readyRead.connect(self.updateServerProgress) self.tcpServerConnection.error.connect(self.displayError) self.serverStatusLabel.setText("Accepted connection") self.tcpServer.close() def startTransfer(self): self.bytesToWrite = Dialog.TotalBytes - self.tcpClient.write(QByteArray(Dialog.PayloadSize, '@')) self.clientStatusLabel.setText("Connected") def updateServerProgress(self): self.bytesReceived += self.tcpServerConnection.bytesAvailable() self.tcpServerConnection.readAll() self.serverProgressBar.setMaximum(Dialog.TotalBytes) self.serverProgressBar.setValue(self.bytesReceived) self.serverStatusLabel.setText("Received %dMB" % (self.bytesReceived / (1024 * 1024))) if self.bytesReceived == Dialog.TotalBytes: self.tcpServerConnection.close() self.startButton.setEnabled(True) QApplication.restoreOverrideCursor() def updateClientProgress(self, numBytes): self.bytesWritten += numBytes if self.bytesToWrite > 0: self.bytesToWrite -= self.tcpClient.write(QByteArray( min(self.bytesToWrite, Dialog.PayloadSize), '@')) self.clientProgressBar.setMaximum(Dialog.TotalBytes) self.clientProgressBar.setValue(self.bytesWritten) self.clientStatusLabel.setText("Sent %dMB" % (self.bytesWritten / (1024 * 1024))) def displayError(self, socketError): if socketError == QTcpSocket.RemoteHostClosedError: return QMessageBox.information(self, "Network error", "The following error occured: %s." % self.tcpClient.errorString()) self.tcpClient.close() self.tcpServer.close() self.clientProgressBar.reset() self.serverProgressBar.reset() self.clientStatusLabel.setText("Client ready") self.serverStatusLabel.setText("Server ready") self.startButton.setEnabled(True) QApplication.restoreOverrideCursor()
class Dialog(QDialog): def __init__(self, parent: QWidget = None) -> None: super().__init__(parent) self.tcpServer = QTcpServer() self.tcpClient = QTcpSocket() self.tcpServerConnection: QTcpSocket = None self.bytesToWrite = 0 self.bytesWritten = 0 self.bytesReceived = 0 self.clientProgressBar = QProgressBar() self.clientStatusLabel = QLabel(self.tr("Client ready")) self.serverProgressBar = QProgressBar() self.serverStatusLabel = QLabel(self.tr("Server ready")) self.startButton = QPushButton(self.tr("&Start")) self.quitButton = QPushButton(self.tr("&Quit")) self.buttonBox = QDialogButtonBox() self.buttonBox.addButton(self.startButton, QDialogButtonBox.ActionRole) self.buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole) self.startButton.clicked.connect(self.start) self.quitButton.clicked.connect(self.close) self.tcpServer.newConnection.connect(self.acceptConnection) self.tcpClient.connected.connect(self.startTransfer) self.tcpClient.bytesWritten.connect(self.updateClientProgress) self.tcpClient.error.connect(self.displayError) mainLayout = QVBoxLayout(self) mainLayout.addWidget(self.clientProgressBar) mainLayout.addWidget(self.clientStatusLabel) mainLayout.addWidget(self.serverProgressBar) mainLayout.addWidget(self.serverStatusLabel) mainLayout.addStretch(1) mainLayout.addSpacing(10) mainLayout.addWidget(self.buttonBox) @pyqtSlot() def start(self): self.startButton.setEnabled(False) QGuiApplication.setOverrideCursor(Qt.WaitCursor) self.bytesWritten = 0 self.bytesReceived = 0 while not self.tcpServer.isListening() and not self.tcpServer.listen(): ret = QMessageBox.critical( self, self.tr("Loopback"), self.tr( "Unable to start the test: %s" % (self.tcpServer.errorString()) ), QMessageBox.Retry | QMessageBox.Cancel, ) if ret == QMessageBox.Cancel: return self.serverStatusLabel.setText(self.tr("Listening")) self.clientStatusLabel.setText(self.tr("Connecting")) self.tcpClient.connectToHost( QHostAddress.LocalHost, self.tcpServer.serverPort() ) @pyqtSlot() def acceptConnection(self): self.tcpServerConnection = self.tcpServer.nextPendingConnection() if not self.tcpServerConnection: self.serverStatusLabel.setText( self.tr("Error: got invalid pending connection!") ) return self.tcpServerConnection.readyRead.connect(self.updateServerProgress) self.tcpServerConnection.error.connect(self.displayError) self.tcpServerConnection.disconnected.connect( self.tcpServerConnection.deleteLater ) self.serverStatusLabel.setText(self.tr("Accepted connection")) self.tcpServer.close() @pyqtSlot() def startTransfer(self): # called when the TCP client connected to the loopback server self.bytesToWrite = TOTAL_BYTES - int( self.tcpClient.write(QByteArray(PAYLOAD_SIZE, "@")) ) self.clientStatusLabel.setText(self.tr("Connected")) @pyqtSlot() def updateServerProgress(self): self.bytesReceived += int(self.tcpServerConnection.bytesAvailable()) self.tcpServerConnection.readAll() self.serverProgressBar.setMaximum(TOTAL_BYTES) self.serverProgressBar.setValue(self.bytesReceived) self.serverStatusLabel.setText( self.tr("Received %dMB" % (self.bytesReceived / (1024 * 1024),)) ) if self.bytesReceived == TOTAL_BYTES: self.tcpServerConnection.close() self.startButton.setEnabled(True) QGuiApplication.restoreOverrideCursor() @pyqtSlot("qint64") def updateClientProgress(self, numBytes): self.bytesWritten += int(numBytes) if self.bytesToWrite > 0 and self.tcpClient.bytesToWrite() <= 4 * PAYLOAD_SIZE: self.bytesToWrite -= self.tcpClient.write( QByteArray(min(self.bytesToWrite, PAYLOAD_SIZE), "@") ) self.clientProgressBar.setMaximum(TOTAL_BYTES) self.clientProgressBar.setValue(self.bytesWritten) self.clientStatusLabel.setText( self.tr("Sent %dMB" % (self.bytesWritten / (1024 * 1024),)) ) @pyqtSlot(QAbstractSocket.SocketError) def displayError(self, socketError): if socketError == QTcpSocket.RemoteHostClosedError: return QMessageBox.information( self, self.tr("Network error"), self.tr( "The following error occurred: %s." % (self.tcpClient.errorString(),) ), ) self.tcpClient.close() self.tcpServer.close() self.clientProgressBar.reset() self.serverProgressBar.reset() self.clientStatusLabel.setText(self.tr("Client ready")) self.serverStatusLabel.setText(self.tr("Server ready")) self.startButton.setEnabled(True) QGuiApplication.restoreOverrideCursor()
class ListenPipe(QObject): sig_data_arrived = pyqtSignal(QByteArray) sig_listening_state = pyqtSignal(bool) def __init__(self, parent=None): super(ListenPipe, self).__init__(parent=parent) self.tcpServer = QTcpServer() self.socketClients = [] self.tcpServer.newConnection.connect(self.on_newconnection) @pyqtSlot() def start_listen(self): print('start_listen:%s %s' % (appsetting.listen_ip, appsetting.listen_port)) self.tcpServer.close() for socketClient in self.socketClients: socketClient.close() socketClient.deleteLater() if appsetting.listen_enable != '0': self.tcpServer.listen(QHostAddress(appsetting.listen_ip), int(appsetting.listen_port)) self.sig_listening_state.emit(self.tcpServer.isListening()) @pyqtSlot() def on_newconnection(self): print('on_newconnection') socketClient = self.tcpServer.nextPendingConnection() socketClient.readyRead.connect(self.on_readyRead) socketClient.connected.connect(self.on_connected) socketClient.disconnected.connect(self.on_disconnected) socketClient.error.connect(self.on_error) self.socketClients.append(socketClient) print('self.socketClients size=%d' % len(self.socketClients)) @pyqtSlot() def on_readyRead(self): print("%s on_readyRead" % self.__class__.__name__) socketClient = self.sender() data = socketClient.readAll() #print(data) self.sig_data_arrived.emit(data) @pyqtSlot(QByteArray) def on_data_arrived(self, data): print("%s on_data_arrived" % self.__class__.__name__) for socketClient in self.socketClients: socketClient.write(data) @pyqtSlot() def on_connected(self): print('on_connected') @pyqtSlot() def on_disconnected(self): print('on_disconnected') socketClient = self.sender() self.socketClients.remove(socketClient) socketClient.deleteLater() print('self.socketClients size=%d' % len(self.socketClients)) @pyqtSlot() def on_error(self): socketClient = self.sender() print('%s on_error:%s' % (self.__class__.__name__, socketClient.errorString()))