QTcpSocket tcp socket client 프로그래밍 예제.
시작
예전에 C Standard library 를 이용해서 TCP/IP 소켓 프로그래밍을 만들었 적이 있었습니다.
그런데 찾으려고 하니 그 코드가 어디갔는지 알수가 없어서 이번엔 블로그를 남깁니다.
QT App 개발 중 순수 QT lib 을 이용해서 기능을 구현한 것입니다.
샘플 코드 첨부는 가장 아랫 쪽에 해 두었습니다.
Base 참고 사이트
https://doc.qt.io/archives/qt-4.8/qt-network-blockingfortuneclient-example.html
참고 코드에서 UI부분만 제외한 코드를 건드려 보았습니다.
client 접속부는 thread를 이용할 것이므로, 2개의 파일을 이용하였습니다.
1. parent Qobject
2. child QThread
우선 .pro 파일에 network lib 를 사용하겠다고 명시를 해야 합니다.
QT += gui network
1. Parent QObject
1-1. Header file
#include <QObject>
#include <QMutex>
#include <QDebug>
#include "client.h"
class network : public QObject
{
Q_OBJECT
public:
explicit network(QObject *parent = nullptr);
private:
void requestNewConnection();
Client client;
QString currentFortune;
QString ip;
quint16 port;
private slots:
void showFortune(const QString &fortune);
void Error(int socketError, const QString &message);
}
1-2. Source File
#include <QMessageBox>
#include "network.h"
network::network(QObject *parent) : QObject(parent)
{
connect(&c, SIGNAL(newFortune(QString)),
this, SLOT(showFortune(QString)));
connect(&c, SIGNAL(error(int,QString)),
this, SLOT(Error(int,QString)));
ip = "192.168.1.2";
port = 6000;
requestNewConnection();
}
void network::requestNewConnection()
{
clinet.requestNewConnection(ip, port);
}
void network::showFortune(const QString &nextFortune)
{
if (nextFortune == currentFortune) {
requestNewConnection();
return;
}
currentFortune = nextFortune;
}
void network::Error(int socketError, const QString &message)
{
switch (socketError) {
case QAbstractSocket::HostNotFoundError:
break;
case QAbstractSocket::ConnectionRefusedError:
break;
default:
break;
}
}
상위 코드 requestNewForture event 발생시 서버 접속을 시도 합니다.
2. Thread
2-1. Header File
#include <QThread>
#include <QMutex>
#include <QtNetwork>
#include <QWaitCondition>
class Client : public QThread
{
Q_OBJECT
public:
client(QObject *parent = 0);
~client();
void requestNewConnection(const QString &hostName, quint16 port);
void run();
signals:
void newFortune(const QString &fortune);
void error(int socketError, const QString &message);
private:
QString hostName;
quint16 port;
QMutex mutex;
QWaitCondition cond;
bool quit;
};
2-2. Source file
#include "Client.h"
Client::Client(QObject *parent)
: QThread(parent), quit(false)
{
}
Client::~Client()
{
mutex.lock();
cond.wakeOne();
quit = true;
mutex.unlock();
wait();
}
void Client::requestNewConnection(const QString &hostName, quint16 port)
{
QMutexLocker locker(&mutex);
this->hostName = hostName;
this->port = port;
if (!isRunning())
start();
else
cond.wakeOne();
}
void Client::run()
{
애드블럭을 해제하시면 source code가 보여요
}
#include "Client.h"
Client::Client(QObject *parent)
: QThread(parent), quit(false)
{
}
Client::~Client()
{
mutex.lock();
cond.wakeOne();
quit = true;
mutex.unlock();
wait();
}
void Client::requestNewConnection(const QString &hostName, quint16 port)
{
QMutexLocker locker(&mutex);
this->hostName = hostName;
this->port = port;
if (!isRunning())
start();
else
cond.wakeOne();
}
void Client::run()
{
mutex.lock();
QString serverName = this->hostName;
quint16 serverPort = this->port;
mutex.unlock();
while (!quit) {
const int Timeout = 20 * 1000;
QTcpSocket socket;
socket.connectToHost(serverName, serverPort);
if (!socket.waitForConnected(Timeout)) {
emit error(socket.error(), socket.errorString());
return;
}
QByteArray data;
data.append("A");
data.append("B");
data.append("C");
data.append("E");
data.append("F");
socket.write(data);
if(socket.waitForBytesWritten(Timeout)) {
emit error(socket.error(), socket.errorString());
return;
}
while (socket.bytesAvailable() < (int)sizeof(quint16)) {
if (!socket.waitForReadyRead(Timeout)) {
emit error(socket.error(), socket.errorString());
return;
}
else
{
QString s_data = QString::fromAscii(socket.readAll());
qDebug() << "recv : " << s_data;
}
}
quint16 blockSize;
QDataStream in(&socket);
in.setVersion(QDataStream::Qt_4_8);
in >> blockSize;
while (socket.bytesAvailable() < blockSize) {
if (!socket.waitForReadyRead(Timeout)) {
emit error(socket.error(), socket.errorString());
return;
}
}
mutex.lock();
QString fortune;
in >> fortune;
emit newFortune(fortune);
cond.wait(&mutex);
serverName = hostName;
serverPort = port;
mutex.unlock();
}
}
코드는 위와 같이 끝났습니다.
그럼 이제 간단한 설명을 하도록 하겠습니다.
이 블로그를 보시는 분들이라면 당연히 위에 코드만 보시더라도 분석이 가능하시겠지만,
QT를 처음하신 분은 도움이 필요 할 수도 있으니 간단히 설명을 해보도록 하겠습니다.
Line : 36 Timeout 을 설정하는 부분입니다. 단위가 ms 이므로 20초를 지정하였습니다. |
QTcpSocket socket; socket.connectToHost(serverName, serverPort);
socket 변수 선언 후 해당 socket을 이용하여 IP, Port로 연결을 시도 합니다. |
if (!socket.waitForConnected(Timeout)) { emit error(socket.error(), socket.errorString()); return; } timeout 시간 까지 대기 후 error signal을 발생합니다. |
QByteArray data; data.append("A"); data.append("B"); data.append("C"); data.append("E"); data.append("F"); socket.write(data);
if(socket.waitForBytesWritten(Timeout)) { emit error(socket.error(), socket.errorString()); return; }
연결 성공시 socket을 이용하여 write를 진행하고, write 실패시 error signal을 발생 합니다. |
while (socket.bytesAvailable() < (int)sizeof(quint16)) { if (!socket.waitForReadyRead(Timeout)) { emit error(socket.error(), socket.errorString()); return; }else{ QString s_data = QString::fromAscii(socket.readAll()); qDebug() << "recv : " << s_data; } }
데이터 수신을 기다리며, 데이터 read시 qDebug를 이용하여 수신 받은 데이터를 출력합니다. |
위의 코드를 파일로 첨부합니다.
필요하신 분은 가져다 사용하시기 바랍니다.
애드블럭 해제시 파일이 나타납니다.
'QT' 카테고리의 다른 글
리눅스에서 USB 정보 가져오기 (0) | 2019.06.16 |
---|---|
ubuntu 에서 윈도우 QT Application 빌드하기 (0) | 2019.02.27 |
[QT] QTablView 사용하기 및 row 배경 색깔 지정. (0) | 2019.02.11 |
[QT] QListview 에 항목추가 및 배경 지정하기 (0) | 2019.02.08 |
[QT] Ubuntu 18.04LTS環境設定にQTフレームワークを設置する(X11 platform 用) (0) | 2019.02.06 |