1.TCPSocket
1.在工程文件中加入 network (QT += newwork) , 然后ctrl + s 保存一下
2.头文件引入TCP套接字类
#include <QTcpSocket>
3.在widget.h定义一个套接字指针,设置在成员里(因为都会用到)
QTcpSocket *socket;
4.构造套接字
socket = new QtcpSocket(this);
5.按钮绑定信号
连接按键槽函数:
void Widget::on_btnCon_clicked()
{
socket->connectToHost(ui->edtIP->text(),ui->edtPort->text().toInt());
}
断开连接按键槽函数:
void Widget::on_btnDis_clicked()
{
socket->disconnectFromHost();
}
6.发送数据
检查数据是否为空,检查套接字是否有效
void Widget::on_btnSend_clicked()
{
if(ui->msgSend->toPlainText().isEmpty())
return;
if(socket->isValid()){
socket->write( ui->msgSend->toPlainText().toLatin1() );
}
}
------------------------------------
7.接收数据
获取读缓存的有效字节数:
bytesAvailable()
读取缓冲区所有数据;
readAll()
如果每条数据有严格长度要求。就必须使用 bytesAvailable()和read(n)函数配合使用
------------------------------------
读的槽函数绑定:
connect(socket, &QTcpSocket::readyRead, this, &Widget::recvData);
只要socket读缓存 一有新数据,就立即触发 readyRead信号
------------------------------------
槽函数:
void Widget::recvData()
{
qDebug() << socket->bytesAvailable();
QByteArray data = socket->readAll();
ui->msgRecv->append(data);
}
------------------------------------
8.检测客户端连接和断开连接
信号:
connected
disconnected
绑定槽函数:
connect(socket, &QTcpSocket::connected, [&]() {
ui->lStatus->setText("已连接服务器");
});
connect(socket, &QTcpSocket::disconnected, [&](){
ui->lStatus->setText("连接已断开");
});
2.TcpServer
1.在工程文件中加入 network (QT += newwork) , 然后ctrl + s 保存一下
2.头文件引入TCP套接字类
#include <QTcpSocket>
3.在widget.h定义服务器对象
QTcpServer *server;
4.构建服务器对象
server = new QTcpServer(this);
---------------------------------------------------------------------------
5.设置监听套接字:
(通过启动按钮转到槽函数---按下启动服务器的启动按钮,槽函数触发)
void Widget::on_btnStart_clicked()
{
if(!server->listen(QHostAddress::AnyIPv4, ui->lineEdit->text().toInt())){
ui->msgRecv->append("服务器启动失败");
return;
}
ui->msgRecv->append("启动成功");
connect(server, &QTcpServer::newConnection, this, &Widget::newConnection);
}
----------------------------------------------------------------------------
6.等待连接---在监听套接字里绑定的槽函数,一旦有客户端有连接请求的信号,立即触发该槽函数
连接成功后,就会返回一个连接后的套接字,要先在成员的声明,用于接收该套接字
QTcpSocket *socket;
void Widget::newConnection()
{
socket = server->nextPendingConnection();
if(!socket->isValid()){
ui->msgRecv->append("通信建立失败");
return;
}
ui->msgRecv->append(QString("%1 :%2").arg(socket->peerAddress().toString()).arg(socket->peerPort()));
connect(socket, &QTcpSocket::readyRead, this, &Widget::recvData);
connect(socket, &QTcpSocket::disconnected, [&](){
ui->msgRecv->append("客户端已掉线");
disconnect(socket, &QTcpSocket::readyRead, this, &Widget::recvData);
});
}
---------------------------------------------------------------------------
7.发数据和收数据
void Widget::on_btnSend_clicked()
{
if(socket->isValid()){
socket->write(ui->msgSend->toPlainText().toLatin1());
}
}
void Widget::recvData()
{
QByteArray data = socket->readAll();
ui->msgRecv->append(data);
}
3.eg:通过tcp发送图片(用小飞机发)—数据粘包问题
![在这里插入图片描述](https://img-blog.csdnimg.cn/f3f8743597164efabbfa1f42c9a56c17.png)
传数据就要用 QByteArray类
tcp数据发送,有自动纠错的功能,一次性发送的内容不一定是正确的,接收到的字节也不一定是正确的,
所以一定就不可以的readAll()来读取发送的图片数据,会导致数据粘包:
思路:
先发图片上的长度,再发图片内容
在发送图片之前,先一步将图片的大小发送过来,确定好图片的大小,以确保读取精准的字节数。
读取图片时,如果少于之前发送的字节数,那么就不读取,等到字节数等于或大于之前的字节数说明
缓冲区至少累计缓冲了一张图片,这个时候再读取