[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)

[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)


Unplugging ethernet (물리적 문제로 인터넷 끊김)

Qt에서 소켓을 사용하다 보면 끊어진 경우를 확인해야 할 상황이 발생합니다. 굳이 Qt가 아니더라도 언제나 서비스 제공자는 확인해야 합니다만, Qt를 이용한 프로그래밍을 할 땐, 단순히 플러그가 뽑힌 경우를 체크하기엔 까다롭습니다.


왜냐면 QAbstractSocket 클래스가 제공하는 ConnectedState는 연결된 이후, 물리적인 플러그 뽑힘을 확인하지 못합니다.


슬프게도, Qt가 제공하는 그 어떤 소켓 클래스들도 물리적인 플러그 Unconnected를 확인하질 못합니다. 그래서 조금 돌아가는 방법을 사용해야 합니다.

Simple Main Code

우선, 간단한 Socket Program을 만들어 봅니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
main.h
 
#include <QProcess>
#include <QTcpSocket>
 
QProcess ping_process_;
QStringList ping_params_;
 
private slots:
    void HandleConnected();
    void HandleDisconnected();
    void HandleReadyRead();
    void HandleStateChange(QAbstractSocket::SocketState state);
cs


1
2
3
4
5
6
7
8
9
main.cpp
 
tcp_socket_ = new QTcpSocket(this);
 
connect(tcp_socket_, SIGNAL(connected()), this, SLOT(HandleConnected()));
connect(tcp_socket_, SIGNAL(readyRead()), this, SLOT(HandleReadyRead()));
connect(tcp_socket_, SIGNAL(disconnected()), this, SLOT(HandleDisconnected()));
connect(tcp_socket_, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this,
SLOT(HandleStateChange(QAbstractSocket::SocketState)));
cs


1
ping_params_ << "-c" << "1" << server_ip_;
cs


아주 간단한 프로그램입니다.


Qt QTcpSocket 네트워크[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)


4개의 slot은 연결, 끊김, 읽기, 상태 변화를 의미합니다. 메인 코드의 마지막엔 연결이 잘 이루어졌는지 ping을 날려보는 간단한 코드도 들어있습니다.


Ping and QProcess

Ping 테스트를 할 땐, http://를 넣으면 오류가 발생하고, Window와 Linux에선 서로 다른 인자를 사용하니 그것도 확인해 줘야 합니다.


이를 확인하지 않으면 아래와 유사한 에러 메시지가 뜰 겁니다.


error, message


Try removing "http://" in line number

Ping expects an IP or an hostname, "http://" isn't part of the hostname.

cout<<"\n\nexitCode,exitStatus=="<<exitCode<<","<<exitStatus


ping 코드를 작성할 아래처럼 http://를 제거하고 난 뒤에 시도하셔야 합니다.

그러면 아래와 같은 코드를 작성하실 수 있습니다.


아주 간단하게 말이죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#if defined(WIN32)
   QString parameter = "-n 1";
#else
   QString parameter = "-c 1";
#endif
 
int exitCode = QProcess::execute("ping", QStringList() << parameter << "1.1.1.11");
 
