如何关闭在while循环中的线程中侦听的阻塞套接字?
Posted
技术标签:
【中文标题】如何关闭在while循环中的线程中侦听的阻塞套接字?【英文标题】:How to close a blocking socket listening in a thread in while loop? 【发布时间】:2017-01-28 01:57:45 【问题描述】:我有一个需要双向通信的服务器和客户端,但问题是当客户端在等待服务器数据时,它无法关闭,我在客户端closeEvent
中调用了socket.shutdown()
,但应用程序没有t退出,它只是挂在那里。正确的做法是什么?谢谢!
看问题demoscreencast 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()
【问题讨论】:
Qt 有各种高级套接字 API - 为什么不使用它们呢? 【参考方案1】:在从套接字读取数据之前,您应该使用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://***.com/a/38520949/1212012(同样的问题,但在非 GUI 程序中)
【讨论】:
以上是关于如何关闭在while循环中的线程中侦听的阻塞套接字?的主要内容,如果未能解决你的问题,请参考以下文章