2012-05-04 25 views
24

Tôi có một QTcpSocket và tôi đang đọc thành một vòng lặp. Mỗi lần một gói đầy đủ đã được đọc, hoặc đã có một lỗi, tôi tự kiểm tra tình trạng của ổ cắm bên trong vòng lặp, với:Trạng thái QTcpSocket luôn được kết nối, thậm chí rút dây ethernet

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

Khi tôi thực hiện de chương trình, một khi được kết nối và vòng lặp khởi động, nó luôn luôn in qDebug()<<"Socket status: connected. Looking for packets..."; và sau đó stucks tại waitForReadyRead cho đến khi một số dữ liệu đã sẵn sàng để được đọc.

Vấn đề là việc ngắt kết nối không được phát hiện. Nếu tôi ngắt kết nối từ mạng từ các tùy chọn OS, hoặc thậm chí nếu tôi rút dây ethernet, nó hoạt động giống nhau: Socket state bằng QAbstractSocket::ConnectedStat e, do đó, nó đi vào, nhưng không nhận được bất cứ điều gì tất nhiên.

Tôi cũng đã cố gắng để phát hiện ngắt kết nối kết nối disconnected() tín hiệu (sau khi kết nối nắm tay) đến một chức năng kết nối lại:

// 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(); 
} 

Nhưng tín hiệu không bao giờ emited, bởi vì mã này không bao giờ được thực thi. Đó là hợp lý, vì nó trông giống như trạng thái socket luôn là ConnectedState.

Nếu tôi cắm lại, kết nối sẽ được khôi phục và bắt đầu nhận lại dữ liệu, nhưng tôi muốn phát hiện các ngắt kết nối để hiển thị "Đã ngắt kết nối" tại GUI.

Tại sao QTcpSocket hoạt động theo cách này và cách tôi có thể giải quyết vấn đề này?

EDIT: Tôi đang tạo ổ cắm tại các nhà xây dựng lớp học, và sau đó initialising gọi prepareSocket chức năng:

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

Bạn đã đợi bao lâu bằng cáp chưa cắm? – Mat

+0

Không biết ... 10 giây? Ít hơn là cần thiết cho các ổ cắm POSIX để xác định chúng đã bị ngắt kết nối. Tôi nên đợi bao lâu? –

+0

Thời gian chờ TCP là cách trên đó. Đợi ít nhất vài phút. – Mat

Trả lời

20

Cuối cùng tìm thấy giải pháp trong này Qt forum:

Nếu không có dữ liệu được trao đổi trong một thời gian nhất định, TCP sẽ bắt đầu gửi các phân đoạn còn sống (về cơ bản, các phân đoạn ACK có số xác nhận được đặt thành số thứ tự hiện tại ít hơn một). Người ngang hàng khác rồi trả lời bằng một xác nhận khác. Nếu xác nhận này là không nhận được trong một số lượng nhất định các đoạn dò, kết nối sẽ tự động bị loại bỏ. Vấn đề nhỏ là hạt nhân bắt đầu gửi các phân đoạn liên tục sau 2 giờ kể từ khi kết nối trở thành không hoạt động! Do đó, bạn cần phải thay đổi giá trị này (nếu hệ điều hành của bạn cho phép) hoặc triển khai cơ chế tiếp tục của riêng bạn trong giao thức (như nhiều giao thức, ví dụ: SSH).Linux cho phép bạn thay đổi nó bằng cách sử 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

Đối với bất kỳ ai khác đang cố gắng này: '#include #include #include ' – kwahn

+1

Mã này có ngay sau khi bạn đã tạo QTcpSocket? Và bạn có phải gọi một chức năng khác để thiết lập các tùy chọn này trên socket Qt (hoặc chúng có hiệu lực trên socket Qt ngay lập tức). – TSG

0

thử mẫu của tôi về khách hàng trong 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(); 
}; 

trên cpp:

