2016-09-20 51 views
1

我有一臺服務器和客戶端,它需要雙向交談,但是當客戶在等待服務器數據的問題是,它不能被關閉,我在客戶端closeEvent稱爲socket.shutdown(),但應用不只是退出,它掛在那裏。什麼是正確的做法?謝謝!如何關閉阻塞套接字在while循環線程聽嗎?

看到問題演示screencast here,當我關閉client.py窗口時,進程沒有終止,是不是因爲client.py中的recv調用被阻塞?

我在這裏嘗試了建議how to close a blocking socket while it is waiting to receive data?,但它不工作。

server.py

import os 
import sys 
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 
from PyQt4 import uic 

# enable ctrl-c to kill the app 
import signal 
signal.signal(signal.SIGINT, signal.SIG_DFL) 

import mysocket 
import random 


class MyWindow(QDialog): 

    def __init__(self, parent=None): 
     super(MyWindow, self).__init__(parent) 
     layout = QVBoxLayout(self) 
     button = QPushButton('start') 
     button2 = QPushButton('send rand int') 
     layout.addWidget(button) 
     layout.addWidget(button2) 
     self.setLayout(layout) 
     self.resize(200, 40) 

     button.clicked.connect(self.start_server) 
     button2.clicked.connect(self.send_num) 

     self.start_server() 

    def send_num(self, *args): 
     rand_num = random.randint(1, 10) 
     self.socket.send(str(rand_num)) 

    def start_server(self): 
     self.socket = mysocket.SocketServer(port=5000) 
     self.socket.start() 
     print 'socket server started' 


def main(): 
    app = QApplication(sys.argv) 
    win = MyWindow() 
    win.show() 
    sys.exit(app.exec_()) 

if __name__ == "__main__": 
    main() 

mysocket.py

# enable ctrl-c to kill the app 
import signal 
signal.signal(signal.SIGINT, signal.SIG_DFL) 

import socket 
import threading 

# enable ctrl-c to kill the app 
import signal 
signal.signal(signal.SIGINT, signal.SIG_DFL) 


class SocketServer(object): 

    def __init__(self, port=0): 
     self.host = 'localhost' 
     self.port = port 
     self.bufsize = 4096 
     self.backlog = 5 
     self.separator = '<>' 
     self.clients = [] 

    def listen(self): 
     while True: 
      client, address = self.socket.accept() 
      # client.settimeout(60) 
      self.clients.append(client) 
      threading.Thread(
       target=self.server, args=(client, address)).start() 

    def start(self): 
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     self.socket.bind((self.host, self.port)) 
     self.socket.listen(self.backlog) 

     threading.Thread(target=self.listen).start() 

    def server(self, client, address): 
     data = client.recv(self.bufsize) 
     while True: 

      if self.separator in data: 
       data_split = data.split(self.separator) 
       cmds = data_split[:-1] 

       # execute cmds in threads 
       self.process_cmds(cmds) 

       data = data_split[-1] 

      data += client.recv(self.bufsize) 

    def process_cmds(self, cmds): 

     for cmd in cmds: 
      print 'executing: %s' % cmd 

    def send(self, data): 
     for client in self.clients: 
      try: 
       client.send(data) 
      except: 
       # self.clients.pop(client) 
       pass 

client.py

import os 
import sys 
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 
from PyQt4 import uic 

import socket 
import threading 

# enable ctrl-c to kill the app 
import signal 
signal.signal(signal.SIGINT, signal.SIG_DFL) 


class MyWindow(QDialog): 

    def __init__(self, parent=None): 
     super(MyWindow, self).__init__(parent) 
     layout = QVBoxLayout(self) 
     button = QPushButton('connect') 

     for i in range(5): 
      cmd_button = QPushButton('cmd - %s' % i) 
      layout.addWidget(cmd_button) 
      cmd_button.clicked.connect(lambda _, i=i: self.send_cmd(i)) 

     layout.addWidget(button) 
     self.setLayout(layout) 
     self.resize(200, 40) 

     button.clicked.connect(self.connect_server) 

     self.connect_server() 

    def listen(self): 
     while True: 
      data = self.socket.recv(1024) 
      if data: 
       print 'received:', data 

      print 'executed while' 

    def connect_server(self): 
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.socket.connect(('localhost', 5000)) 
     print 'socket connected' 
     threading.Thread(target=self.listen).start() 

    def send_cmd(self, i): 
     cmd = 'cmd - %s<>' % i 
     print 'sending : %s' % cmd 
     self.socket.send(cmd) 

    def closeEvent(self, e): 
     # s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     # s.connect(('localhost', 5000)) 
     # self.socket.close() 
     self.socket.shutdown(socket.SHUT_WR) 

     super(MyWindow, self).closeEvent(e) 


def main(): 
    app = QApplication(sys.argv) 
    win = MyWindow() 
    win.show() 
    sys.exit(app.exec_()) 

if __name__ == "__main__": 
    main() 
+0

Qt擁有各種高級別套接字API - 爲什麼不使用它們呢? –

回答

0

你應該讀取數據之前使用select功能從插座:

在程序頂部:

import select 

而改性listen功能:

def listen(self): 
    while self.isVisible(self): 
     readable,_,_ = select.select([self.socket], [], [], 5) 
     if (readable): 
      data = self.socket.recv(1024) 
      print 'received:', data 
     else: 
      print 'client send nothing in 5 seconds, or socket has been closed' 

     print 'executed while' 

參見:https://stackoverflow.com/a/38520949/1212012(同樣的問題,但在未GUI程序)