c++ - simultaneas - ethernet y wifi al mismo tiempo
Estado de QTcpSocket siempre conectado, incluso desconectando el cable de ethernet (4)
Tengo un QTcpSocket y estoy leyendo en un bucle. Cada vez que se lee un paquete completo, o ha habido un error, verifico manualmente el estado del socket dentro del ciclo, con:
while(true){
if(socket->state()==QAbstractSocket::ConnectedState){
qDebug()<<"Socket status: connected. Looking for packets...";
if(socket->waitForReadyRead(2000)){
//...
}
Cuando ejecuto el programa, una vez conectado y el ciclo se inicia, siempre imprime qDebug()<<"Socket status: connected. Looking for packets..."
; y luego sobresale en waitForReadyRead
hasta que algunos datos estén listos para ser leídos.
El problema es que las desconexiones no se detectan. Si me desconecto de la red de las opciones del SO, o incluso si desconecto el cable de ethernet, se comporta de la misma manera: el estado del socket es igual a QAbstractSocket::ConnectedStat
e, por lo que continúa, pero sin recibir nada, por supuesto.
También traté de detectar las desconexiones que conectan la señal disconnected()
(después de la conexión directa) a una función de reconexión:
// 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();
}
Pero la señal nunca se emite, porque este código nunca se ejecuta. Lo cual es lógico, ya que parece que el estado del socket siempre es ConnectedState
.
Si vuelvo a conectar, la conexión se restablece y comienza a recibir datos nuevamente, pero quiero detectar las desconexiones para mostrar "Desconectado" en la GUI.
¿Por qué QTcpSocket se comporta de esta manera y cómo puedo resolver este problema?
EDITAR: estoy creando un socket en el constructor de la clase, y luego inicializando la llamada a la función 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;
}
Finalmente encontré la solución en este foro de Qt :
Si no se intercambian datos durante un cierto tiempo, TCP comenzará a enviar segmentos de permanencia activa (básicamente, segmentos de ACK con el número de acuse de recibo establecido en el número de secuencia actual menos uno). El otro compañero responde con otro reconocimiento. Si este acuse de recibo no se recibe dentro de un cierto número de segmentos de prueba, la conexión se cancela automáticamente. ¡El pequeño problema es que el kernel comienza a enviar segmentos de permanencia activa después de 2 horas desde que la conexión queda inactiva! Por lo tanto, debe cambiar este valor (si su sistema operativo lo permite) o implementar su propio mecanismo de mantener vivo en su protocolo (como hacen muchos protocolos, por ejemplo, SSH). Linux te permite cambiarlo usando 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));
He estado enfrentando problemas similares con una aplicación de cliente de QT. Básicamente lo manejo con temporizadores, señales y ranuras. Cuando la aplicación se inicia, inicia un temporizador de verificación de 4 segundos. ¡Cada 4 segundos el temporizador expira, si el estado del socket del cliente! = AbstractSocket :: Connected o Connecting, intenta conectarse con clientSocket-> connectToHost
Cuando el zócalo señala "conectado ()", inicia un temporizador de latido del servidor de 5 segundos. El servidor debe enviar un mensaje de latido de un byte a sus clientes cada 4 segundos. Cuando obtengo el latido del corazón (o cualquier tipo de mensaje señalado por readyRead ()), reinicio el temporizador de latido. Entonces, si el temporizador de latido del corazón tiene un tiempo de espera, supongo que la conexión está clientSocket->disconnectFromHost ();
y llama a clientSocket->disconnectFromHost ();
Esto funciona muy bien para todos los tipos diferentes de desconexiones en el servidor, elegante o no (cable de extracción). Sí, requiere un tipo de ritmo de latido personalizado, pero al final del día fue la solución más rápida y portátil.
No estaba interesado en establecer tiempos de espera KEEPALIVE en el kernel. De esta manera es más portátil. En el 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();
}
prueba esta conexión de ranura de señal:
connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));
en la implementación de la ranura:
void TCPWorker::onStateChanged(QAbstractSocket::SocketState socketState ){
qDebug()<< "|GSTCPWorkerThread::onStateChanged|"<<socketState;
...}
Tengo el mismo problema, pero en cambio su problema (siempre conectado), tengo un retraso de 4-5 segundos para recibir las señales de desconexión, después de desconectar el cable de ethernet.
Sigue buscando solución, publica respuesta si la encuentras.
prueba mi plantilla de cliente en 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();
};
en 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
}
omití algunos códigos como conexiones de constructor y slots ... intente con esto y, si no funciona, tal vez haya algún error en el servidor ...