前言

前面的博客有写过如果使用TCP搭建一个客户端与服务器,连接并互发信息,这里主是演示,如何把客户端的图像发往服务器,服务器得到图像后,按指令做不同的处理,并返回给客户端处理之后的结果,客户端只负责打开和发送图像,所有关于图像的图像的处理,比如灰度图像,人脸检测啊,都经过服务器处理之后返回给客户端。而服务器用来处理图像的是OpenCV库,
代码注释比较多,具体流程可以看源码,就应该了解。

代码

1.客户端
client.h

#ifndef CLIENT_H
#define CLIENT_H#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>
#include <QFile>
#include  <QFileDialog>
#include <QCompleter>
#include <QBuffer>
#include <QDebug>
#include <QTextEdit>
#include <QImageReader>
#include <QTextCursor>
#include <QColor>QT_BEGIN_NAMESPACE
namespace Ui { class Client; }
QT_END_NAMESPACEclass Client : public QMainWindow
{Q_OBJECTpublic:Client(QWidget *parent = nullptr);~Client();void initUI();void insertImage(QTextEdit *ui_text, QImage &image);void transformImage(QImage &image,qint64 mask);QByteArray getImageData(const QImage &image);void transformImage(QImage &image, QByteArray &block,int mask);QImage getImage(const QString &data);QByteArray imageToBuffer(QImage &image);private slots:void on_buttonConnect_clicked();//连接服务器void on_buttonDisconnect_clicked();//断开连接void on_buttonOpenImage_clicked();//打开图像void readServerMessage();//读取消息void on_buttonFaceDetection_clicked();//人脸检测void on_buttonGaryImage_clicked();//灰度图像void on_buttonCannyImage_clicked();//边缘检测
private:QTcpSocket *tcpClient;QImage image;Ui::Client *ui;QByteArray outBlock;  //数据缓冲区,即存放每次要发送的数据块int totalBytes;    // 发送数据的总大小int mask;QString fileName;int imageSize;int bytesReceived;
};
#endif // CLIENT_H

client.cpp

