TCP 协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,通过三次握手来建立可靠的连接。
TCP连接是双向的,断开时经历四次挥手,在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
QTcpSocket 继承自QAbstractSocket 类,用于建立 TCP 连接并传输数据流。
QTcpServer 服务端通过 nextPendingConnection() 接口获取到建立了 TCP 连接的 QTcpSocket 对象。
客户端在创建好 QTcpSocket 对象后,调用 connectToHost() 连接到服务端
服务端代码如下:
.h
class ServerProject : public QWidget
{
Q_OBJECT
public:
ServerProject(QWidget *parent = nullptr);
~ServerProject();
void iniServer();
void closeServer();
private slots:
void on_listenBtn_clicked();
void on_sendBtn_clicked();
void newConnectionSlot();
void readyReadSlot();
private:
Ui::ServerProjectClass ui;
QTcpServer* m_server;
QTcpSocket* m_socket;
};
.cpp
ServerProject::ServerProject(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
setWindowTitle("Server");
iniServer();
}
ServerProject::~ServerProject()
{
closeServer();
if (m_socket != NULL) {
delete m_socket;
m_socket = NULL;
}
}
void ServerProject::iniServer() {
m_socket = NULL;
m_server = new QTcpServer(this);
}
void ServerProject::closeServer() {
m_server->close();
if (m_socket == NULL) {
return;
}
//断开与客户端的连接
if (m_socket->state() == QAbstractSocket::ConnectedState) {
m_socket->disconnectFromHost();
if (m_socket->state() != QAbstractSocket::UnconnectedState) {
m_socket->abort();
}
}
}
void ServerProject::on_listenBtn_clicked() {
if (m_server->isListening()) {
closeServer();
//关闭server后恢复界面状态
ui.listenBtn->setText("Listen");
ui.addressEt->setEnabled(true);
ui.portEt->setEnabled(true);
}
else {
//可以使用 QHostAddress::Any 监听所有地址的对应端口
const QString address_text = ui.addressEt->text();
const unsigned short port = ui.portEt->text().toUShort();
const QHostAddress address = (address_text == "Any")
? QHostAddress::Any
: QHostAddress(address_text);
//开始监听,并判断是否成功
if (m_server->listen(address, port)) {
//连接成功就修改界面按钮提示,以及地址栏不可编辑
ui.listenBtn->setText("Close");
ui.addressEt->setEnabled(false);
ui.portEt->setEnabled(false);
}
connect(m_server, &QTcpServer::newConnection, this, &ServerProject::newConnectionSlot);
}
}
void ServerProject::newConnectionSlot() {
if (m_server->hasPendingConnections())
{
//nextPendingConnection返回下一个挂起的连接作为已连接的QTcpSocket对象
//套接字是作为服务器的子级创建的,这意味着销毁QTcpServer对象时会自动删除该套接字。
//最好在完成处理后显式删除该对象,以避免浪费内存。
//返回的QTcpSocket对象不能从另一个线程使用,如有需要可重写incomingConnection().
m_socket = m_server->nextPendingConnection();
ui.receiveEt->append("connected.........");
connect(m_socket, &QTcpSocket::readyRead, this, &ServerProject::readyReadSlot);
}
}
void ServerProject::readyReadSlot() {
if (m_socket->bytesAvailable() <= 0)
return;
//注意收发两端文本要使用对应的编解码
const QString recv_text = QString::fromUtf8(m_socket->readAll());
ui.receiveEt->append(recv_text);
}
void ServerProject::on_sendBtn_clicked() {
if (!m_server->isListening())
return;
//将发送区文本发送给客户端
const QByteArray send_data = ui.sendEt->toPlainText().toUtf8();
//数据为空就返回
if (send_data.isEmpty())
return;
m_socket->write(send_data);
}
客户端代码如下:
.h
class ClientPro : public QWidget
{
Q_OBJECT
public:
ClientPro(QWidget *parent = nullptr);
~ClientPro();
//初始化client操作
void initClient();
private slots:
void on_connectBtn_clicked();
void on_sendBtn_clicked();
void connectedSlot();
void disconnectedSlot();
void readyReadSlot();
private:
Ui::ClientProClass ui;
QTcpSocket* m_client;
};
.cpp
ClientPro::ClientPro(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
initClient();
}
ClientPro::~ClientPro()
{
qDebug() << "~ClientPro()----------------------------";
if (m_client->state() == QAbstractSocket::ConnectedState) {
//如果使用disconnectFromHost()不会重置套接字,isValid还是会为true
m_client->abort();
}
}
void ClientPro::initClient() {
//创建client对象
m_client = new QTcpSocket(this);
}
void ClientPro::on_connectBtn_clicked() {
if (m_client->state() == QAbstractSocket::ConnectedState) {
//如果使用disconnectFromHost()不会重置套接字,isValid还是会为true
m_client->abort();
}
else if (m_client->state() == QAbstractSocket::UnconnectedState) {
//从界面上读取ip和端口
const QHostAddress address = QHostAddress(ui.addressEt->text());
const unsigned short port = ui.portEt->text().toUShort();
//连接服务器
m_client->connectToHost(address, port);
}
else {
ui.receiveEt->append("It is not ConnectedState or UnconnectedState");
}
connect(m_client, &QTcpSocket::connected, this, &ClientPro::connectedSlot);
connect(m_client, &QTcpSocket::disconnected, this, &ClientPro::disconnectedSlot);
connect(m_client, &QTcpSocket::readyRead, this, &ClientPro::readyReadSlot);
}
void ClientPro::connectedSlot() {
ui.connectBtn->setText("Disconnect");
ui.addressEt->setEnabled(false);
ui.portEt->setEnabled(false);
}
void ClientPro::disconnectedSlot() {
ui.connectBtn->setText("Connect");
ui.addressEt->setEnabled(true);
ui.portEt->setEnabled(true);
}
void ClientPro::readyReadSlot() {
if (m_client->bytesAvailable() <= 0)
return;
//注意收发两端文本要使用对应的编解码
const QString recv_text = QString::fromUtf8(m_client->readAll());
ui.receiveEt->append(recv_text);
}
void ClientPro::on_sendBtn_clicked() {
if (!m_client->isValid())
return;
//将发送区文本发送给客户端
const QByteArray send_data = ui.sendEt->toPlainText().toUtf8();
//数据为空就返回
if (send_data.isEmpty())
return;
m_client->write(send_data);
}