본문 바로가기
C++ 200제/코딩 IT 정보

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

by vicddory 2017. 2. 8.

[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개의 Qt 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


위에서 선언만 했던 Qt 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.


비정상적인 상황 : 없음


Qt 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


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


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



그렇지만 이 코드는 프로세스 자체가 아예 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 속성을 변경해주는 게 좋다고 봅니다.


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




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

 written by vicddory

댓글