#include "client.h"
#include "ui_client.h"Client::Client(QWidget *parent): QMainWindow(parent), ui(new Ui::Client)
{ui->setupUi(this);initUI();tcpClient = new QTcpSocket(this);//取消原有连接tcpClient->abort();bytesReceived = 0;imageSize = 0;connect(tcpClient, SIGNAL(readyRead()), this, SLOT(readServerMessage()));
}//UI界面相关
void Client::initUI()
{ui->buttonDisconnect->setEnabled(false);
}Client::~Client()
{delete ui;
}//连接服务器
void Client::on_buttonConnect_clicked()
{tcpClient->connectToHost(ui->lineEditServerIP->text(),ui->lineEditServerPorts->text().toInt());if(tcpClient->waitForConnected(1000)){ui->buttonConnect->setEnabled(false);ui->buttonDisconnect->setEnabled(true);ui->textEditStatus->append("连接服务器成功!");}else{ui->textEditStatus->append("连接失败,请检查IP地址和端口!");}
}//断开
void Client::on_buttonDisconnect_clicked()
{tcpClient->disconnectFromHost();//断开成功if (tcpClient->state() == QAbstractSocket::UnconnectedState || tcpClient->waitForDisconnected(1000)){ui->buttonConnect->setEnabled(true);ui->textEditStatus->append("连接已断开!");ui->buttonDisconnect->setEnabled(false);ui->buttonConnect->setEnabled(true);}else{ui->textEditStatus->append("无法断开与服务器的连接!");}
}void Client::on_buttonFaceDetection_clicked()
{}void Client::on_buttonGaryImage_clicked()
{transformImage(image,outBlock,10);if(image.isNull()){ui->textEditStatus->append("当前图像不能发送!");return;}if(outBlock.size() != 0){tcpClient->write(outBlock);ui->textEditStatus->append("图像发送成功!");ui->textEditInput->clear();}
}void Client::on_buttonCannyImage_clicked()
{transformImage(image,outBlock,9);if(image.isNull()){ui->textEditStatus->append("当前图像不能发送!");return;}if(outBlock.size() != 0){tcpClient->write(outBlock);ui->textEditStatus->append("图像发送成功!");ui->textEditInput->clear();}
}//接收服务器端的信息并显示
void Client::readServerMessage()
{QDataStream in(tcpClient);in.setVersion(QDataStream::Qt_5_7);QImage imageData;QString imageContent;// 如果已接收到的数据小于16个字节,保存到文件头结构if (bytesReceived <= sizeof(int)*3){if((tcpClient->bytesAvailable() >= sizeof(int)*3)&& (imageSize == 0)){// 接收数据总大小信息和文件名大小信息in >>mask>> totalBytes  >> imageSize;bytesReceived += sizeof(int) * 3;}if((tcpClient->bytesAvailable() >= imageSize) && (imageSize != 0)){// 接收文件,并建立文件in >> imageContent;imageData = getImage(imageContent);if(imageData.isNull()){ui->textEditStatus->append("没有接收图像数据!");}insertImage(ui->textEditAccept,imageData);bytesReceived += imageSize;if(bytesReceived == totalBytes){ui->textEditStatus->append("接收文件成功");totalBytes = 0;bytesReceived = 0;imageSize = 0;}}}
}//QTextEdit显示图像
void Client::insertImage(QTextEdit *ui_text_edit, QImage &image)
{QUrl Uri;QTextDocument * textDocument = ui_text_edit->document();textDocument->addResource( QTextDocument::ImageResource, Uri, QVariant ( image ) );QTextCursor cursor = ui_text_edit->textCursor();QTextImageFormat imageFormat;imageFormat.setWidth( image.width() );imageFormat.setHeight( image.height() );cursor.insertImage(imageFormat);}//打开图像按键
void Client::on_buttonOpenImage_clicked()
{fileName = QFileDialog::getOpenFileName(this);if (!fileName.isEmpty()){image = QImage(fileName);if(image.isNull()){ui->textEditStatus->append("打开图像失败!");return;}else{QBuffer buffer(&outBlock);buffer.open(QIODevice::WriteOnly);image.save(&buffer, "JPG");insertImage(ui->textEditInput,image);ui->textEditStatus->append("打开图像成功!");}}else{ui->textEditStatus->append("打开路径失败,请确定是否是图像路径!");return;}}//图像转换
/*QImage &image 输入图像*QByteArray &block 输出流*int mask 指令
*/
void Client::transformImage(QImage &image, QByteArray &block,int mask)
{int total_size = 0;QDataStream sendOut(&block, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_5_7);//获得图片数据QString imageData = getImageData(image);// 保留总大小信息空间、图像大小信息空间,然后输入图像信息sendOut << int(0) << int(0) << int(0) << imageData;// 这里的总大小是总大小信息、图像大小信息和实际图像信息的总和total_size += block.size();sendOut.device()->seek(0);int image_size = int((block.size() - sizeof(int)*3));// 返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间sendOut << mask <<  total_size << image_size;
}//图像转Base 64
QByteArray Client::getImageData(const QImage &image)
{QByteArray imageData;//开缓冲区QBuffer buffer(&imageData);//存入缓冲区image.save(&buffer, "jpg");//转成Base64imageData = imageData.toBase64();return imageData;
}//Base64 转图像
QImage Client::getImage(const QString &data)
{QByteArray imageData = QByteArray::fromBase64(data.toLatin1());QImage image;image.loadFromData(imageData);return image;
}

2.服务器端
server.h

