2012-05-04 47 views
24

我有一個QTcpSocket,我正在讀入一個循環。每一個完整的數據包已經被閱讀,或出現了一個錯誤的時間,我手動檢查插座的狀態內循環,用:QTcpSocket狀態總是連接,甚至拔出以太網線

while(true){ 
    if(socket->state()==QAbstractSocket::ConnectedState){ 
     qDebug()<<"Socket status: connected. Looking for packets..."; 
     if(socket->waitForReadyRead(2000)){ 
     //... 
    } 

當我執行德程序,一旦連接,並在循環開始,它總是打印qDebug()<<"Socket status: connected. Looking for packets...";然後在waitForReadyRead找到一些數據準備好讀取。

問題是未檢測到斷開連接。如果我從操作系統選項中斷開與網絡的連接,或者即使拔出以太網線,它的行爲也是一樣的:套接字狀態等於QAbstractSocket::ConnectedState,因此它繼續,但是沒有收到任何東西。

我也試圖檢測連接disconnected()信號(拳頭連接之後)斷開到重新連接函數:

// Detect disconnection in order to reconnect 
    connect(socket, SIGNAL(disconnected()), this, SLOT(reconnect())); 

void MyClass::reconnect(){ 
    qDebug()<<"Signal DISCONNECTED emitted. Now trying to reconnect"; 
    panelGUI->mostrarValueOffline(); 
    socket->close(); 
    prepareSocket((Global::directionIPSerialServer).toLocal8Bit().data(), 8008, socket); 
    qDebug()<<"Reconnected? Status: "<<socket->state(); 
} 

但信號也決不emited,因爲從不執行該代碼。這是合乎邏輯的,因爲它看起來像套接字狀態總是ConnectedState

如果我再次插入,連接被恢復並開始再次接收數據,但我確實想要檢測斷開連接以在GUI顯示「斷開連接」。

爲什麼QTcpSocket的行爲如此,我該如何解決這個問題?

編輯:我在類的構造函數創建套接字,然後調用初始化函數prepareSocket:

socket = new QTcpSocket(); 
socket->moveToThread(this); 

bool prepareSocket(QString address, int port, QTcpSocket *socket) { 
    socket->connectToHost(address, port); 
    if(!socket->waitForConnected(2000)){ 
     qDebug()<<"Error creating socket: "<<socket->errorString(); 
     sleep(1); 
     return false; 
    } 
    return true; 
} 
+2

多久你與電纜拔出等待? – Mat

+0

不知道... 10秒?對於POSIX套接字來說,確定它們已斷開連接是非常必要的。我應該等多少? –

+0

TCP超時是遠遠高於此。至少等幾分鐘。 – Mat

回答

20

終於找到這個Qt forum解決方案:

如果沒有數據一定同時交換,TCP會開始發送 保持活動段(基本上,確認段號爲 的數字設置爲當前序列號減1)。其他同行 然後用另一個確認答覆。如果此確認爲 未在一定數量的探針段內收到,則連接 會自動丟棄。小問題是內核從連接 變爲空閒時開始 發送保持活動段2小時後!因此,您需要更改此值(如果您的OS 允許)或者在您的 協議中實現您自己的保活機制(如許多協議所做的,例如SSH)。Linux允許你使用setsockopt的它 變化:

int enableKeepAlive = 1; 
int fd = socket->socketDescriptor(); 
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enableKeepAlive, sizeof(enableKeepAlive)); 

int maxIdle = 10; /* seconds */ 
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle)); 

int count = 3; // send up to 3 keepalive packets out, then disconnect if no response 
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &count, sizeof(count)); 

int interval = 2; // send a keepalive packet out every 2 seconds (after the 5 second idle period) 
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); 
+4

對於其他人試用此方法: '#include #include #include ' – kwahn

+1

這段代碼是否在創建與QTcpSocket?你是否需要調用另一個函數來在Qt的套接字上設置這些選項(或者它們是否立即在Qt套接字上生效)。 – TSG

0

試試我的客戶的模板,在Qt的:

class Client: public QTcpSocket { 
    Q_OBJECT 
public: 
    Client(const QHostAddress&, int port, QObject* parent= 0); 
    ~Client(); 
    void Client::sendMessage(const QString&); 
private slots: 
    void readyRead(); 
    void connected(); 
public slots: 
    void doConnect(); 
}; 

上CPP:

​​

我省略了一些代碼作爲構造函數和插槽連接.. 試着用這一點,如果它不將不起作用,也許有一些錯誤在服務器端..

+0

我確定這是有效的,這或多或少是我所擁有的。但是連接已經起作用了,我想要的是檢測斷開連接,這也不會發生在你的模板上。此外,服務器端是好的,因爲它使用POSIX插座 –

+0

你使用'void QAbstractSocket :: disconnectFromHost()'某處? –

10

我一直面臨着類似的問題與QT客戶端應用程序。基本上我用定時器,信號和插槽來處理它。當應用程序啓動時,它啓動一個4秒的checkConnectionTimer。如果客戶端套接字狀態爲每秒4秒,如果客戶端套接字狀態爲!= AbstractSocket :: Connected或Connecting,它嘗試連接到客戶端套接字 - > connectToHost

當套接字信號「connected()」時,它啓動一個5秒服務器心跳計時器。服務器應每4秒向其客戶端發送一個字節的心跳消息。當我收到心跳(或由readyRead())發送任何類型的消息時,我重新啓動心跳定時器。因此,如果心跳計時器超時,我假設連接斷開,並且它呼叫clientSocket->disconnectFromHost();

對於服務器上的所有不同類型的斷開連接,優雅或其他方式(絞線電纜)都可以很好地工作。是的,它需要定製心跳類型的東西,但在一天結束時,它是最快和最便攜的解決方案。

我並不熱衷於在內核設置KEEPALIVE超時。這種方式更便攜。 在構造函數中:

connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readMessage())); 
connect(clientSocket, SIGNAL(connected()), this, SLOT(socketConnected())); 
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); 
connect(heartbeatTimer, SIGNAL(timeout()), this, SLOT(serverTimeout())); 
... 
// Other Methods 

void NetworkClient::checkConnection(){ 
    if (clientSocket->state() != QAbstractSocket::ConnectedState && 
      clientSocket->state() != QAbstractSocket::ConnectingState){ 
     connectSocketToHost(clientSocket, hostAddress, port); 
    } 
} 

void NetworkClient::readMessage() 
{ 
    // Restart the timer by calling start. 
    heartbeatTimer->start(5000); 
    //Read the data from the socket 
    ... 
} 

void NetworkClient::socketConnected(){ 
    heartbeatTimer->start(5000); 
} 

void NetworkClient::socketDisconnected(){ 
    prioResponseTimer->stop(); 
} 

void NetworkClient::serverTimeout() { 
    clientSocket->disconnectFromHost(); 
} 
+0

我嘗試了頁面上的所有答案。這是唯一爲我工作的人。 – kwahn

+0

如果網絡過載並且(心跳)數據包延遲了2或3秒,然後到達,該怎麼辦? – Xsmael

1

試試這個信號槽連接:

connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));

在插槽實現:

void TCPWorker::onStateChanged(QAbstractSocket::SocketState socketState){ 
qDebug()<< "|GSTCPWorkerThread::onStateChanged|"<<socketState; 
...} 

我有同樣的問題,而是你的問題(總是連接),我有延遲4-5秒接收斷開信號,拔掉以太網線後。

還在尋找解決方案,後期的答案,如果找到。

+3

對於其他人閱讀,這是行不通的。拔下電線時onStateChanged()不會被調用。 – kwahn