void Client::readyRead() { 

    // if you need to read the answer of server.. 
    while (this->canReadLine()) { 
    } 
} 

void Client::doConnect() { 
    this->connectToHost(ip_, port_); 
    qDebug() << " INFO : " << QDateTime::currentDateTime() 
      << " : CONNESSIONE..."; 
} 

void Client::connected() { 
    qDebug() << " INFO : " << QDateTime::currentDateTime() << " : CONNESSO a " 
      << ip_ << " e PORTA " << port_; 
    //do stuff if you need 
} 


void Client::sendMessage(const QString& message) { 
    this->write(message.toUtf8()); 
    this->write("\n"); //every message ends with a new line 
} 

tôi bỏ qua một số mã làm kết nối bộ tạo và vị trí .. thử với điều này và nếu nó doesn t làm việc có thể có cái gì đó sai về phía máy chủ ..

+0

Tôi chắc chắn điều này hoạt động, đó là nhiều hơn hoặc ít hơn những gì tôi có. Nhưng kết nối đã hoạt động, những gì tôi muốn là để phát hiện ngắt kết nối, và điều đó cũng không xảy ra ở mẫu của bạn.Bên cạnh đó, phía máy chủ là OK vì nó đã làm việc với các ổ cắm POSIX –

+0

để bạn sử dụng 'void QAbstractSocket :: disconnectFromHost()' ở đâu đó? –

10

Tôi đã phải đối mặt với vấn đề tương tự với một ứng dụng client QT. Về cơ bản tôi xử lý nó với Timers, tín hiệu và khe. Khi ứng dụng khởi động, nó bắt đầu một checkConnectionTimer 4 giây. Mỗi 4 giây hẹn giờ hết hạn, nếu trạng thái socket của client! = AbstractSocket :: Connected or Connecting, nó cố gắng kết nối với clientSocket-> connectToHost

Khi tín hiệu socket "connected()", nó khởi động máy chủ thứ 5 nhịp tim. Máy chủ sẽ gửi thông báo nhịp tim một byte cho khách hàng của mình sau mỗi 4 giây. Khi tôi nhận được nhịp tim (hoặc bất kỳ loại tin nhắn nào được báo hiệu bởi readyRead()), tôi khởi động lại bộ hẹn giờ nhịp tim. Vì vậy, nếu bộ đếm thời gian nhịp tim có thời gian chờ, tôi giả định kết nối bị ngắt và gọi số clientSocket->disconnectFromHost();

Điều này hoạt động rất tốt cho tất cả các loại ngắt kết nối khác nhau trên máy chủ, duyên dáng hoặc cách khác (cáp yanking). Có, nó đòi hỏi loại nhịp tim tùy chỉnh của công cụ, nhưng vào cuối ngày nó là giải pháp nhanh nhất và cầm tay nhất.

Tôi đã không quan tâm đến việc đặt thời gian chờ KEEPALIVE trong hạt nhân. Bằng cách này nó di động hơn. Trong constructor:

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

Tôi đã thử tất cả các câu trả lời trên trang. Đây là người duy nhất làm việc cho tôi. – kwahn

+0

nếu mạng bị quá tải và các gói (nhịp tim) bị trì hoãn như 2 hoặc 3 sencond, và sau đó đến? – Xsmael

1

thử này kết nối khe cắm tín hiệu:

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

tại thực hiện khe:

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

Tôi có cùng một vấn đề, nhưng thay vì vấn đề của bạn (luôn luôn kết nối), tôi có sự chậm trễ 4-5 giây để nhận tín hiệu ngắt kết nối, sau khi rút dây ethernet.

Giải pháp tìm kiếm vẫn còn, hãy đăng câu trả lời nếu tìm.

+3

Đối với những người khác đọc, điều này không hoạt động. onStateChanged() không được gọi khi dây được rút phích cắm. – kwahn

Các vấn đề liên quan