1
votes

I've got a subclass of QTcpSocket. And problem is : when i firt time connect to server - everything ok, but after socket connected i restart server (python socketServer,just close and start script again) socket disconnecting and tryin to reconnect while server is down, but when i start server again - nothing happened, socket.state() always in ConnectingState.. what is wrong ?

Here example code:

# -*- coding: utf-8 -*-
from PyQt4.QtCore import QVariant,  QTimer, pyqtSignal, QCoreApplication
import sys
from PyQt4.QtNetwork import QTcpSocket
from re import match
import json

MAX_WAIT_LEN  = 8

class UpQSocket(QTcpSocket):
    data_ready = pyqtSignal(unicode)
    def __init__(self):
        QTcpSocket.__init__(self)
        self.wait_len = ''
        self.temp = ''
        self.setSocketOption(QTcpSocket.KeepAliveOption, QVariant(1))
        self.readyRead.connect(self.on_ready_read)
        self.connected.connect(self.on_connected)
        self.disconnected.connect(self.on_disconnect)
        self.error.connect(self.on_error)
        self.data_ready.connect(self.print_command)

    def connectToHost(self, host, port):
        print 'connectToHost'
        self.temp = ''
        self.wait_len = ''
        QTcpSocket.abort(self)
        QTcpSocket.connectToHost(self, host, port)

    def close(self):
        print 'close!'
        self.disconnectFromHost()

    def send(self, data):
        self.writeData('%s|%s' % (len(data), data))

    def on_ready_read(self):
        if self.bytesAvailable():
            data = str(self.readAll())
            while data:
                if not self.wait_len and '|' in data:#new data and new message
                    self.wait_len , data = data.split('|',1)
                    if match('[0-9]+', self.wait_len) and (len(self.wait_len) <= MAX_WAIT_LEN) and data.startswith('{'):#okay, this is normal length
                        self.wait_len = int(self.wait_len)
                        self.temp = data[:self.wait_len]
                        data = data[self.wait_len:]
                    else:#oh, it was crap
                        self.wait_len , self.temp = '',''
                        return
                elif self.wait_len:#okay, not new message, appending
                    tl= int(self.wait_len)-len(self.temp)
                    self.temp+=data[:tl]
                    data=data[tl:]
                elif not self.wait_len and not '|' in data:#crap
                    return
                if self.wait_len and self.wait_len == len(self.temp):#okay, full message
                    self.data_ready.emit(self.temp)
                    self.wait_len , self.temp = '',''
                    if not data:
                        return

    def print_command(self,data):
        print 'data!'

    def get_sstate(self):
        print self.state()

    def on_error(self):
        print 'error', self.errorString()
        self.close()
        self.connectToHost('dev.ulab.ru', 10000)

    def on_disconnect(self):
        print 'disconnected!'

    def on_connected(self):
        print 'connected!'
        self.send(json.dumps(
                {'command' : "operator_insite",
                 'password' : "376c43878878ac04e05946ec1dd7a55f",
                 'login' : "nsandr",
                 'version':unicode("1.2.9")}))

if __name__ == "__main__":
    app = QCoreApplication(sys.argv)
    main_socket = UpQSocket()
    state_timer = QTimer()
    state_timer.setInterval(1000)
    state_timer.timeout.connect(main_socket.get_sstate)
    state_timer.start()
    main_socket.connectToHost('dev.ulab.ru', 10000)
    sys.exit(app.exec_())

Here is output:

    connectToHost
    1
    1
connected!
data!
data!
3
3
3
3
3
error The remote host closed the connection 
close!
disconnected!
connectToHost
2
2
1
I think it tries to establish connection, and waits until timeout, which is big by default. Maybe you can retry connections in loop with checking for QTcpSocket.waitForConnected(smallTiemout).reclosedev
Hm, this worked for me, but only one time and after socket disconnect immediatly..Andrey Baryshnikov
Yes, it was wrong advice. It's somehow related to event system, but I can't figure out how. Anyway, see workaround from my answer. It works for me.reclosedev
So, where is no solution without workaround like your, this looks like bug?Andrey Baryshnikov
Found related bugreport, see last comment: bugreports.qt-project.org/browse/QTBUG-18082 .reclosedev

1 Answers

3
votes

Workaround:

import functools

def on_error(self):
    print 'error', self.errorString()
    QTimer.singleShot(2000, functools.partial(self.connectToHost, 'localhost', 9999))
    # 2000 - your prefered reconnect timeout in ms

Update

There is more correct solution in comments for Qt bugreport QTBUG-18082. Here is Python implementation:

@QtCore.pyqtSlot()
def do_reconnect(self):
    print 'Trying to reconnect'
    self.connectToHost('localhost', 9999)

def on_error(self):
    print 'error', self.errorString()
    QtCore.QMetaObject.invokeMethod(self, 'do_reconnect',  QtCore.Qt.QueuedConnection)

or just:

QTimer.singleShot(0, self.do_reconnect) # or any callable, slot is unnecessary 

which anyway will call QtCore.QMetaObject.invokeMethod with QueuedConnection conection type (source)