zoukankan      html  css  js  c++  java
  • QT学习笔记(12) QT下的TCP通信

    一、TCP通信过程

    (1)服务器端:

      服务器端有QTcpServer的监听套接字,运用listen()方法监听网卡的ip和端口。

      如果有新的连接传过来,并且连接成功,服务器会触发newConnection(),通过槽函数取出连接过来的通信套接字QTcpSocket

      如果有数据成功传送过来,对方的通信套接字QTcpSocket会触发readyRead(),通过槽函数可以对接收的数据进行处理

    (2)客户端

      首先根据ip和端口,通过通信套接字QTcpSocket的connectToHost()方法主动和服务器建立连接

      如果连接成功,通信套接字QTcpSocket会自动触发connected(),通过槽函数可以进行操作

      如果有数据成功传送过来,对方的通信套接字QTcpSocket会触发readyRead(),通过槽函数可以对接收的数据进行处理

        

    二、实例代码如下:

    QT_HelloWorld11.pro

     1 #-------------------------------------------------
     2 #
     3 # Project created by QtCreator 2017-08-30T21:18:55
     4 #
     5 #-------------------------------------------------
     6 
     7 QT       += core gui network #添加network模块
     8 
     9 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    10 
    11 TARGET = QT_HelloWorld11
    12 TEMPLATE = app
    13 
    14 
    15 SOURCES += main.cpp
    16         serverwidget.cpp 
    17     clientwidget.cpp
    18 
    19 HEADERS  += serverwidget.h 
    20     clientwidget.h
    21 
    22 FORMS    += serverwidget.ui 
    23     clientwidget.ui
    24 
    25 CINFIG += C++11

    main.cpp

     1 #include "serverwidget.h"
     2 #include <QApplication>
     3 //包含头文件
     4 #include "clientwidget.h"
     5 
     6 
     7 int main(int argc, char *argv[])
     8 {
     9     QApplication a(argc, argv);
    10     //显示服务器窗口
    11     ServerWidget w;
    12     w.show();
    13 
    14     //显示客户端窗口
    15     ClientWidget w2;
    16     w2.show();
    17 
    18     return a.exec();
    19 }

    serverwidget.h

     1 #ifndef SERVERWIDGET_H
     2 #define SERVERWIDGET_H
     3 
     4 #include <QWidget>
     5 #include <QTcpServer>//监听套接字
     6 #include <QTcpSocket>//通信套接字(建立好连接的套接字)
     7 
     8 namespace Ui {
     9 class ServerWidget;
    10 }
    11 
    12 class ServerWidget : public QWidget
    13 {
    14     Q_OBJECT
    15 
    16 public:
    17     explicit ServerWidget(QWidget *parent = 0);
    18     ~ServerWidget();
    19 
    20 private slots:
    21     void on_button_send_clicked();
    22 
    23     void on_button_close_clicked();
    24 
    25 private:
    26     Ui::ServerWidget *ui;
    27 
    28     QTcpServer * tcpServer;//监听套接字
    29     QTcpSocket * tcpSocket;//通信套接字
    30 
    31 };
    32 
    33 #endif // SERVERWIDGET_H

    clientwidget.h

     1 #ifndef CLIENTWIDGET_H
     2 #define CLIENTWIDGET_H
     3 
     4 #include <QWidget>
     5 #include <QTcpSocket>//通信套接字
     6 
     7 namespace Ui {
     8 class ClientWidget;
     9 }
    10 
    11 class ClientWidget : public QWidget
    12 {
    13     Q_OBJECT
    14 
    15 public:
    16     explicit ClientWidget(QWidget *parent = 0);
    17     ~ClientWidget();
    18 
    19 private slots:
    20     void on_pushButton_connect_clicked();
    21 
    22     void on_pushButton_send_clicked();
    23 
    24     void on_pushButton_close_clicked();
    25 
    26 private:
    27     Ui::ClientWidget *ui;
    28 
    29     QTcpSocket *tcpSocket;
    30 };
    31 
    32 #endif // CLIENTWIDGET_H

    serverwidget.cpp

     1 #include "serverwidget.h"
     2 #include "ui_serverwidget.h"
     3 #include <QTcpServer>//监听套接字
     4 #include <QTcpSocket>//通信套接字(建立好连接的套接字)
     5 
     6 ServerWidget::ServerWidget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::ServerWidget)
     9 {
    10     ui->setupUi(this);
    11 
    12     setWindowTitle(QString::fromLocal8Bit("服务器:8888"));
    13 
    14     tcpServer = NULL;//先赋值为空,在后面做空值判断,防止空指针的错误
    15     tcpSocket = NULL;
    16 
    17     //实例化 监听套接字
    18     tcpServer = new QTcpServer(this);//指定父对象,让其自动回收空间
    19     //监听
    20     tcpServer->listen(QHostAddress::Any,8888);//默认绑定当前网卡上的所有ip
    21 
    22     //如果有新的连接传过来,并连接成功,服务器触发newConnection()方法
    23     connect(tcpServer,&QTcpServer::newConnection,
    24             [=]()
    25             {
    26                 //取出建立好连接的套接字
    27                 tcpSocket = tcpServer->nextPendingConnection();//取出当前最近的一次连接的套接字
    28                 //获取对方(客户端)的IP和端口
    29                 QString ip = tcpSocket->peerAddress().toString();
    30                 qint16 port = tcpSocket->peerPort();
    31                 QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);//组包
    32                 //在服务器端显示
    33                 ui->textEdit_read->setText(temp);
    34 
    35                 //此处不能直接放在构造函数中,因为如果直接放在那,还没分配tcpSocket空间,会程序异常
    36                 //如果有数据传送成功,对方的通信套接字会触发readyRead(),需要在对应的槽函数做接收处理
    37                 connect(tcpSocket,&QTcpSocket::readyRead,
    38                         [=]()
    39                         {
    40                             //从通信套接字中取出内容
    41                             QByteArray array = tcpSocket->readAll();
    42                             //然后将内容追加到显示文本中
    43                             ui->textEdit_read->append(array);
    44                         }
    45                         );
    46 
    47                 //如果对方主动断开连接,通信套接字会自动触发disconnected()
    48                 connect(tcpSocket,&QTcpSocket::disconnected,
    49                         [=]()
    50                         {
    51                             ui->textEdit_read->append(QString::fromLocal8Bit("对方主动断开连接"));
    52                         }
    53                         );
    54             }
    55             );
    56 
    57 }
    58 
    59 ServerWidget::~ServerWidget()
    60 {
    61     delete ui;
    62 }
    63 
    64 void ServerWidget::on_button_send_clicked()
    65 {
    66     if(tcpSocket == NULL)
    67     {
    68         return;
    69     }
    70 
    71     //获取编辑区内容
    72     QString str = ui->textEdit_write->toPlainText();
    73     //给对方发送数据,使用套接字是tcpSocket
    74     tcpSocket->write(str.toUtf8().data());
    75 }
    76 
    77 void ServerWidget::on_button_close_clicked()
    78 {
    79     if(tcpSocket == NULL)
    80     {
    81         return;
    82     }
    83     //主动和客户端断开连接
    84     tcpSocket->disconnectFromHost();
    85     tcpSocket->close();
    86 
    87     tcpSocket = NULL;
    88 }

    clientwidget.cpp

     1 #include "clientwidget.h"
     2 #include "ui_clientwidget.h"
     3 //需要包含头文件
     4 #include <QHostAddress>
     5 
     6 ClientWidget::ClientWidget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::ClientWidget)
     9 {
    10     ui->setupUi(this);
    11     setWindowTitle(QString::fromLocal8Bit("客户端"));
    12 
    13     tcpSocket = NULL;
    14 
    15     //分配空间,指定父对象
    16     tcpSocket = new QTcpSocket(this);
    17 
    18     //当tcpSocket套接字建立连接成功
    19     //如果成功和对方建立连接,通信套接字会自动触发connected()
    20     connect(tcpSocket,&QTcpSocket::connected,
    21             [=]()
    22             {
    23                 ui->textEdit_read->setText(QString::fromLocal8Bit("成功和服务器建立连接"));
    24             }
    25             );
    26 
    27     //如果有数据传送成功,对方的通信套接字会触发readyRead(),需要在对应的槽函数做接收处理
    28     connect(tcpSocket,&QTcpSocket::readyRead,
    29             [=]()
    30             {
    31                 //从通信套接字中取出内容
    32                 QByteArray array = tcpSocket->readAll();
    33                 //然后将内容追加到显示文本中
    34                 ui->textEdit_read->append(array);
    35             }
    36             );
    37 
    38     //如果对方主动断开连接,通信套接字会自动触发disconnected()
    39     connect(tcpSocket,&QTcpSocket::disconnected,
    40             [=]()
    41             {
    42                 ui->textEdit_read->append(QString::fromLocal8Bit("对方主动断开连接"));
    43             }
    44             );
    45 
    46 }
    47 
    48 ClientWidget::~ClientWidget()
    49 {
    50     delete ui;
    51 }
    52 
    53 void ClientWidget::on_pushButton_connect_clicked()
    54 {
    55     //获取服务器ip和端口
    56     QString ip = ui->lineEdit_IP->text();
    57     qint16 port = ui->lineEdit_port->text().toInt();
    58 
    59     //主动和服务器建立连接
    60     tcpSocket->connectToHost(QHostAddress(ip),port);
    61 }
    62 
    63 void ClientWidget::on_pushButton_send_clicked()
    64 {
    65     //获取编辑框内容
    66     QString str = ui->textEdit_Write->toPlainText();
    67     //发送数据
    68     tcpSocket->write( str.toUtf8().data() );
    69 }
    70 
    71 void ClientWidget::on_pushButton_close_clicked()
    72 {
    73     //主动和服务器断开连接
    74     tcpSocket->disconnectFromHost();
    75     tcpSocket->close();
    76 }

    serverwidget.ui

    clientwidget.ui

    三、TCP传递文件

      实现以下功能:

        客户端连接到服务器

        服务器端选择文件,然后发送

        客户端接收文件,并提示接收成功

      大概流程图如下:

        

    代码如下:

    QT_HelloWorld14.pro

     1 #-------------------------------------------------
     2 #
     3 # Project created by QtCreator 2017-08-31T19:08:18
     4 #
     5 #-------------------------------------------------
     6 
     7 QT       += core gui network
     8 
     9 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    10 
    11 TARGET = QT_HelloWorld14
    12 TEMPLATE = app
    13 
    14 
    15 SOURCES += main.cpp
    16         serverwidget.cpp 
    17     clientwidget.cpp
    18 
    19 HEADERS  += serverwidget.h 
    20     clientwidget.h
    21 
    22 FORMS    += serverwidget.ui 
    23     clientwidget.ui
    24 
    25 CONFIG += C++11

    main.cpp

     1 #include "serverwidget.h"
     2 #include <QApplication>
     3 #include "clientwidget.h"
     4 
     5 int main(int argc, char *argv[])
     6 {
     7     QApplication a(argc, argv);
     8     ServerWidget w;
     9     w.show();
    10     //显示客户端窗口
    11     ClientWidget w2;
    12     w2.show();
    13 
    14     return a.exec();
    15 }

    clientwidget.h

     1 #ifndef CLIENTWIDGET_H
     2 #define CLIENTWIDGET_H
     3 
     4 #include <QWidget>
     5 #include <QTcpSocket>
     6 #include <QFile>
     7 
     8 namespace Ui {
     9 class ClientWidget;
    10 }
    11 
    12 class ClientWidget : public QWidget
    13 {
    14     Q_OBJECT
    15 
    16 public:
    17     explicit ClientWidget(QWidget *parent = 0);
    18     ~ClientWidget();
    19 
    20 private slots:
    21     void on_pushButton_connect_clicked();
    22 
    23 private:
    24     Ui::ClientWidget *ui;
    25     QTcpSocket *tcpSocket;//通信套接字
    26 
    27     QFile file;//文件对象
    28     QString fileName;//文件名字
    29     qint64 fileSize;//文件大小
    30 
    31     qint64 receiveSize;//已经接收文件的大小
    32     bool isStart ;//标志位,判断是不是开始的部分(文件名和文件大小)
    33 };
    34 
    35 #endif // CLIENTWIDGET_H

    serverwidget.h

     1 #ifndef SERVERWIDGET_H
     2 #define SERVERWIDGET_H
     3 
     4 #include <QWidget>
     5 #include <QTcpServer>//监听套接字
     6 #include <QTcpSocket>//通信套接字
     7 #include <QFile>
     8 #include <QTimer>
     9 
    10 
    11 namespace Ui {
    12 class ServerWidget;
    13 }
    14 
    15 class ServerWidget : public QWidget
    16 {
    17     Q_OBJECT
    18 
    19 public:
    20     explicit ServerWidget(QWidget *parent = 0);
    21     ~ServerWidget();
    22 
    23 private slots:
    24     void on_pushButton_file_clicked();
    25 
    26     void on_pushButton_send_clicked();
    27 
    28     void sendDate();//发送文件数据
    29 
    30 private:
    31     Ui::ServerWidget *ui;
    32     QTcpServer *tcpServer;//监听套接字
    33     QTcpSocket *tcpSocket;//通信套接字
    34 
    35     QFile file;//文件对象
    36     QString fileName;//文件名字
    37     qint64 fileSize;//文件大小
    38 
    39     qint64 sendSize;//已经发送文件的大小
    40     QTimer timer; //定时器,用来间隔头文件和文件正文
    41 };
    42 
    43 #endif // SERVERWIDGET_H

    clientwidget.cpp

      1 #include "clientwidget.h"
      2 #include "ui_clientwidget.h"
      3 #include <QTcpSocket>
      4 #include <QByteArray>
      5 #include <QDebug>
      6 #include <QFile>
      7 #include <QMessageBox>
      8 #include <QHostAddress>
      9 #include <QIODevice>//需要包含此头文件,防止出现"device not open"的错误
     10 
     11 ClientWidget::ClientWidget(QWidget *parent) :
     12     QWidget(parent),
     13     ui(new Ui::ClientWidget)
     14 {
     15     ui->setupUi(this);
     16 
     17     setWindowTitle(QString::fromLocal8Bit("客户端"));
     18     tcpSocket = new QTcpSocket(this);
     19     isStart = true;//刚开始时,为true
     20     ui->progressBar->setValue(0);//初始化,进度条为0
     21 
     22     //数据传送过来成功,自动触发QTcpSocket::readyRead信号
     23     connect(tcpSocket,&QTcpSocket::readyRead,
     24             [=]()
     25             {
     26                 //取出接收的内容
     27                 QByteArray buf = tcpSocket->readAll();
     28                 //接收头文件
     29                 if(isStart == true)
     30                 {
     31                     //接收头文件
     32                     isStart = false;
     33                     //解析头部信息 (文件名##文件大小)
     34                     //拆包
     35                     //初始化工作
     36                     fileName = QString(buf).section("##",0,0);//以"##"分割,获取从第1部分开始,到第1部分结束的字符串
     37                     fileSize = QString(buf).section("##",1,1).toInt();
     38                     receiveSize = 0;
     39                     qDebug() << QString::fromLocal8Bit("客户端接收的头文件:%1 ").arg(fileName) ;
     40 
     41                     //打开文件,并向里面写内容
     42                     file.setFileName(fileName);//关联文件名字
     43                     bool isOK = file.open(QIODevice::WriteOnly);
     44                     if(isOK == false)//如果打开文件失败,中断函数
     45                     {
     46                         qDebug() << "WriteOnly error 37" ;
     47                         tcpSocket->disconnectFromHost();//断开连接
     48                         tcpSocket->close();//关闭套接字
     49                         return;
     50                     }
     51 
     52                     //弹出对话框,显示接收文件的信息
     53                     //QString str =  QString::fromLocal8Bit("接收的文件:[%1  %2kb]").arg(fileName).arg(fileSize);
     54                     //QMessageBox::information(this,QString::fromLocal8Bit("文件信息"),str);
     55 
     56                     //设置进度条
     57                     ui->progressBar->setMinimum(0);//最小值
     58                     ui->progressBar->setMaximum(fileSize);//最大值,因为进度条最大值是int类型,防止范围不够用,最好除以一个数,使最大的范围变小一些
     59                     ui->progressBar->setValue(0);//当前值
     60 
     61                 }
     62                 //接收文件正文
     63                 else
     64                 {
     65                     //接收文件正文
     66                     qint64 len = file.write(buf);//向新文件中写buf数据,并返回写入的数据长度
     67                     if(len > 0)
     68                     {
     69                         //计算累计接收的数据大小
     70                         receiveSize += len;
     71                         //每接收一部分数据,就向服务器端发送一个信息
     72                         //QString str = QString::number(receiveSize);
     73                         //tcpSocket->write( str.toUtf8().data() );
     74                     }
     75                     //更新进度条
     76                     //ui->progressBar->setValue(receiveSize);//当前值
     77                     //接收数据完成
     78                     if(receiveSize == fileSize)
     79                     {
     80                         //先给服务器发送(接收文件完成的信息)
     81                         //tcpSocket->write("file done");
     82                         //弹出对话框,提示文件接收完成
     83                         QMessageBox::information(this,"done",QString::fromLocal8Bit("文件接收完成 50"));
     84                         file.close();//关闭文件
     85                         tcpSocket->disconnectFromHost();//断开连接
     86                         tcpSocket->close();
     87                     }
     88                 }
     89             }
     90             );
     91 }
     92 
     93 ClientWidget::~ClientWidget()
     94 {
     95     delete ui;
     96 }
     97 
     98 void ClientWidget::on_pushButton_connect_clicked()
     99 {
    100     //获取服务器的ip和端口
    101     QString ip = ui->lineEdit_ip->text();
    102     qint16 port = ui->lineEdit_port->text().toInt();
    103 
    104     //主动和服务器建立连接
    105     tcpSocket->connectToHost(QHostAddress(ip),port);
    106 }

    serverwidget.cpp

      1 #include "serverwidget.h"
      2 #include "ui_serverwidget.h"
      3 #include <QHostAddress>
      4 #include <QFileDialog>
      5 #include <QFileInfo>
      6 #include <QDebug>
      7 #include <QTimer>
      8 #include <QIODevice>
      9 
     10 ServerWidget::ServerWidget(QWidget *parent) :
     11     QWidget(parent),
     12     ui(new Ui::ServerWidget)
     13 {
     14     ui->setupUi(this);
     15     setWindowTitle(QString::fromLocal8Bit("服务器端口为:9999"));
     16 
     17     //两个按钮都不能按
     18     ui->pushButton_file->setEnabled(false);
     19     ui->pushButton_send->setEnabled(false);
     20 
     21     //监听套接字
     22     tcpServer = new QTcpServer(this);
     23     //监听
     24     tcpServer->listen(QHostAddress::Any,9999);
     25     //如果客户端成功和服务器连接
     26     //tcpServer会自动触发newConnection()
     27     connect(tcpServer,&QTcpServer::newConnection,
     28             [=]()
     29             {
     30                 //取出建立好连接的套接字
     31                 tcpSocket = tcpServer->nextPendingConnection();
     32                 //获取对方的ip和端口
     33                 QString ip = tcpSocket->peerAddress().toString();
     34                 quint16 port = tcpSocket->peerPort();
     35                 //QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
     36                 QString str = QString::fromLocal8Bit("[%1:%2] 成功连接").arg(ip).arg(port);
     37                 //显示到编辑区
     38                 ui->textEdit->setText(str);
     39 
     40                 //成功连接后,才能按选择文件按钮
     41                 ui->pushButton_file->setEnabled(true);
     42 
     43                 //此处不能直接放在构造函数中,因为如果直接放在那,还没分配tcpSocket空间,会程序异常
     44                 //如果有数据传送成功,对方的通信套接字会触发readyRead(),需要在对应的槽函数做接收处理
     45                 /*connect(tcpSocket,&QTcpSocket::readyRead,
     46                         [=]()
     47                         {
     48                             //从通信套接字中取出内容
     49                             QByteArray array = tcpSocket->readAll();
     50                             //如果接收到"文件接收完毕"的信号
     51                             if(QString(array) == "file done")
     52                             {
     53                                 ui->textEdit->append(QString::fromLocal8Bit("文件发送完毕"));
     54                                 file.close();//关闭文件
     55                                 tcpSocket->disconnectFromHost();//断开与客户端的连接
     56                                 tcpSocket->close();
     57                             }
     58                             else
     59                             {
     60                                 qDebug() << array;
     61                             }
     62                         }
     63                         );
     64                         */
     65             }
     66             );
     67 
     68 
     69     //此处定时器只是起到延迟传送的作用,启动之后,立刻关掉,执行发送文件方法
     70     connect(&timer,&QTimer::timeout,
     71             [=]()
     72             {
     73                 //关闭定时器
     74                 timer.stop();
     75                 //发送文件
     76                 sendDate();
     77             }
     78             );
     79 
     80 }
     81 
     82 ServerWidget::~ServerWidget()
     83 {
     84     delete ui;
     85 }
     86 //选择文件按钮
     87 void ServerWidget::on_pushButton_file_clicked()
     88 {
     89     //打开文件对话框,选择文件
     90     QString filePath = QFileDialog::getOpenFileName(this,"open","../");
     91     //如果选择的文件有效
     92     if(filePath.isEmpty() == false)
     93     {
     94         fileName.clear();
     95         fileSize = 0;
     96 
     97         //获取文件信息
     98         QFileInfo info(filePath);
     99         fileName = info.fileName();
    100         fileSize = info.size();//获取文件名字和大小
    101         sendSize = 0;//发送文件的大小
    102 
    103         //只读方式打开
    104         //指定文件的名字
    105         file.setFileName(filePath);//指定文件,然后才能打开
    106         bool isOK = file.open(QIODevice::ReadOnly);
    107         if(isOK == false)
    108         {
    109             qDebug() << QString::fromLocal8Bit("只读方式打开文件失败 77");
    110         }
    111         //在编辑区提示打开文件的路径
    112         ui->textEdit->append(filePath);
    113         //把选择文件按钮禁用
    114         ui->pushButton_file->setEnabled(false);
    115         //让发送文件按钮可用
    116         ui->pushButton_send->setEnabled(true);
    117     }
    118     else
    119     {
    120         qDebug() << QString::fromLocal8Bit("选择文件出错 59");
    121     }
    122 }
    123 //发送文件按钮
    124 void ServerWidget::on_pushButton_send_clicked()
    125 {
    126     //先发送文件头信息  文件名##文件大小
    127     QString head = QString("%1##%2").arg(fileName).arg(fileSize);
    128     //发送头部信息
    129     qint64 len = tcpSocket->write( head.toUtf8() );
    130 
    131     qDebug() << head.toUtf8() << len ;
    132     if(len > 0)//头部信息发送成功
    133     {
    134         //发送真正的文件
    135         //防止TCP黏包文件
    136         //需要通过定时器延时20ms(保证头文件先发送过去,然后再发送文件正文)
    137         timer.start(20);//启动定时器
    138     }
    139     else
    140     {
    141         qDebug() << QString::fromLocal8Bit("头部信息发送失败 110");
    142         file.close();
    143         //改变按钮可用状态
    144         ui->pushButton_file->setEnabled(true);
    145         ui->pushButton_send->setEnabled(false);
    146     }
    147     //再发送真正的文件信息
    148 }
    149 //发送文件数据
    150 void ServerWidget::sendDate()
    151 {
    152     qint64 len = 0;
    153     do
    154     {
    155         //每次发送数据的大小(4kb)
    156         char buf[4*1024] = {0};
    157         len = 0;
    158         //往文件中读数据
    159         len = file.read(buf,sizeof(buf));
    160         //发送数据,读多少发多少
    161         len = tcpSocket->write(buf,len);
    162         //发送的数据累计
    163         sendSize += len;
    164     }while(len>0);
    165 
    166     //是否发送文件完毕
    167     if(sendSize == fileSize)
    168     {
    169         ui->textEdit->append(QString::fromLocal8Bit("文件发送完毕 128"));
    170         file.close();
    171         //把客户端断开
    172         tcpSocket->disconnectFromHost();
    173         tcpSocket->close();
    174     }
    175 }

    clientwidget.ui

    serverwidget.ui

  • 相关阅读:
    深入Android 【一】 —— 序及开篇
    Android中ContentProvider和ContentResolver使用入门
    深入Android 【六】 —— 界面构造
    The service cannot be activated because it does not support ASP.NET compatibility. ASP.NET compatibility is enabled for this application. Turn off ASP.NET compatibility mode in the web.config or add the AspNetCompatibilityRequirements attribute to the ser
    Dynamic Business代码片段总结
    对文件的BuildAction以content,resource两种方式的读取
    paraview 3.12.0 windows下编译成功 小记
    百度网盘PanDownload使用Aria2满速下载
    netdata的安装与使用
    用PS给证件照排版教程
  • 原文地址:https://www.cnblogs.com/blog-ccs/p/7457870.html
Copyright © 2011-2022 走看看