■ QTcpServer 클래스
- 상세 설명
QTcpServer 클래스를 사용하면 Client로 부터 들어오는 TCP 연결을 받아들일 수 있습니다.
포트를 지정하거나 QTcpServer가 자동으로 포트를 선택하도록 설정이 가능합니다.
특정 IP의 기기 혹은 모든 기기의 IP의 수신 요청에 대한 대기를 할 수 있습니다.
서버가 들어오는 연결을 수신하기 위해 listen()을 호출하면, 클라이언트가 서버에 연결할 때마다 newConnection() 신호가 생성됩니다.
보류 중인 연결을 연결된 QTcpSocket으로 승인하려면 nextPendingConnection()을 호출하면 됩니다.
이 함수는 QAbstractSocket :: ConnectedState에서 클라이언트와의 통신에 사용할 수있는 QTcpSocket에 대한 포인터를 Return 합니다.
오류가 발생하면 serverError()가 오류 유형을 반환하고, errorString()을 호출하여 발생한 일에 대한 설명을 제공합니다.
연결을 기다릴 경우 서버가 기다리는 IP 및 Port는 serverAddress() 및 serverPort()로 알 수 있습니다.
close()를 호출 시 QTcpServer가 수신을 중지합니다.
- 사용법
QTcpServer 클래스는 서버를 수행하는 데 필요한 모든 함수를 제공합니다.
우선, 임의의 ip, 무작위 포트를 청취하고 클라이언트가 연결을 요청할 때 Signal / Slot으로 동작시킵니다.
Server. 49번째 줄: connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); Server. 52번째 줄: socket_data.sprintf("Listening: %s\n", server->listen(QHostAddress::Any, 1024) ? "true" : "false"); |
이후, 새 연결이 있으면 클라이언트 목록에 추가하고 소켓에서 Read / Write가 가능합니다.
Server. 65번째 줄: QTcpSocket *socket = server->nextPendingConnection();(QAbstractSocket::SocketState))); Server. 66번째 줄: connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); |
■ QTcpSocket 클래스
- 상세 설명
TCP (Transmission Control Protocol)는 안정적인 스트림 지향 연결 지향 전송 프로토콜입니다.
데이터의 지속적인 전송에 특히 적합하게 설계 되었습니다.
connectToHost(ip, port) 함수를 통해 Server에 연결을 요청하고, 연결이 될 경우 write() 함수로 Data를 전달합니다.
QTcpSocket은 QAbstractSocket의 편리한 서브 클래스로 TCP 연결을 설정하고 데이터 스트림을 전송할 수 있습니다.
- 사용법
Qt에서 TCP 연결을 만들려면 QTcpSocket을 사용해야 하는데 connectToHost() 함수로 연결해야 합니다.
로컬 TCP 서버에 연결하기 위해 아래와 같이 함수를 사용합니다.
Client. 51번째 줄: fd_flag = connectToHost("127.0.0.1"); |
연결이 완료되면 Server에 데이터를 보내기 위해, 아래와 같이 전송 가능합니다.
Client .72번째 줄: socket->write(IntToArray(data.size())); Client. 73번째 줄: socket->write(data); |
아래의 예제에서 Server에서 Client 쪽으로의 Write 함수, Client에서 Server로 부터의 Read 함수는 구현하지 않았지만,
이미 서로 TCP 연결은 되어있기 때문에 Server에서는 write(), Client에서는 Signal / Slot만 예제와 반대로 구현해주면 사용 가능합니다.
■ 예제
QTcpServer 클래스와 QTcpSocket 클래스를 이용한 Socket 통신 예제입니다.
Server 클래스가 Client의 요청을 기다리다가 Client 클래스 실행 시 TCP 통신을 할 소켓이 연결됩니다.
또한, 예제를 실행하기 위해서는 Qt Network C++ Class 사용해야 하는데, 이를 위해 모듈을 연결하는 작업이 필요합니다.
모듈과 연결하기 위해 Project 파일에 아래와 같이 코드를 추가해야 합니다.
.pro file
QT += network |
- Server
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
// Server.h
#ifndef SERVER_H
#define SERVER_H
#include <QMainWindow>
#include <QtCore>
#include <QtNetwork>
#include <QString>
namespace Ui {
class Server;
}
class Server : public QMainWindow
{
Q_OBJECT
public:
explicit Server(QWidget *parent = nullptr);
~Server();
signals:
void dataReceived(QByteArray);
private slots:
void newConnection();
void disconnected();
void readyRead();
private:
Ui::Server *ui;
QTcpServer *server;
QHash<QTcpSocket*, QByteArray*> buffers; //We need a buffer to store data until block has completely received
QHash<QTcpSocket*, qint32*> sizes; //We need to store the size to verify if a block has received completely
};
#endif // SERVER_H
// Server.cpp
#include "server.h"
#include "ui_server.h"
static inline qint32 ArrayToInt(QByteArray source);
Server::Server(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Server)
{
ui->setupUi(this);
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
QString socket_data;
socket_data.sprintf("Listening: %s\n", server->listen(QHostAddress::Any, 1024) ? "true" : "false");
ui->textBrowser->insertPlainText(socket_data);
}
Server::~Server()
{
delete ui;
}
void Server::newConnection()
{
while (server->hasPendingConnections())
{
QTcpSocket *socket = server->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
QByteArray *buffer = new QByteArray();
qint32 *s = new qint32(0);
buffers.insert(socket, buffer);
sizes.insert(socket, s);
}
}
void Server::disconnected()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *buffer = buffers.value(socket);
qint32 *s = sizes.value(socket);
socket->deleteLater();
delete buffer;
delete s;
}
void Server::readyRead()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *buffer = buffers.value(socket);
qint32 *s = sizes.value(socket);
qint32 size = *s;
while (socket->bytesAvailable() > 0)
{
buffer->append(socket->readAll());
while ((size == 0 && buffer->size() >= 4) || (size > 0 && buffer->size() >= size)) //While can process data, process it
{
if (size == 0 && buffer->size() >= 4) //if size of data has received completely, then store it on our global variable
{
size = ArrayToInt(buffer->mid(0, 4));
*s = size;
buffer->remove(0, 4);
}
if (size > 0 && buffer->size() >= size) // If data has received completely, then emit our SIGNAL with the data
{
ui->textBrowser->insertPlainText(QString(buffer->data()));
QByteArray data = buffer->mid(0, size);
buffer->remove(0, size);
size = 0;
*s = size;
emit dataReceived(data);
}
}
}
}
qint32 ArrayToInt(QByteArray source)
{
qint32 temp;
QDataStream data(&source, QIODevice::ReadWrite);
data >> temp;
return temp;
}
|
- Client
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
// Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QMainWindow>
#include <QtCore>
#include <QtNetwork>
namespace Ui {
class Client;
}
class Client : public QMainWindow
{
Q_OBJECT
public:
explicit Client(QWidget *parent = nullptr);
~Client();
public slots:
bool connectToHost(QString host);
bool writeData(QByteArray data);
private slots:
void on_pushButton_clicked();
private:
Ui::Client *ui;
QTcpSocket *socket;
bool fd_flag = false;
bool send_flag = false;
int cnt = 0;
};
#endif // CLIENT_H
// Client.cpp
#include "client.h"
#include "ui_client.h"
static inline QByteArray IntToArray(qint32 source);
Client::Client(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Client)
{
ui->setupUi(this);
socket = new QTcpSocket(this);
fd_flag = connectToHost("127.0.0.1"); // localhost
if(!fd_flag)
ui->textBrowser->insertPlainText("Socket connect fail\n");
}
Client::~Client()
{
delete ui;
}
bool Client::connectToHost(QString host)
{
socket->connectToHost(host, 1024); // ip address, port
return socket->waitForConnected();
}
bool Client::writeData(QByteArray data)
{
if(socket->state() == QAbstractSocket::ConnectedState)
{
socket->write(IntToArray(data.size())); // write size of data
socket->write(data); // write the data itself
return socket->waitForBytesWritten();
}
else
{
return false;
}
}
QByteArray IntToArray(qint32 source) // Use qint32 to ensure that the number have 4 bytes
{
// Avoid use of cast, this is the Qt way to serialize objects
QByteArray temp;
QDataStream data(&temp, QIODevice::ReadWrite);
data << source;
return temp;
}
void Client::on_pushButton_clicked()
{
if(fd_flag)
{
QString textData, sendData;
textData.sprintf("Button click: %d\n", cnt);
sendData.sprintf("Socket data: %d\n", cnt);
ui->textBrowser->insertPlainText(textData);
send_flag = writeData(sendData.toStdString().c_str());
if(!send_flag)
ui->textBrowser->insertPlainText("Socket send fail\n");
else
cnt++;
}
}
|
// Image
'Qt' 카테고리의 다른 글
pthread 및 QThread 쓰레드 사용하기 (5) | 2020.03.30 |
---|---|
QSqlDatabase 클래스 활용 데이터베이스 다루기 (0) | 2020.03.16 |
Qt Resource 활용 Main Toolbar 이미지 버튼 추가하기 (0) | 2020.03.06 |
Qt 메인 쓰레드 이벤트 처리 (0) | 2020.02.25 |
QWidget / QDialog / QMainWindow 및 Modal / Modaless 비교 (1) | 2020.02.24 |