if (exitCode==0
{
    // it's alive
else 
{
    // it's dead
}
 
.....
params << "-c" << "1" << "www.google.com";
cs


ConnectToServer function


1
2
3
4
5
6
7
void TcpClient::ConnectToServer()
{
    if (tcp_socket_->state() != QAbstractSocket::ConnectedState &&
            tcp_socket_->state() != QAbstractSocket::ConnectingState) {
        tcp_socket_->connectToHost(server_ip_, server_port_);
    }
}
cs


위에서 선언만 했던 ConnecToServer 함수를 정의합니다.


socket 객체가 아직도 연결되지 않은 상태라면 연결을 시도합니다. 이것도 아주 간단하죠?


Qt 네트워크 프로그래밍[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)


HandleConnected function


1
2
3
4
5
6
7
8
void TcpClient::HandleConnected()
{
    qWarning() << "connect Complete";
 
    bla~ bla~ bla~
 
    emit ConnectionComplete();
}
cs


이어서 HandleConnected 함수도 정의합니다. 이름 그대로 연결이 되면 구동되는 함수입니다.


내부의 코드는 각자 입맛에 맞춰 구성하면 됩니다.

StateChange property

StateChange 속성은 아래와 같습니다.


Constant

 Value

 Description

 QAbstractSocket::UnconnectedState

0

 The socket is not connected.

 QAbstractSocket::HostLookupState

1

 The socket is performing a host name lookup.

 QAbstractSocket::ConnectingState

2

 The socket has started establishing a connection.

 QAbstractSocket::ConnectedState

3

 A connection is established.

 QAbstractSocket::BoundState

4

 The socket is bound to an address and port

 (for servers).

 QAbstractSocket::ClosingState

5

 The socket is about to close

 (data may still be waiting to be written).

 QAbstractSocket::ListeningState

6

 For internal use only.


일반적으로 주로 사용하는 Constant는 굵게 표시된 위의 두 개입니다.


아무튼, 저 표를 통해서 유추할 수 있는 아주 간단한 사실은,


1
2
3
4
5
// Normal
QAbstractSocket::ConnectedState
 
// Abnormal
QAbstractSocket::UnconnectedState
cs


이겁니다. 그래서 아래와 같은 한 줄로 코드에서 분기가 가능합니다.


1
tcp_socket_->state() == QAbstractSocket::UnconnectedState
cs


Qt QTcpSocket 소켓 프로그래밍[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)


아래엔 실제 구현할 수 있는 소켓 프로그램의 전체 코드입니다. (위와 이어지는 내용)


1
2
3
4
5
6
7
void TcpClient::HandleStateChange(QAbstractSocket::SocketState state)
{
    if (tcp_socket_->state() == QAbstractSocket::UnconnectedState)
        HandleDisconnected();
 
    qWarning() << "State Changed : " << state;
}
cs


1
2
3
4
5
6
7
8
9
10
11
void TcpClient::HandleDisconnected()
{
    if (timer_connecte_->isActive() == false) {
        tcp_socket_->close();
   bla~ bla~
    }
 
    emit ConnectionError();
 
    qWarning() << "HandleDisconnected";
}
cs


1
2
3
4
5
void TcpClient::HandleReadyRead()
{
    recv_data_ = tcp_socket_->readAll();
    CheckPacketID();
}
cs


자, 이제 몇 가지 케이스를 들어 Qt의 소켓이 여러 끊김 현상을 감지할 수 있는지 확인합니다.


물론 위에 나온 코드를 이용해 테스트를 진행할 겁니다. (위의 코드만 복사하면 프로그램 하나가 만들어지니깐요)

Case1 : Unplugged ethernet

QProcess에서 과연 ethernet의 끊김을 확인할 수 있을까?


output 메시지와 error 메시지를 확인하면 알 수 있습니다.


output message


정상적인 상황 : 아래 메시지


"PING 192.168.1.17 (192.168.1.17) 56(84) bytes of data.

64 bytes from 192.168.1.17: icmp seq=1 ttl=128 time=0.830ms


--- 192.168.1.17 ping statistics ---

1 packets transmitted, 1 received, 0% packet loss, time 0ms

rtt min/avg/max/mdev = 0.830/0.830/0.830/0.000 ms"


비정상적인 상황 : 없음


error message


정상적인 상황 : 없음

비정상적인 상황 : 아래 메시지


connect: Network is unreachable


분명히 error 메시지를 통해서 이더넷의 연결 끊김은 확실히 확인 할 수 있습니다.


Case2 : Power off the target (thing..)

어떤 게 연결된진 모르겠지만, 하여간에 연결된 장치 쪽의 전원이 꺼진 상태입니다.

PLC가 될 수도 있고, 작은 임베디드 보드일 수도 있고, 데스크톱일 수도 있고, 경우는 많습니다.


output message


정상적인 상황 : 아래 메시지

"PING 192.168.1.17 (192.168.1.17) 56(84) bytes of data.

64 bytes from 192.168.1.17: icmp seq=1 ttl=128 time=0.830ms


--- 192.168.1.17 ping statistics ---

1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.830/0.830/0.830/0.000 ms"


비정상적인 상황 : 없음


error message


정상적인 상황 : 아래 메시지


QProcess: Destroyed while process ("ping") is still running.


비정상적인 상황 : 없음


ping을 통해서 연결 끊김이 확인됩니다. Case 1에선 ethernet이 끊어짐을 확인했으나 이번엔 그러질 못했습니다. 오직 ping 메시지를 통해서만 알 수 있습니다.


Qt 통신 네트워크 QTcpSocket[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)


Solution


단순히 StateChange 속성만 가지고선 언플러그를 확인할 수가 없습니다. 그래서 Case2와 같이 ping을 전달하는 방법으로 언플러그를 확인해야 합니다.


Ping을 다루는 소스는 위의 소스처럼 아주 간단합니다.


1
2
3
4
5
6
QProcess p;
p.start( /* whatever your command is, see the doc for param types */ );
p.waitForFinished(-1);
 
QString p_stdout = p.readAllStandardOutput();
QString p_stderr = p.readAllStandardError();
cs


원형은 위와 같은데 실제 응용하기 위해선 뭔가 변화를 줘야 합니다.


저는 아래처럼 소스를 구성해 메인 클래스에 추가했습니다.


1
2
3
4
5
6
7
8
9
/** ping 테스트 */
ping_process_.start("ping", ping_params_, QIODevice::ReadOnly);
ping_process_.waitForFinished(kInterval1500);
 
QString out = ping_process_.readAllStandardOutput();
QString err = ping_process_.readAllStandardError();
 
if (out.compare(""== 0 && err.compare(""== 0)
    HandleDisconnected();
cs


넣고 싶은 곳에 적당히 넣어주면 되는 소스입니다.


그리고 아래와 같은 소스도 구동해 볼 수 있습니다.

(출처 - 간단한 ping 명령어 사용하기(QProcess::execute) [클릭])


그렇지만 이 코드는 프로세스 자체가 아예 wait 상태로 진입해 버리는 문제가 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <qapplication>
#include <qprocess>
#include <qtgui>
 
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QMessageBox *box = new QMessageBox;
    int res = -1;
    QStringList list;
 
    list << "66.249.89.99";
 
    res = QProcess::execute("ping", list);
 
    if(res == 0)
    {
        box->setText("ping success");
    }
    else
    {
        box->setText("ping failure");
    }
 
    box->show();
 
    return app.exec();
}
</qtgui></qprocess></qapplication>
cs


위에서 살펴봤지만, 플러그 뽑힘 증상을 기본 QTcpSocket으로는 확인하질 못합니다.


부득이하게 ping을 이용해 확인할 순 있는데 이것도 임시방편에 지나지 않기에, Qt에선 이 부분을 반영하는 형태로 Disconnected 속성을 변경해주는 게 좋다고 봅니다.


마지막으로, 아래는 진행하면서 참고했던 사이트들입니다.


참조 사이트

1. Ping and QProcess (exitcode() question) [클릭]

2. running ping with Qprocess, exit code always 2 if host reachable or not [클릭]

3. QAbstractSocket Class [클릭]

4. Run Linux commands from Qt4 [클릭]

5. How to Detect “Network is Unreachable” via Ping using QProcess? [클릭]

6. 간단한 ping 명령어 사용하기(QProcess::execute) [클릭]

7. QT - TCP/IP 소켓 프로그래밍(클라이언트) [클릭]


== 본문 표 그림 파일 ==


[C++] Qt 인터넷 네트워크 연결 상태 확인 (QTcpSocket)

댓글(0)

Designed by JB FACTORY