Rom*_*dgz 25 c++ qt state network-programming qtcpsocket
我有一个QTcpSocket,我正在读一个循环.每次读取完整数据包或出现错误时,我都会手动检查循环内套接字的状态:
while(true){
if(socket->state()==QAbstractSocket::ConnectedState){
qDebug()<<"Socket status: connected. Looking for packets...";
if(socket->waitForReadyRead(2000)){
//...
}
Run Code Online (Sandbox Code Playgroud)
当我执行de program时,一旦连接并且循环开始,它总是打印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();
}
Run Code Online (Sandbox Code Playgroud)
但是信号永远不会被发出,因为这段代码永远不会被执行.这是合乎逻辑的,因为看起来套接字状态总是如此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;
}
Run Code Online (Sandbox Code Playgroud)
Rom*_*dgz 20
终于在这个Qt论坛找到了解决方案:
如果在一定时间内没有交换数据,TCP将开始发送保持活动段(基本上,确认号设置为当前序列号减去1的ACK段).然后另一个同行回复另一个确认.如果在一定数量的探测段内未收到此确认,则会自动断开连接.小问题是内核在连接空闲后的2小时后开始发送保持活动段!因此,您需要更改此值(如果您的操作系统允许)或在您的协议中实现您自己的保持活动机制(就像许多协议一样,例如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));
Run Code Online (Sandbox Code Playgroud)
Mat*_*own 11
我一直面临着QT客户端应用程序的类似问题.基本上我用定时器,信号和插槽处理它.当应用程序启动时,它会启动4秒checkConnectionTimer.定时器到期每4秒,如果客户端套接字状态!= AbstractSocket :: Connected或Connecting,它尝试连接clientSocket-> connectToHost
当套接字信号"connected()"时,它启动一个5秒的服务器心跳计时器.服务器应每4秒向其客户端发送一个单字节心跳消息.当我获得心跳(或由readyRead()发出的任何类型的消息)时,我重新启动心跳计时器.因此,如果心跳计时器有超时,我认为连接已关闭并且它会调用clientSocket->disconnectFromHost ();
这适用于服务器上所有不同类型的断开连接,优雅或其他(yanking电缆).是的,它需要自定义心跳类型的东西,但在一天结束时它是最快,最便携的解决方案.
我并不热衷于在内核中设置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();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
24194 次 |
| 最近记录: |