#ifndef SERVER_H
#define SERVER_H#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QMessageBox>
#include <QFileDialog>
#include <iostream>
#include <QBuffer>
#include <opencv2/opencv.hpp>
#include <QTextEdit>
#include <QFile>QT_BEGIN_NAMESPACE
namespace Ui { class Server; }
QT_END_NAMESPACEclass Server : public QMainWindow
{Q_OBJECTpublic:Server(QWidget *parent = nullptr);~Server();void initUI();//界面初始化QImage base64ToQImage(const QString &data);//base64转QImagevoid insertImage(QTextEdit *ui_text_edit, QImage &image);//QTextEdit显示图像cv::Mat QImage2cvMat(QImage &image);//QImage转MatQImage cvMat2QImage(const cv::Mat& mat);//Mat转QImagevoid transformImage(QImage &image, QByteArray &block,int mask);//封装发送数据QByteArray QImageToBase64(const QImage &image);//QImage转Base64void edgeDetection(cv::Mat &cv_src,cv::Mat &cv_dst);private:QTcpServer *tcpServer;//服务器类QList<QTcpSocket*> tcpClient;QTcpSocket *currentClient;int mask,total_size;int total_bytes;int bytes_received;int image_size;QString image_content;QByteArray out_block;  //数据缓冲区,即存放每次要发送的数据块QImage image_data;QString file_name;QImage image;QByteArray buffer_all;private slots:void on_buttonMonitor_clicked();//监听事件void on_buttonDisconnect_clicked();//断开事件void newConnectionSlot();//新客户端连接事件void readMessageData();//接收信息void on_buttonOpenImage_clicked();//打开图像void on_buttonSendMessage_clicked();//发送图像private:Ui::Server *ui;
};
#endif // SERVER_H

server.cpp

