不知道有么有小伙伴和我一样
明明教程已经很详细了
但是架不住菜的抠脚
在人家那里是孙红雷
下载下来自己一用就是马冬梅
于是我研究了一个网上的案例,把每一句话都写了注释
希望对和我一样菜的能有帮助
--只写保姆级教程--
(因为其他的也写不了~~!)
话不多说,上代码。
***----------****
首先TCP通讯,我们不想复杂了,因为我们是菜鸟。
我们想象打电话,是不是的一个打,一个接?
对,就是这个过程,所以我们需要两个端。
客户端和服务端。
Client端和Server端;
一:Client端代码:
--.h文件(里面我都做好了注释)
(只看cpp文件就行,从001开始)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_sendMsg_clicked();
void on_connect_clicked();
void on_disconnect_clicked();
private:
Ui::MainWindow *ui;
QTcpSocket* c_tcp;
//002--定义一个c_tcp监听对象;
//002--在做这个定义之前,记得加上<QTcpSocket>的头文件;
//这个就是创建一个Client(客户端)监听对象,我们简写为c_tcp;
QTcpSocket* m_tcp;
QLabel* m_status;
};
#endif // MAINWINDOW_H
--.cpp文件
//注释说明********崔某人********//
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostAddress>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->port->setText("9527");
ui->ip->setText("127.0.0.1");
setWindowTitle("客户端");
//001--把界面上的端口和IP,让它获取一个默认值;
//001--更改一下窗口的名称;
ui->disconnect->setDisabled(true);
//002--去头文件内,定义一个监听对象;(---此处转头文件)
// 创建监听的服务器对象
c_tcp = new QTcpSocket;
//003--创建监听对象
//知识点:xxx = new xxx是个什么玩意?
connect(c_tcp, &QTcpSocket::readyRead, this, [=]()
{
QByteArray data = c_tcp->readAll();
ui->record->append("服务器say: " + data);
//004--创建连接(信号和槽)
//信号的发出者是我们定义的监听对象c_tcp,它发出了什么信号?readyRead(读取数据)
//信号的接收者是this,接受了之后作何反应?(下面大括号的内容)
//定义一个字节数组 data,然后把我们从---服务端---监听到的数据,read all(读取所有),放到data里面;
//顺手把他放到UI界面上(append增加内容;record是控件的名称,可自主命名)
//知识点:接收者this是个什么东西?
});
connect(c_tcp, &QTcpSocket::disconnected, this, [=]()
{
c_tcp->close();
c_tcp->deleteLater(); // delete c_tcp
m_status->setPixmap(QPixmap(":/disconnect.png").scaled(20, 20));
ui->record->append("服务器已经和客户端断开了连接...");
ui->connect->setDisabled(false);
ui->disconnect->setEnabled(false);
//005--这个和004相对应的,关闭监听对象;
//首先是个connect连接;
//信号的发出者是c_tcp(还是我们的监听者;它发出的信号是disconnected;
//接收者还是this;做出的反应见大括号内;
//大括号内:
//首先是 c_tcp close(关闭监听什么的)
//然后是 c_tcp deleteLater (关闭这个线程,这个描述不准确)
//这个后面还加了个delete c_tcp 这个不推荐
//deleteLater 是 QObject 类对象的成员函数,用于延迟删除一个 QObject 类对象;
//deleteLater 会在当前对象的所有事件处理完成后再删除对象
//delete 则是立即删除对象,对象的既有事件不再处理
//接下来处理下状态栏m_status;
//setPixmap 是让图片在控件上显示;
//scaled(20, 20) 这个玩意是给我们需要显示的图片一个固定的尺寸;
//这里需要注意,我们的先添加资源文件,而且":/disconnect.png"路径表述方式需要注意,免得找不到;
//ui->record->append("服务器已经和客户端断开了连接...");
//这一句,和之前一样,在历史消息里面加一句;(写到这里,应该注意一下append的用法了)
//下面两句不明白
});
connect(c_tcp, &QTcpSocket::connected, this, [=](){
m_status->setPixmap(QPixmap(":/connect.png").scaled(20, 20));
ui->record->append("已经成功连接到了服务器...");
ui->connect->setDisabled(true);
ui->disconnect->setEnabled(true);
//006--这个是连接成功的槽;
//不做解释,和005基本一样,就是发出的信号不同;大括号内的内容稍有不同;
});
//007-- 状态栏
m_status = new QLabel;
m_status->setPixmap(QPixmap(":/disconnect.png").scaled(20, 20));
//这里让状态栏控件,默认获得连接失败的图标;
ui->statusbar->addWidget(new QLabel("连接状态: "));
ui->statusbar->addWidget(m_status);
//这里给状态栏status再加上文本 label (注意和增加图片的不同)
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_sendMsg_clicked()
{
QString msg = ui->msg->toPlainText();
c_tcp->write(msg.toUtf8());
ui->record->append("客户端say: " + msg);
//008--这个是当“发送信息”按钮被单击的槽函数;
//首先是是使用下toPlainText()获取下文本内容,放到msg内;
//接下来强行理解一波就行,我们这个按钮干嘛的?发送数据。
//放到哪里发送呢?我们从头到尾就创建了一个对象,叫c_tcp,显然是放在这里面;
//write是写入,写入什么?msg;以什么格式写入?utf8这个格式;
//顺手把上面的历史消息更新一波;(这个其实就是单机版,不管是否连接了服务器,都能成功)
//严谨点的话这个应该放在别的地方;不过问题不大;
}
void MainWindow::on_connect_clicked()
{
QString ip = ui->ip->text();
unsigned short port = ui->port->text().toUShort();
c_tcp->connectToHost(QHostAddress(ip), port);
//009--这个是连接服务器的按钮;
//首先获取一下,IP那个控件内的内容;
//这里要注意了,我们前面给了一个127.0.0.1;
//所以实际去应用的时候,前面给不给无所谓;(或者直接填上你要给的那个就行)
//接下来是获取端口号;
//端口号前面的unsiged short 这个叫无符号短整型;(防止你有什么骚操作,或者不小心打错了)
//最后也是最重要的一步,IP地址,端口号我们都正确的获取了,再干嘛?
//肯定是和服务器连接一波;connect to host 是连接到主机;
//这个函数里面有俩参数--IP地址和端口;
//注意出现了个新的玩意儿,QHostAddress,在头文件要加上<QHostAddress>
//当我们的IP地址和端口都正确的时候,就连接成功了(这么说不严谨,前提是那边开着)
}
void MainWindow::on_disconnect_clicked()
{
c_tcp->close();
ui->connect->setDisabled(false);
ui->disconnect->setEnabled(false);
//010--这个是关闭连接,没什么可解释的;
}
//*****Client端解释完毕,现在我们去Server端(Client序号是001--010)
二:客户端
--.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_setListen_clicked();
void on_sendMsg_clicked();
private:
Ui::MainWindow *ui;
QTcpServer* m_s;
QTcpSocket* s_tcp;
QTcpSocket* m_tcp;
QLabel* m_status;
};
#endif // MAINWINDOW_H
--.cpp文件
//注释说明********崔某人********//
//***先去看Client端**//
//Server端的序号从011开始//
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->port->setText("9527");
setWindowTitle("服务器");
//011--使端口号默认获得一个端口;
//011--更改一下窗口的标题;
m_s = new QTcpServer(this);
// 012--创建监听的服务器对象;
//在Client端,我们创建的是c_tcp,是管着发送的;
//那么对应的,我们需要创建一个用来监听的;
//就像打电话一样,起码需要两部手机,一个打,一个接;这么理解;
//
connect(m_s, &QTcpServer::newConnection, this, [=](){
s_tcp = m_s->nextPendingConnection();
//013--注意看这个连接connect;
//调用nextPendingConnection()来接受待处理的连接;我们的客户端,点击发送按钮之后,肯定发送了个连接请求;
//nextPendingConnection这个就是用来处理请求;
//剖析下这个连接;
//信号的发出者是我们创建的服务端监听,它发出的信号是newConnection
//可以把它理解为每当有新的连接可用时,都会发出此信号;
//信号的接收者是this;接收者做出的反应是大括号内内容;
m_status->setPixmap(QPixmap(":/connect.png").scaled(20, 20));
//014--这一行不解释了,还是状态栏,只不过换了个图;
connect(s_tcp, &QTcpSocket::readyRead, this, [=]()
{
QByteArray data = s_tcp->readAll();
ui->record->append("客户端say: " + data);
//015--这个也比较好理解;右这么几个点需要注意下:
//首先,和Client端不一样的是,这个连接不是独立的,而是在上一个连接内,为什么?
//(上面那个链接干嘛的?)(自己理解--结果就是,不显示客户端发送过来的内容)
//第二点,还是剖析四件套--发出者,发出的信号,接收者,做出的反应;
//发出者是s_tcp,信号是读取数据,接收者是this,反应是大括号内;
//这个地方有点绕,顺一下:
//首先s_tcp->readAll() 好理解,就是读取所有数据;
//那么问题来了,s_tcp,里面有啥呀你就读?
//往上翻我们发现了 s_tcp = m_s->nextPendingConnection();
//咱们现在还在人家大连接里面呢,m_s->nextPendingConnection()是处理信号;
//也就是说它瞎几把处理之后,赋值给了我们定义的s_tcp,然后读出来;
//016--ui那个比较好理解;读取的内容显示到UI界面上;
});
connect(s_tcp, &QTcpSocket::disconnected, this, [=]()
{
s_tcp->close();
s_tcp->deleteLater(); // delete s_tcp
m_status->setPixmap(QPixmap(":/disconnect.png").scaled(20, 20));
//017--这个也好理解,关闭连接的,就是换个图;
});
});
//018--不解释了,更改状态栏的一些操作;(有时间可以研究下里面的函数使用)
m_status = new QLabel;
m_status->setPixmap(QPixmap(":/disconnect.png").scaled(20, 20));
ui->statusbar->addWidget(new QLabel("连接状态: "));
ui->statusbar->addWidget(m_status);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_setListen_clicked()
{
unsigned short port = ui->port->text().toUShort();
m_s->listen(QHostAddress::Any, port);
ui->setListen->setDisabled(true);
//019--这个是启动监听的按钮;
//unsigned short port = ui->port->text().toUShort();这句话解释过了在Client端,不说了;
//下面有新东西 m_s->listen(QHostAddress::Any, port);
//m_s 是什么?是我们创建的监听啊!他要干嘛?
//listen 监听;和我们Client端的那个发送很像;
//还是别忘了加上<QHostAddress>的头文件;
}
void MainWindow::on_sendMsg_clicked()
{
QString msg = ui->msg->toPlainText();
s_tcp->write(msg.toUtf8());
ui->record->append("服务器say: " + msg);
//020--发送消息,和Client一样的不说了;
}
//总结:这个案例也有很多不足的地方,但是理解起来还是不错的;
//程序容易死掉,而且顺序不要错;
//总而言之还不错;
PS:效果图
先做UI界面,记好了控件的名称,免得到了代码里不认识