2016-09-20 2 views
1

У меня есть сервер и клиент, которым нужно поговорить двунаправленно, но проблема в том, что клиент ждет серверных данных, он не может быть закрыт, я вызвал socket.shutdown() на клиенте closeEvent, но приложение не уходит, это просто там висит. Каков правильный способ сделать это? Благодаря!Как закрыть блокирующий сокет, прослушивающий поток в цикле while?

см. Проблему demo screencast here, когда я закрываю окно client.py, процесс не завершается, потому что вызов recv в client.py блокируется?

Я пробовал здесь предложение 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)

 Смежные вопросы

  • Нет связанных вопросов^_^