#include "server.h"
#include "ui_server.h"Server::Server(QWidget *parent): QMainWindow(parent), ui(new Ui::Server)
{ui->setupUi(this);initUI();tcpServer = new QTcpServer(this);image_size = 0;total_bytes = 0;bytes_received = 0;//有新的连接时的槽函数connect(tcpServer,SIGNAL(newConnection()),this, SLOT(newConnectionSlot()));
}void Server::initUI()
{ui->buttonDisconnect->setEnabled(false);
}Server::~Server()
{delete ui;
}//启动服务器开始监听
void Server::on_buttonMonitor_clicked()
{//监听所有IP地址bool monitor = tcpServer->listen(QHostAddress::Any,ui->lineEditServerPorts->text().toInt());//按键状态if(monitor){ui->buttonMonitor->setEnabled(false);ui->buttonDisconnect->setEnabled(true);ui->textEditStatus->append("开始监端口......");}else{ui->textEditStatus->append("启动服务器失败!");}
}//有新客户端连接时
void Server::newConnectionSlot()
{//返回套接字指针currentClient = tcpServer->nextPendingConnection();tcpClient.append(currentClient);ui->comboBoxIP->addItem(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1])\.arg(currentClient->peerPort()));//读取消息处理connect(currentClient, SIGNAL(readyRead()), this, SLOT(readMessageData()));
}//断开与所有客户端的连接
void Server::on_buttonDisconnect_clicked()
{//如果有客户端连接if(tcpClient.length() > 0){for(int i = 0; i < tcpClient.length();i++){//断开tcpClient.at(i)->disconnectFromHost();//等待bool dis = tcpClient.at(i)->waitForDisconnected(1000);if(dis){//删除客户端tcpClient.removeAt(i);tcpServer->close();ui->buttonMonitor->setEnabled(true);ui->buttonDisconnect->setEnabled(false);ui->textEditStatus->append("断开连接成功!");ui->buttonMonitor->setEnabled(true);}else{ui->textEditStatus->append("断开连接失败!");}}}else{ui->textEditStatus->append("当前没有连接的客户端!");ui->buttonMonitor->setEnabled(true);}
}//接收消息并显示到界面
void Server::readMessageData()
{//由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端//遍历所有连接的客户端for(int i=0; i<tcpClient.length(); i++){QDataStream in(tcpClient.at(i));// 如果已接收到的数据小于16个字节,保存到文件头结构if (bytes_received <= sizeof(int)*3){if((currentClient->bytesAvailable() >= sizeof(int)*3)&& (image_size == 0)){// 接收数据总大小信息和文件名大小信息in >>mask>>total_bytes  >> image_size;bytes_received += sizeof(int) * 3;ui->textEditStatus->append("开始接收图像......");}if((currentClient->bytesAvailable() >= image_size) && (image_size != 0)){// 接收文件,并建立文件in >> image_content;image_data = base64ToQImage(image_content);insertImage(ui->textEditAccept,image_data);bytes_received += image_size;if(!image_data.isNull()){ui->textEditStatus->append("接收文件成功");//判断转过来的指令,对图像进行处理switch (mask){case 9:{QByteArray out;//使用opencv处理图像cv::Mat cv_src = QImage2cvMat(image_data);cv::Mat cv_dst;edgeDetection(cv_src,cv_dst);QImage qt_image = cvMat2QImage(cv_dst);//处理完成之后返回给客户端transformImage(qt_image,out,9);//返回给之前发送信息的客户端tcpClient.at(i)->write(out);ui->textEditStatus->append("处理并返回图像成功!");mask = 0;break;}case 10:{QByteArray out;//使用opencv处理图像cv::Mat cv_src = QImage2cvMat(image_data);cv::Mat cv_dst;cv::cvtColor(cv_src,cv_dst,cv::COLOR_BGR2GRAY);QImage qt_image = cvMat2QImage(cv_dst);//处理完成之后返回给客户端transformImage(qt_image,out,10);//返回给之前发送信息的客户端tcpClient.at(i)->write(out);ui->textEditStatus->append("处理并返回图像成功!");mask = 0;break;}}}if(bytes_received == total_bytes){total_bytes = 0;bytes_received = 0;image_size = 0;}}}}
}void Server::edgeDetection(cv::Mat &cv_src, cv::Mat &cv_dst)
{cv::Mat cv_gray,cv_edge;cv::cvtColor(cv_src,cv_gray,cv::COLOR_BGR2GRAY);cv::blur(cv_gray, cv_gray, cv::Size(3, 3));//调用Canny算子cv::Canny(cv_edge, cv_dst, 10, 30, 3);
}//Base64转QImage
QImage Server::base64ToQImage(const QString &data)
{QByteArray imageData = QByteArray::fromBase64(data.toLatin1());QImage image;image.loadFromData(imageData);return image;
}//QTextEdit显示图像
void Server::insertImage(QTextEdit *ui_text_edit, QImage &image)
{QUrl Uri;QTextDocument * textDocument = ui_text_edit->document();textDocument->addResource( QTextDocument::ImageResource, Uri, QVariant ( image ) );QTextCursor cursor = ui_text_edit->textCursor();QTextImageFormat imageFormat;imageFormat.setWidth( image.width() );imageFormat.setHeight( image.height() );cursor.insertImage(imageFormat);}// QImage转换成cv::Mat
cv::Mat Server::QImage2cvMat(QImage &image)
{cv::Mat mat;switch (image.format()){case QImage::Format_ARGB32:case QImage::Format_RGB32:case QImage::Format_ARGB32_Premultiplied:mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());break;case QImage::Format_RGB888:mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);break;case QImage::Format_Indexed8:mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());break;}return mat;
}// cv::Mat转换成QImage
QImage Server::cvMat2QImage(const cv::Mat& mat)
{if (mat.type() == CV_8UC1)                          // 单通道{QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);image.setColorCount(256);                       // 灰度级数256for (int i = 0; i < 256; i++){image.setColor(i, qRgb(i, i, i));}uchar *pSrc = mat.data;                         // 复制mat数据for (int row = 0; row < mat.rows; row++){uchar *pDest = image.scanLine(row);memcpy(pDest, pSrc, mat.cols);pSrc += mat.step;}return image;}else if (mat.type() == CV_8UC3)                     // 3通道{const uchar *pSrc = (const uchar*)mat.data;     // 复制像素QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);    // R, G, B 对应 0,1,2return image.rgbSwapped();                      // rgbSwapped是为了显示效果色彩好一些。}else if (mat.type() == CV_8UC4)                     // 4通道{const uchar *pSrc = (const uchar*)mat.data;     // 复制像素QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);        // B,G,R,A 对应 0,1,2,3return image.copy();}else{return QImage();}
}//转换封装图像数据
void Server::transformImage(QImage &image, QByteArray &block,int mask)
{int total = 0;QDataStream sendOut(&block, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_5_7);//获得图片数据QString imageData = QImageToBase64(image);// 保留总大小信息空间、图像大小信息空间,然后输入图像信息sendOut << int(0) << int(0) << int(0) << imageData;// 这里的总大小是总大小信息、图像大小信息和实际图像信息的总和total += block.size();sendOut.device()->seek(0);// 返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间sendOut << mask <<  total << int((block.size() - sizeof(int)*3));
}QByteArray Server::QImageToBase64(const QImage &image)
{QByteArray imageData;//开缓冲区QBuffer buffer(&imageData);//存入缓冲区image.save(&buffer, "jpg");//转成Base64imageData = imageData.toBase64();return imageData;
}//打开图像
void Server::on_buttonOpenImage_clicked()
{file_name = QFileDialog::getOpenFileName(this);if (!file_name.isEmpty()){image = QImage(file_name);if(image.isNull()){ui->textEditStatus->append("打开图像失败!");return;}else{insertImage(ui->textEditInput,image);ui->textEditStatus->append("打开图像成功!");ui->buttonSendMessage->setEnabled(true);}}else{ui->textEditStatus->append("打开路径失败,请确定是否是图像路径!");return;}
}
void Server::on_buttonSendMessage_clicked()
{transformImage(image_data,out_block,10);//如果选择全部发送信息if(ui->comboBoxIP->currentIndex() == 0){for(int i = 0; i < tcpClient.length(); i++){tcpClient.at(i)->write(out_block);ui->textEditStatus->append("信息发送成功!");ui->textEditInput->clear();}}//指定接收的客户端/*else{//得到选择的IP地址QString client_IP = ui->comboBoxIP->currentText().split(":").at(0);//得到端口int client_port = ui->comboBoxIP->currentText().split(":").at(1).toInt();//遍历连接到的客户端for(int i = 0; i < tcpClient.length(); i++){if(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]==client_IP\&& tcpClient[i]->peerPort()==client_port){tcpClient.at(i)->write(input_data.toLatin1());ui->textEditStatus->append("发送信息到:"+client_IP+"成功!");//ui->textEditInput->clear();input_data.clear();return; //ip:port唯一,无需继续检索}}}*/}

3.运行效果

Qt网络编程——使用OpenCV与TCP搭建图像处理服务器相关推荐

  1. Qt网络编程小项目-基于Tcp协议的文件传输项目

    目录 项目描述 Qt下Tcp服务器端和客户端流程: 具体流程: 客户端: 服务器端: 源码: 服务器端: 服务器头文件: 服务器源文件: 服务器端ui 客户端: 客户端头文件: 客户端源文件: 客户端 ...

  2. UNIX网络编程——使用线程的TCP回射服务器程序

    同一进程内的所有线程除了共享全局变量外还共享: (1)进程指令: (2)大多数数据: (3)  打开的文件(即描述符): (4)信号处理函数和信号处置: (5)当前工作目录: (6)用户ID和组ID. ...

  3. qt android 网络编程实例,QT网络编程Tcp下C/S架构的即时通信实例

    先写一个客户端,实现简单的,能加入聊天,以及加入服务器的界面. #ifndef TCPCLIENT_H #define TCPCLIENT_H #include #include #include # ...

  4. Qt网络编程——TCP

    Qt网络编程--TCP 1. 概念 2. 服务器 3. 客户端 4. TCP服务器和客户端互传文件 5. 资源下载 1. 概念 TCP(Transmission Control Protocol, 传 ...

  5. Qt网络编程电子白板

    Qt网络编程电子白板 2018-08-11 08:44:04 天行健_地势坤 阅读数 915更多 分类专栏: Qt 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

  6. 第十四章:Qt网络编程

    回顾: 第一章:Qt的概述 第二章:在Ubuntu编写第一个Qt程序 第三章:Qt的字符串和字符编码 第四章:Qt的信号和槽 第五章:Qt容器窗口(父窗口) 第六章:面向对象的Qt编程 第七章:Qt设 ...

  7. Qt网络编程概述(一)

    分享主题 Qt网络编程概述(一) Qt网络编程之QTCPSocket和QTCPServer实例(二) Qt网络编程之QUdpSocket实例(三) Qt网络编程概述 QtNetWork模块提供了若干类 ...

  8. QT网络编程开发服务端

    下一篇: QT网络编程开发客户端 文章目录 基于Qt的网络编程服务端 QTcpServer 配置 listen() close() newConnection() SINGL readyRead() ...

  9. QT:Qt 网络编程基础

    Qt 网络编程基础 1 )网络协议层次 // linux/unix ---> 4 层  OSI ---> 7 层  应用层(应用层.表示层.会话层)  传输层(传输层) // TCP/IP ...

最新文章

  1. 贪心 ---- Educational Codeforces Round 90 (Rated for Div. 2)E. Sum of Digits[数位贡献+思维题+贪心]
  2. 大开源时代,“仁慈的独裁者”管理模式还走得通吗?
  3. 网络编程中的缓冲区溢出
  4. java 线程池 资源回收_JAVA线程池资源回收的问题
  5. 运用cnn实现手写体(mnist)数字识别_实现 MNIST 手写数字识别
  6. 跟着 Microsoft 跑!
  7. 比 matplotlib 效率高十倍的数据可视化神器
  8. winsock2.h与ws2def.h等文件大量报错相关问题解决方法
  9. 北京云计算HCIE培训机构入门技术快速了解laaS、Paas和 SaaS的区别-ielab网络实验室
  10. 计算圆柱的侧面积及体积
  11. 红帽linux创建c文件,RedHat成功运行的第一个C程序全过程(适合新手)
  12. cipher加密解密
  13. [源码和文档分享]基于JAVA的葫芦娃救爷爷游戏
  14. 习题 3.6 请编程序将China译成密码,密码规律是:用原来的字母后面第4个字母代替原来的字母。
  15. 服务器过载保护(上篇)——过载介绍
  16. 互联网日报 | 7月21日 星期四 | 脉脉CEO再回应点评招聘评论真实性;​微信版本再更新;上半年前十位SUV品牌销量排名出炉...
  17. 教育类型网站用户体验分析——以UMU学习平台、学生安全教育平台、师路南通为例...
  18. MFC的PNG图片按钮
  19. Event-B建模(三)——控制桥上汽车,初始模型
  20. google外链重要性高吗?谷歌外链作用大不大

热门文章

  1. 哨兵机器人钢力士_还记得秒杀X战警的哨兵机器人吗?在漫威原著里,X战警更憋屈...
  2. android冒烟测试自动化,自动化冒烟测试脚本应当遵循的原则
  3. 安卓linux终端termux下载,高级手机终端app
  4. 【数学和算法】初识卡尔曼滤波器(一)
  5. Vue.js 自定义指令
  6. java 8大happen-before原则超全面详解
  7. sql必知必会(第四版) 学习笔记二 视图
  8. 彻底解决Git中文乱码问题
  9. 在Spring MVC中使用Apache Shiro安全框架
  10. Java并发编程(3):线程挂起、恢复与终止的正确方法(含代码)