一、前言
本文主要讲讲QT中服务器与客户端的使用方法,QT已经封装好了,调用相应类直接访问即可。本文以QT中的QT中的TCP为例子,讲下使用方法以及线程中使用。
二、正文
2.1 Sever的使用方法
2.1.1 思路
QT中Sever使用的时候大致步骤为:
1、创建监听的服务器对象
2、监听到数据信号,创建QTcpSocket的套接字对象
3、检测是否可以接收数据
4、信息发送,查看数据发送是否正常
总的来说,就是先确定要监听的地址与端口,然后数据在套接字中,套接字在QT中是QTcpSocket
封装的类,直接用就行,最后通过套接字接收数据或者发送数据即可。
2.1.2 代码示例
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QTcpServer> #include <QTcpSocket> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: Ui::Widget *ui; QTcpServer *m_server; QTcpSocket *m_tcp; }; #endif // WIDGET_H
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->lineEdit_port->setText("8888"); //创建监听的服务器对象 m_server = new QTcpServer(this); connect(ui->pushButton_listen, &QPushButton::clicked, this, [=](){ m_server->listen(QHostAddress::Any, ui->lineEdit_port->text().toInt()); ui->pushButton_listen->setDisabled(true); }); //监听到数据信号,创建QTcpSocket的套接字对象 connect(m_server, &QTcpServer::newConnection, this, [=](){ m_tcp = m_server->nextPendingConnection(); //检测是否可以接收数据 connect(m_tcp, &QTcpSocket::readyRead, this, [=](){ QByteArray data = m_tcp->readAll(); ui->textEdit_historymsg->append("客户端说: " + data); }); }); //信息发送 connect(ui->pushButton_send, &QPushButton::clicked, this, [=](){ QString msg = ui->textEdit_sendmsg->toPlainText(); m_tcp->write(msg.toUtf8()); ui->textEdit_historymsg->append("服务器说: " + msg); }); } Widget::~Widget() { delete ui; }
2.1.3 细节讲解
创建服务器对象QTcpServer,对象有监听的方法listen。
监听的地址设置所有即可访问你电脑可以收到的所有地址,可以用win+R进入cmd查看有哪些,比如:
获取到新的连接后,将下一个挂起的连接作为已连接的QTcpSocket对象返回
准备读取信号有了后即可开始读取套接字内的数据。
2.1.4 演示
监听成功
2.2 Client的使用方法
2.2.1 思路
QT中Client使用的时候大致步骤为:
1、创建Socket对象,连接服务器
2、数据交互
3、根据需求断开连接
总体来说很简单,套接字连接好服务器的地址与端口即可,前提是服务器开启了,不然连接会失败。
2.2.2 代码示例
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QTcpSocket> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: Ui::Widget *ui; QTcpSocket *m_socket; }; #endif // WIDGET_H
#include "widget.h" #include "ui_widget.h" #include <QHostAddress> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->lineEdit_address->setText("192.168.2.24"); ui->lineEdit_connect->setText("8888"); //创建Socket对象,连接服务器 m_socket = new QTcpSocket(this); connect(ui->pushButton_connect, &QPushButton::clicked, this, [=](){ m_socket->connectToHost(QHostAddress(ui->lineEdit_address->text()), ui->lineEdit_connect->text().toInt()); ui->pushButton_connect->setDisabled(true); ui->pushButton_close->setEnabled(true); }); //断开连接 connect(ui->pushButton_close, &QPushButton::clicked, this, [=](){ m_socket->close(); ui->pushButton_connect->setEnabled(true); ui->pushButton_close->setDisabled(true); }); //数据交互 connect(m_socket, &QTcpSocket::connected, this, [=](){ ui->textEdit_history->append("服务器连接成功!!!!"); }); connect(m_socket, &QTcpSocket::readyRead, this, [=](){ ui->textEdit_history->append("服务器说: " + m_socket->readAll()); }); connect(m_socket, &QTcpSocket::disconnected, this, [=](){ ui->textEdit_history->append("服务器断开连接!!!!"); }); connect(ui->pushButton_send, &QPushButton::clicked, this, [=](){ m_socket->write(ui->textEdit_send->toPlainText().toUtf8()); ui->textEdit_history->append("客户端说: " + ui->textEdit_send->toPlainText().toUtf8()); }); } Widget::~Widget() { delete ui; }
2.2.3 细节讲解
没啥可讲的,QT都封装好了,连接好就能直接用,比串口的请求响应简单方便多了。
2.2.4 演示
注意,先开启服务器,然后才能使用,局域网可以内部使用,外部使用需要申请IP。
2.3 线程中的使用
在实际项目中,数据的部分大多是写进线程的,关于线程的了解在前文中有讲述,需要的朋友可以自行查阅。
下面以文件从客户端传到服务器为例:
2.3.1 代码示例
2.3.1.1 Sever
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTcpServer> 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_pushButton_listen_clicked(); private: Ui::MainWindow *ui; QTcpServer* m_server; }; #endif // MAINWINDOW_H
#ifndef ACCEPTFILE_H #define ACCEPTFILE_H #include <QObject> #include <QThread> #include <QTcpSocket> class acceptfile : public QThread { Q_OBJECT public: explicit acceptfile(QTcpSocket* tcp, QThread *parent = nullptr); signals: void over(); protected: void run() override; private: QTcpSocket* m_tcp; }; #endif // ACCEPTFILE_H
#include "mainwindow.h" #include "ui_mainwindow.h" #include "acceptfile.h" #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); //创建服务器对象 m_server = new QTcpServer(this); //创建套接字 connect(m_server, &QTcpServer::newConnection, this, [=](){ QTcpSocket* tcp = m_server->nextPendingConnection(); //创建子线程 acceptfile* subthread = new acceptfile(tcp); subthread->start(); //释放资源 connect(subthread, &acceptfile::over, this, [=](){ subthread->quit(); subthread->wait(); subthread->deleteLater(); QMessageBox::information(this, "文件接收", "文件接收完毕!!!!!!!"); }); }); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_listen_clicked() { m_server->listen(QHostAddress::Any, ui->lineEdit_port->text().toUShort()); }
#include "acceptfile.h" #include <QFile> acceptfile::acceptfile(QTcpSocket* tcp, QThread *parent) : QThread(parent) { m_tcp = tcp; } void acceptfile::run() { //创建文件对象,保存客户端发送过来的文件内容 QFile* file = new QFile("C:/Users/EDY/Desktop/QTtest/recv.txt"); file->open(QFile::WriteOnly); //读取套接字socket的内容 connect(m_tcp, &QTcpSocket::readyRead, this, [=](){ static int total = 0; static int count = 0; if(count == 0) { m_tcp->read((char*)&total, 4); } //读出剩余数据 QByteArray all = m_tcp->readAll(); count += all.size(); file->write(all); //判断是否接收完毕 if(total == count) { m_tcp->close(); m_tcp->deleteLater(); file->close(); file->deleteLater(); emit over(); } }); //进入事件循环 exec(); }
2.3.1.2 Client
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThread> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); signals: void startconnect(unsigned short, QString); void sendFile(QString path); private slots: void on_connectserver_btn_clicked(); void on_file_btn_clicked(); void on_send_btn_clicked(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
#ifndef SENDFILE_H #define SENDFILE_H #include <QObject> #include <QTcpSocket> class sendfile : public QObject { Q_OBJECT public: explicit sendfile(QObject *parent = nullptr); //连接服务器 void connectServer(unsigned short address, QString ip); //发送文件 void sendFile(QString path); signals: void connectOK(); void gameover(); void curpercent(int); private: QTcpSocket* m_tcp; }; #endif // SENDFILE_H
#include "mainwindow.h" #include "ui_mainwindow.h" #include "sendfile.h" #include <QFileDialog> #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); ui->port->setText("8888"); ui->IP->setText("192.168.2.24"); ui->progressBar->setRange(0, 100); ui->progressBar->setValue(0); //创建线程对象 QThread* thrd = new QThread; //创建任务 sendfile* worker = new sendfile; //将work放入子线程 worker->moveToThread(thrd); //连接服务器 connect(this, &MainWindow::startconnect, worker, &sendfile::connectServer); //连接文件发送 connect(this, &MainWindow::sendFile, worker, &sendfile::sendFile); //处理连接服务器的子线程数据 connect(worker, &sendfile::connectOK, this, [=](){ QMessageBox::information(this, "连接服务器", "连接成功!!!!"); }); connect(worker, &sendfile::gameover, this, [=](){ //资源释放 thrd->quit(); thrd->wait(); worker->deleteLater(); thrd->deleteLater(); }); //处理选择文件发送子线程数据 connect(worker, &sendfile::curpercent, ui->progressBar, &QProgressBar::setValue); //线程开始 thrd->start(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_connectserver_btn_clicked() { QString ip = ui->IP->text(); unsigned short port = ui->port->text().toUShort(); emit startconnect(port, ip); } void MainWindow::on_file_btn_clicked() { QString path = QFileDialog::getOpenFileName(); if(path.isEmpty()) { QMessageBox::warning(this, "打开文件", "选择的文件路径不能为空!!"); return; } ui->file->setText(path); } void MainWindow::on_send_btn_clicked() { emit sendFile(ui->file->text()); }
#include "sendfile.h" #include <QFile> #include <QFileInfo> sendfile::sendfile(QObject *parent) : QObject(parent) { } void sendfile::connectServer(unsigned short address, QString ip) { m_tcp = new QTcpSocket; m_tcp->connectToHost(ip, address); connect(m_tcp, &QTcpSocket::connected, this, &sendfile::connectOK); connect(m_tcp, &QTcpSocket::disconnected, this, [=](){ m_tcp->close(); m_tcp->deleteLater(); emit gameover(); }); } void sendfile::sendFile(QString path) { QFile file(path); QFileInfo info(path); int filesize = info.size(); file.open(QFile::ReadOnly); while (!file.atEnd()) { static int num = 0; if(num == 0) { m_tcp->write((char*)&filesize, 4); } QByteArray line = file.readLine(); num += line.size(); int percent = (num * 100 / filesize); emit curpercent(percent); m_tcp->write(line); } }
2.3.2 代码讲解
没啥好讲的,可以看看代码,写得很详细,文件传输部分跟普通数据也是一样的,区别就是需要用到文件的类QFile和QFileInfo,一个是文件对象,一个是文件信息对象。简单来说指文件是QFile,指文件的具体数据是QFileInfo。源码已上传
2.3.3 演示
QTcp服务器与客户端