Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

Gray-Ice

个人博客兼个人网站

这里使用到了QTcpSocket,QTcpServer和QHostAddress这三个头文件。
你应该先在项目的.pro文件里添加QT+=network这段文本。
先说服务端。
服务端的逻辑很简单,bind和listen一步到位,直接使用QTcpServer的对象的listen函数就行:

1
server->listen(QHostAddress::Any, quint16(8888));

然后服务就打开啦!QHostAddress::Any的意思是任意ip段,quint16(8888)的意思是8888号端口。
也就是说我们打开了一个服务,它能够接受任意ip段的地址,而且它在监听8888端口。
然后我们来看服务端完整代码,代码里我做了关闭socket通信,发送消息,接受消息并将消息显示在textEdit控件上的操作:

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
#include "serverwidget.h"
#include "ui_serverwidget.h"

ServerWidget::ServerWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ServerWidget),
// server 和socket分别为QTcpServer和QTcpSocket类型的指针
server(nullptr),
socket(nullptr),
sock_count(0) // 这个参数用来防止多次connect
{
ui->setupUi(this);
// 实例化QTcpServer对象并设置其监听端口,然后开始监听
server = new QTcpServer(this);
server->listen(QHostAddress::Any, quint16(8888));
// 当有一个QTcpSocket连接过来时,会触发newConnection信号
connect(server, &QTcpServer::newConnection,
[=]()
{
std::cout << "Connected" << std::endl;
// nextPendingConnection返回的是一个QTcpSocket对象的指针,我们之后要进行的操作都是对这个返回的QTcpSocket对象进行操作
socket = server->nextPendingConnection();
// 将连接过来的Socket的信息显示在textEdit控件上
ui->textEdit->append(QString("ip: %1, port: %2 connected").arg(socket->peerAddress().toString()).arg(socket->peerPort()));
// 避免多次connect。因为一开始socket还是个nullptr,所以如果连接放在外面的话会出现奇妙的Bug。
if(sock_count == 0)
{
sock_count++;
// 当接收到对方发送的信息后,会触发readyRead信号。这时候可以用readAll来读取对方发送的全部内容
connect(socket, &QTcpSocket::readyRead, [=](){
// readAll返回的是一个QByteArray对象
QString msg = socket->readAll();
// QByteArray对象隐式转换为QString,在控件上显示其内容
ui->textEdit->append(msg);
});
// 当socket连接关闭时,会触发disconnected信号(注意是disconnected不是disconnect哦)
connect(socket, &QTcpSocket::disconnected, [=](){
// 在控件上提示连接已断开
ui->textEdit->append("Disconnected.");
});
}
}
);
}

ServerWidget::~ServerWidget()
{
delete ui;
}


// 这是发送信息的按钮的槽函数
void ServerWidget::on_pushButton_clicked()
{
// 这一步十分必要,因为在构造函数里socket被初始化为nullptr,如果不加判断的话有可能引发段错误
if(socket != nullptr)
{
// 获取textEdit控件上的内容
QString msg = ui->textEdit_2->toPlainText();
// 发送内容。write接收一个const char*的参数,所以要这么转换。
socket->write(msg.toUtf8().data());
}
}

// 这是关闭socket连接的按钮的槽函数
void ServerWidget::on_pushButton_2_clicked()
{
// 判断socket指针是否为空
if(socket != nullptr)
{
// 关闭连接
socket->disconnected();
socket->close();
// 在控件上提示关闭连接
ui->textEdit->append("Activily disconnected");
}
}

其实最主要的就是readyRead, newConnection, disconnected这几个信号和readAll,write,listen等成员函数,其他的也没啥。
然后来看客户端的代码。
客户端我做了发送消息,接收消息,发起连接和关闭连接。
代码如下:

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
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <iostream>

ClientWidget::ClientWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ClientWidget)
, socket(nullptr) // 初始化socket为nullptr
{
ui->setupUi(this);
setWindowTitle("Client");
// socket指向一个QTcpSocket对象
socket = new QTcpSocket(this);
// connected是一个信号,它的触发代表着连接成功
connect(socket, &QTcpSocket::connected, [=](){
ui->textEdit->append("Connect successful.");
});
// readyRead是一个信号。它的触发代表着有消息过来了
connect(socket, &QTcpSocket::readyRead, [=](){
QString msg;
// 将QbyteArray转换成QString
msg.prepend(socket->readAll());
// 在控件上展示消息
ui->textEdit->append(msg);
});
// 当连接关闭的时候会触发disconnected信号
connect(socket, &QTcpSocket::disconnected, [=](){
ui->textEdit->append("Disconnected.");
std::cout << "disconnected" << std::endl;
// 关闭连接。其实这一步应该是不用的。但是我们为了保险起见,还是关闭一下。
socket->disconnectFromHost();
socket->close();
// 重置socket为nullptr
socket = nullptr;
});
}

ClientWidget::~ClientWidget()
{
delete ui;
}


// 这是发起连接按钮的槽函数
void ClientWidget::on_connButton_clicked()
{
std::cout << "connecting" << std::endl;
// 获取用户输入的ip
QString ip = ui->lineEdit->text();
// 获取用户输入的端口号
QString port = ui->lineEdit_2->text();
// 把用户输入的端口号转化为无符号int,再转化为quint16类型
quint16 p = port.toUInt();
// 向服务端发起连接
socket->connectToHost(ip, p);
}

// 这是关闭连接按钮的槽函数
void ClientWidget::on_closeButton_clicked()
{
// 依然是判断是否为nullptr防止访问未分配的内存
if(socket != nullptr)
{
// 主动断开Tcp连接
socket->disconnectFromHost();
socket->close();
}
}

// 这是发送消息的槽函数
void ClientWidget::on_sendButton_clicked()
{
// 防止访问为分配的内存
if(socket != nullptr)
{
// 从控件获取要发送的消息
QString msg = ui->textEdit_2->toPlainText();
// 把要发送的消息转换成const char*类型作为write函数的参数
socket->write(msg.toUtf8().data());
}
}

相比于服务端,客户端要做的内容要简单一些,主要就是主动发起连接,收发信息以及关闭socket连接。
各段代码的内容我的注释写的很明白了,若是还不懂欢迎在我博客下方的评论区发起评论,我会在收到消息后第一时间与发起者进行技术讨论。
关键内容就是上面那些,那么下面来看看我的头文件:
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
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include <iostream>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE

class ServerWidget : public QWidget
{
Q_OBJECT

public:
ServerWidget(QWidget *parent = nullptr);
~ServerWidget();

private slots:
void on_pushButton_clicked();

void on_pushButton_2_clicked();

private:
Ui::ServerWidget *ui;
QTcpServer* server;
QTcpSocket* socket;
char sock_count;
};
#endif // SERVERWIDGET_H

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
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include <QWidget>
#include <QHostAddress>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class ClientWidget; }
QT_END_NAMESPACE

class ClientWidget : public QWidget
{
Q_OBJECT

public:
ClientWidget(QWidget *parent = nullptr);
~ClientWidget();

private slots:
void on_connButton_clicked();

void on_closeButton_clicked();

void on_sendButton_clicked();

private:
Ui::ClientWidget *ui;
QTcpSocket* socket;
};
#endif // CLIENTWIDGET_H

最后再说一次,别忘记在项目的.pro文件里加上:

1
QT += network

好了,本篇完。

评论



愿火焰指引你