1 效果图

2 Server端
#include "widget.h"#include "ui_widget.h"#include <QDebug>#include <QHostAddress>#include <QFileDialog>#include <QDataStream>#include <QDateTime>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); ui->splitter->setStretchFactor(0,2); // 上面占2份 ui->splitter->setStretchFactor(1,1); // 下面占1份 connect(&server,SIGNAL(newConnection()), this,SLOT(onNewConnection())); // 服务端开始监听所有IPv4地址 bool isOk = server.listen(QHostAddress::AnyIPv4,5485); qDebug() << "监听结果:" << isOk; imageIndex = 0; LastPacksize = 0;}Widget::~Widget(){ delete ui;}void Widget::onNewConnection(){ QTcpSocket* socket = server.nextPendingConnection(); clients.append(socket); connect(socket,SIGNAL(connected()), this,SLOT(onConnected())); connect(socket,SIGNAL(disconnected()), this,SLOT(onDisconnected())); connect(socket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(onError(QAbstractSocket::SocketError))); connect(socket,SIGNAL(readyRead()), this,SLOT(onReadyRead()));}void Widget::onConnected() // 连接成功{ qDebug() << "connected";}void Widget::onDisconnected() // 断开{ QObject* obj = this->sender(); QTcpSocket* socket = qobject_cast<QTcpSocket*>(obj); clients.removeAll(socket); socket->deleteLater(); qDebug() << "disconnected";}void Widget::onError(QAbstractSocket::SocketError socketError) // 出错{ qDebug() << "error: " << socketError;}void Widget::onReadyRead() // 读取数据{ QObject* obj = this->sender(); QTcpSocket* socket = qobject_cast<QTcpSocket*>(obj); quint64 sizeNow = 0; QDataStream stream(socket); do { // 当前缓冲区里的数据大小 sizeNow = socket->bytesAvailable(); // 获取等待读取的传入字节数。 if(LastPacksize == 0) // 第一次要先读取包大小 { // 第一次要先判断传入字节数是否小于4字节 if(sizeNow < sizeof(quint32)) return; stream >> LastPacksize; } qDebug() << "sizeNow:"<< sizeNow; qDebug() << "sizePack:"<< LastPacksize; // 包不完整返回,防止出现半包 if(sizeNow < LastPacksize - 4) return; // 包已经完整,数据读取完毕 qDebug() << "数据包读取完毕"; LastPacksize = 0; // 设置输出框内容 QString dateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); QByteArray dataFull; stream >> dataFull; // 读取完整数据 QString prefix = dataFull.mid(0,4); if(prefix == "TXT:") { ui->tedtOutput->append(dateTime); QString textContent = dataFull.mid(4); ui->tedtOutput->append("<p>" + textContent + "</p>"); } else if(prefix == "IMG:") { ui->tedtOutput->append(dateTime); QString index = QString::number(imageIndex); QFile file(index + ".png"); file.open(QIODevice::WriteOnly); file.write(dataFull.mid(4)); file.close(); QString htmlTag = QString("<img src=\"%1\"></img>").arg(index + ".png"); ui->tedtOutput->append(htmlTag); imageIndex++; } // 判断剩下的字节数,是否会有粘包的情况 sizeNow = socket->bytesAvailable(); qDebug() << "剩余等待读取的传入字节数 sizeNow=" << sizeNow; }while(sizeNow > 0);}void Widget::on_btnSend_clicked(){ QString qstrSend = ui->tedtInput->toPlainText(); if(qstrSend.isEmpty()) return; QList<QTcpSocket*>::iterator it; // 把服务端的文字发给所有连接的客户端 for(it = clients.begin(); it != clients.end(); ++it) { QString input = "TXT:"+ qstrSend; QByteArray data = input.toLocal8Bit(); // 封装数据包,加上包头 QByteArray dataSend; // 封装的数据包 QDataStream stream(&dataSend,QIODevice::WriteOnly); // 定义一个数据流 // 写入数据到数据流,格式为:数据包大小(32bit)+数据 stream << (quint32)0 << data; // 移到流索引为0处, 用数据大小覆盖 stream.device()->seek(0); stream << dataSend.size(); (*it)->write(dataSend); } ui->tedtInput->clear();}void Widget::on_btnImage_clicked(){ QString imagePath = QFileDialog::getOpenFileName(this,"请选择一张图片",".","Images (*.png *.xpm *.jpg)"); if(imagePath.isEmpty()) return; QFile file(imagePath); file.open(QIODevice::ReadOnly); QByteArray data = "IMG:" + file.readAll(); file.close(); // 封装数据包,加上包头 QByteArray dataSend; // 封装的数据包 QDataStream stream(&dataSend,QIODevice::WriteOnly); // 定义一个数据流 // 写入数据到数据流,格式为:数据包大小(32bit)+数据 stream << (quint32)0 << data; // 移到流索引为0处, 用数据大小覆盖 stream.device()->seek(0); stream << dataSend.size(); QList<QTcpSocket*>::iterator it; // 把服务端的图片发给所有连接的客户端 for(it = clients.begin(); it != clients.end(); ++it) { (*it)->write(dataSend); }}
3 客户端
#include "widget.h"#include "ui_widget.h"#include <QDebug>#include <QFile>#include <QFileDialog>#include <QDateTime>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); ui->splitter->setStretchFactor(0,2); // 上面占2份 ui->splitter->setStretchFactor(1,1); // 下面占1份 connect(&tcpSocket,SIGNAL(connected()), this,SLOT(onConnected())); connect(&tcpSocket,SIGNAL(disconnected()), this,SLOT(onDisconnected())); connect(&tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(onError(QAbstractSocket::SocketError))); connect(&tcpSocket,SIGNAL(readyRead()), this,SLOT(onReadyRead())); // 客户端主动连接服务端 tcpSocket.connectToHost("127.0.0.1",5485); // host地址为本机,端口号为5485 imageIndex = 0; LastPacksize = 0;}Widget::~Widget(){ delete ui;}void Widget::onConnected(){ qDebug() << "connected";}void Widget::onDisconnected(){ qDebug() << "disconnected"; QObject* obj = this->sender(); QTcpSocket* socket = qobject_cast<QTcpSocket*>(obj); if(socket == 0) return; // 说明是客户端先关闭,不用做其他操作 socket->close(); // 如果是服务端先关闭,就关闭这个socket}void Widget::onError(QAbstractSocket::SocketError socketError){ qDebug() << "error: " << socketError;}void Widget::onReadyRead(){ QObject* obj = this->sender(); QTcpSocket* socket = qobject_cast<QTcpSocket*>(obj); quint64 sizeNow = 0; QDataStream stream(socket); do { // 当前缓冲区里的数据大小 sizeNow = socket->bytesAvailable(); // 获取等待读取的传入字节数。 if(LastPacksize == 0) // 第一次要先读取包大小 { // 第一次要先判断传入字节数是否小于4字节 if(sizeNow < sizeof(quint32)) return; stream >> LastPacksize; } qDebug() << "sizeNow:"<< sizeNow; qDebug() << "sizePack:"<< LastPacksize; // 包不完整返回,防止出现半包 if(sizeNow < LastPacksize - 4) return; // 包已经完整,数据读取完毕 qDebug() << "包已经完整,数据读取完毕"; LastPacksize = 0; // 设置输出框内容 QString dateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); QByteArray dataFull; stream >> dataFull; // 读取完整数据 QString prefix = dataFull.mid(0,4); if(prefix == "TXT:") { ui->tedtOutput->append(dateTime); QString textContent = dataFull.mid(4); ui->tedtOutput->append("<p>" + textContent + "</p>"); } else if(prefix == "IMG:") { ui->tedtOutput->append(dateTime); QString index = QString::number(imageIndex); QFile file(index + ".png"); file.open(QIODevice::WriteOnly); file.write(dataFull.mid(4)); file.close(); QString htmlTag = QString("<img src=\"%1\"></img>").arg(index + ".png"); ui->tedtOutput->append(htmlTag); imageIndex++; } // 判断剩下的字节数,是否会有粘包的情况 sizeNow = socket->bytesAvailable(); qDebug() << "剩余等待读取的传入字节数 sizeNow=" << sizeNow; }while(sizeNow > 0);}void Widget::on_btnSend_clicked(){ QString qstrSend = ui->tedtInput->toPlainText(); if(qstrSend.isEmpty()) return; QString input = "TXT:"+ qstrSend; QByteArray data = input.toLocal8Bit(); // 封装数据包,加上包头 QByteArray dataSend; // 封装的数据包 QDataStream stream(&dataSend,QIODevice::WriteOnly); // 定义一个数据流 // 写入数据到数据流,格式为:数据包大小(32bit)+数据 stream << (quint32)0 << data; // 移到流索引为0处, 用数据大小覆盖 stream.device()->seek(0); stream << dataSend.size(); tcpSocket.write(dataSend); ui->tedtInput->clear();}void Widget::on_btnImage_clicked(){ QString imagePath = QFileDialog::getOpenFileName(this,"请选择一张图片",".","Images (*.png *.xpm *.jpg)"); if(imagePath.isEmpty()) return; QFile file(imagePath); file.open(QIODevice::ReadOnly); QByteArray data = "IMG:" + file.readAll(); file.close(); // 封装数据包,加上包头 QByteArray dataSend; // 封装的数据包 QDataStream stream(&dataSend,QIODevice::WriteOnly); // 定义一个数据流 // 写入数据到数据流,格式为:数据包大小(32bit)+数据 stream << (quint32)0 << data; // 移到流索引为0处, 用数据大小覆盖 stream.device()->seek(0); stream << dataSend.size(); tcpSocket.write(dataSend);}