QT5 網路通訊

来源:https://www.cnblogs.com/flowingwind/archive/2018/01/26/8348519.html
-Advertisement-
Play Games

QT5 TCP網路通訊 伺服器與客戶端建立連接listen() - connectToHost(); 觸發newPendingConnect信號 實時數據通訊write(); read(); 觸發readyRead信號 通訊主要使用的類: QTcpServer Class QTcpServer類提供 ...


QT5 TCP網路通訊

  • 伺服器與客戶端建立連接listen() - connectToHost();  觸發newPendingConnect信號
  • 實時數據通訊write(); read();  觸發readyRead信號

通訊主要使用的類:

QTcpServer  Class

QTcpServer類提供了一個基於TCP的伺服器。
這個類可以接受傳入的TCP連接。您可以指定埠或讓QTcpServer自動選擇一個埠。您可以收聽特定地址或所有機器的地址。
調用listen()讓伺服器偵聽傳入的連接。每次客戶端連接到伺服器時,都會發出newConnection()信號。

QTcpSocket Class

QTcpSocket類提供了一個TCP套接字。
TCP(傳輸控制協議)是一種可靠的,面向流的,面向連接的傳輸協議。 它特別適合連續傳輸數據。
QTcpSocket是QAbstractSocket的一個方便的子類,它允許你建立一個TCP連接並傳輸數據流。

建立連接:

伺服器端以監聽的方式監聽客服端是否有連接請求

客戶端以調用connectToHost()函數主動連接伺服器端

tcp協議伺服器端實現流程

建立伺服器對象

listen伺服器, 通過建立的伺服器 監聽指定地址/埠的客服端;判斷是否有客戶連接有連接就觸發newConnection();

通過connect處理newConnection()信號;

    server = new QTcpServer(this); //建立一個伺服器對象
    server->listen(QHostAddress::Any, 8000);//通過建立的伺服器監聽指定ip地址及埠號的客服端,如不指定埠號,系統會隨機分配
    connect(server, QTcpServer::newConnection,
    [=]()
    {
        qDebug() << "有連接進來";
    }
    );

tcp協議客戶端實現流程

建立QTcpSocket套節字(ip,埠)

通過套節字connectToHost()函數主動連接伺服器;連接成功則觸發伺服器QTcpServer::newConnection信號;併發送套節字到伺服器端;

關閉連接;

QTcpSocket Sc(this);
Sc.connectToHost("127.0.0.1", 8888);//實際代碼中參數要進行類型轉化
Sc.close();

實時通訊:

  • 客戶端到伺服器端通訊
  1. 當客戶端與伺服器端建立連接後;
  2. 客戶端與伺服器端通訊在客戶端通過套節字對象調用write()函數發送上傳內容;
  3. 伺服器端有客戶端數據寫入時伺服器端會自動調用readyread信號
  4. 伺服器端在connect中處理readyread信號,並由nextPendingConnection()函數接收客戶端發送的套節字;
  5. 伺服器端對接收的套節字進行相應處理,即完成一次客戶端到伺服器端的通訊
  • 伺服器端到客戶端的通訊
  1. 當客戶端與伺服器端建立連接後;
  2. 伺服器通過套節字對象調用write()函數發送上傳內容;客戶端會觸發readyread信號
  3. 客戶端在connect中處理readyread信號

客戶端到伺服器端實現代碼:

//伺服器端   
 connect(server, QTcpServer::newConnection,
            [=]()
    {
       QTcpSocket socket = server->nextPendingConnection();
        connect(socket, &QTcpSocket::readyRead, [=]()
        {
            tp = socket->readAll();
            ui->textrc->append(tp);
        });
    }
    );
//客戶端
void socket::on_buttonsend_clicked()
{
    QString temp = ui->textrc->toPlainText();
    if(!temp.isEmpty())sock->write(temp.toUtf8());

}

 

伺服器端客戶端實現代碼:

//伺服器端
void Widget::on_buttonsend_clicked()
{
    socket->write(ui->textEdit->toPlainText().toUtf8());
}
//客戶端
    connect(sock, &QTcpSocket::readyRead,
            [=]()
    {
       ui->textdis->append(sock->readAll());
    });

完整代碼:

伺服器ui設計:

伺服器端頭文件widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_buttonsend_clicked();

private:
    Ui::Widget *ui;

    QTcpServer *server; //建立伺服器對象
    QTcpSocket *socket; //套節字對象
    QByteArray tp;   //
};

#endif // WIDGET_H

伺服器端cpp文件 widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("伺服器");
    tp = nullptr;

    server = new QTcpServer(this);
    server->listen(QHostAddress::Any, 8000);
    connect(server, QTcpServer::newConnection,
            [=]()
    {
        socket = server->nextPendingConnection();
        connect(socket, &QTcpSocket::readyRead, [=]()
        {
            tp = socket->readAll();
            ui->testdis->append(tp);
        });
    }
    );

}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_buttonsend_clicked()
{
    socket->write(ui->textEdit->toPlainText().toUtf8());
}

客戶端ui:

 

客戶端頭文件socket.h:

#ifndef SOCKET_H
#define SOCKET_H

#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>

namespace Ui {
class socket;
}

class socket : public QWidget
{
    Q_OBJECT

public:
    explicit socket(QWidget *parent = 0);
    ~socket();

private slots:

    void on_buttonLink_clicked();

    void on_buttonsend_clicked();

    void on_serverclose_clicked();

private:
    Ui::socket *ui;

    QTcpSocket *sock;
    QHostAddress adrs;
    quint16 port;
};

#endif // SOCKET_H

客戶端cpp文件socket.cpp

#include "socket.h"
#include "ui_socket.h"

socket::socket(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::socket)
{
    ui->setupUi(this);
    sock = new QTcpSocket(this);
    setWindowTitle("張三");
    connect(sock, &QTcpSocket::readyRead,
            [=]()
    {
       ui->textdis->append(sock->readAll());
    });
}

socket::~socket()
{
    delete ui;
}

void socket::on_buttonLink_clicked()
{
    QString ip = ui->serverIP->text();
    QString p = ui->serverPort->text();
    sock->connectToHost(ip, p.toUShort());
}

void socket::on_buttonsend_clicked()
{
    QString temp = ui->textEdit->toPlainText();
    if(!temp.isEmpty())sock->write(temp.toUtf8());
}

void socket::on_serverclose_clicked()
{
    sock->close();
}

main.cpp文件

#include "widget.h"
#include <QApplication>
#include "socket.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    socket w1;
    w1.show();

    return a.exec();
}

最終運行效果:

 當然在具體的實現過程中還有很多很多的細節需要優化;

 

QT5對tcp協議基本的通訊總結:                             

  • QTcpServer *p = new QTcpServer(this);//建立伺服器對象               QTcpSocket *q = new QTcpSocket(this); //客戶機建立套節字對象                 
  • p.listen(監聽的客戶ip , 監聽埠port);//監聽客戶機           q.conncetToHost(要連接的伺服器ip, 要連接的伺服器埠);
  • connect(p, &QTcpServer::newConnection, )連接成功觸發信號   q.write();//發送數劇 到伺服器
  • QTcpSocket skt = p.nextPendingConnection();//獲取客戶機套節字     connect(q, &QTcpSocket::readyRead, )//服務端發送數據客戶端觸發信號
  • connect(skt, &QTcpSocket::readyRead, )//客戶發送數據觸發信號    q.readall();//讀取客戶端發送的數據;  
  • skt.readall();//讀取客戶端發送的數據;                                                       客戶端處理數據
  • 伺服器端處理數據 

 

QT5 UDP網路通訊 

UDP沒有伺服器與客戶端之分;單純通過writeDatagram發( 參數1, 參數2,參數3)送指定的內容(參數1)到指定的ip(參數2),埠(參數3)上;

當收取到網路中的數據發送,就會觸發自己的readyRead信號;readDatagram(參數1, 參數2,參數3),保存讀取的內容(參數1);保存對方ip(參數2);保存對方埠(參數3)

具體實現過程:

  • 建立QUdpSocket套節字                                                                                   QUdpSocket* p = new QUdpSocket(this);
  • 綁定本程式埠號 bind()                                                                                    p.bind(8000);
  • 通過writeDatagram()發送數據到指定目標                                                      p.writeDatagram();
  • 當有readyRead信號發生通過readDatagram()函數讀取保存數據        p.readDatagram();

 

 實現代碼:

    QUdpSocket *udp = new QUdpSocket(this);
    udp->bind(8000);
    connect(udp, &QUdpSocket::readyRead, [=]()
    {

       char temp[1024] = {0};
       QHostAddress q;
       quint16 p;
       udp->readDatagram(temp, sizeof(temp), &q, &p);
       ui->textdis->append(temp);
    });

/......./

//按鍵確定發送
void Widget::on_buttonlink_clicked()
{
    udp->writeDatagram(ui->textsend->toPlainText().toUtf8(), QHostAddress(ui->ip->text()), ui->port->text().toUShort());
}

 

整體代碼:

ui設計:

 

widget.h頭文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_buttonlink_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket *udp;
};

#endif // WIDGET_H

 

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QDialog>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("8000");
    udp = new QUdpSocket(this);
    udp->bind(8000);
    udp->joinMulticastGroup(QHostAddress(""));
    connect(udp, &QUdpSocket::readyRead, [=]()
    {

       char temp[1024] = {0};
       QHostAddress q;
       quint16 p;
       udp->readDatagram(temp, sizeof(temp), &q, &p);
       ui->textdis->append(temp);
    });
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_buttonlink_clicked()
{
    udp->writeDatagram(ui->textsend->toPlainText().toUtf8(), QHostAddress(ui->ip->text()), ui->port->text().toUShort());
}

main.cpp文件

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

 

以便測試:我們先編譯生成一份客戶端;再在源文件中改變bind埠號為8888再生成一份客戶端;最終就會有兩份客戶端以便相互通信測試

運行測試結果:

由於UDP不需要伺服器,所以,UDP發送的數據,只要能接收到你的ip及埠的客戶端就殾能收到信息;所以在區域網內,ip地址欄可輸入

255.255.255.255 即整個區域網內的客戶端都能收到信息;

UDP通訊組包

為了滿足,發送的信息指定ip段內的客戶收到信息可以用函數JoinMulticastGroup(IPAddress)加入到組;根據msdn記載,沒錯是同樣的功能;

 

目前個人理解也不深;詳細數據可查msdn;可用leaveMulticastGroup()函數離開組翻;

Tcp 與 Udp的比較:

Udp不需要伺服器,只管發送數據,不對數據進行檢查,也不對接收者檢測;速度快,易丟包,做即時數據傳送比較好;

 Tcp方式總結:

伺服器端:QTcpServer

1】基本用法: 創建一個QTcpServer,然後調用listen函數監聽相應的地址和埠。當有客戶端鏈接到伺服器時,會有信號newConnection()產生。 調用nextPendingConnection()接受一個掛起的TcpSocket連接,該函數返回一個指向QTcpSocket的指針,同時進入到QAbstractSocket::ConnectedState狀態。這樣就可以和客戶端進行通信了。如果錯誤發生,可以用函數serverError()返回錯誤類型,用errorString()返回錯誤提示字元串。 調用close使得QTcpServer停止監聽連接請求。儘管QTcpServer使用了事件迴圈,但是可以不這麼使用。利用waitForNewConnection(),該函數阻塞直到有連接可用或者時間超時。 2】重要函數: void incomingConnection (int socketDescriptor); 當一個連接可以用時,QTcpServer調用該函數。其基本過程是現創建一個QTcpSocket,設置描述符和保存到列表,最後發送newConnection() 事件消息。   QTcpSocket*ss  QTcpServer::nextPendingConnection(); 返回下一個將要連接的QTcpSocket對象,該返回對象是QTcpServer的子對象,意味著如果刪除了QTcpSServer,則刪除了該對象。也可以在你不需要該對象時,將他刪除掉,以免占用記憶體。

       ss->peerAddress();ss->peerName();ss->peerPort(); 當連接成功可能通過函數獲取客戶端ip,name,port;

  客戶端:QTcpSocket,QAbstractSocket   1】基本用法: 在客戶端創建一個QTcpSocket,然後用connectToHost函數向對應的主機和埠建立連接。 任何時候,可以用state()查詢狀態,初始為UnconnectedState,然後調用連接函數之後,HostLookupState,如果連接成功進入ConnectedState,並且發送hostFound()信號。 當連接建立,發送connected(),在任何狀態下如果在錯誤發生error()信號發送。狀態改變發送stateChanged()信號。如果QTcpSocket準備好可讀可寫,則isValid() 函數範圍為真。   用read()和write()來讀寫,或者使用readLine()和readAll.當有數據到來的時候,系統會發送readyRead()信號。 bytesAvailable()返回包的位元組數,如果你不是一次性讀完數據,新的數據包到來的時候將會附加到內部讀緩存後面。setReadBufferSize()可以設置讀緩存的大小。   用disconnectFromHost()關閉連接,進入ClosingState。當所有數據寫入到socket,QAbstractSocket會關閉該台socket,同時發送disconnected()消息。 如果想立即終止一個連接,放棄數據發送,調用abort(). 如果遠程主機關閉連接,QAbstractSocket發送QAbstractSocket::RemoteHostClosedError錯誤,但是狀態還停留在ConnectedState,然後發送disconnected()信號。   QAbstractSocket提供幾個函數用來掛起調用線程,知道一定的信號發送,這些函數可以用來阻塞socket: waitForConnected() 阻塞知道一個連接建立。 waitForReadyRead() 阻塞知道有新的數據可以讀取。 waitForBytesWritten() 阻塞直到發送數據寫道socket中。 waitForDisconnected() 阻塞知道鏈接關閉。  

QT5 TCP網路通訊綜合案例之文件傳送

最終效果圖:

 

 1.功能設計

伺服器端:

  1. 有客戶端連接進入就提示連接客戶ip埠號等基本信息
  2. [選擇文件]按鈕被激活可在系統選取任意文件,顯示選取的文件路徑
  3. [發送]按鈕被激活,點擊[發送]向客戶端發送文件請求;
  4. 文件發送完成後 顯示發送完成提示

客戶端:

  1. 連接伺服器ip,port編輯框
  2. 按鈕[連接伺服器]成功後顯示提示並激活斷開伺服器按鈕
  3. 可選文件接收保存路徑
  4. 當收到伺服器發送文件請求,顯示所要接收的文件名,文件大小等基本信息
  5. 僅當收到伺服器發送的文件請求與選擇了有效文件路徑前提下,激活[接收文件]按鈕
  6. 點擊[接收文件]開始從伺服器接收文件,文件接收進度條提示功能,並顯示文件傳送完成提示

實現原代碼:

//main主函數
#include "widget.h"
#include "client.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    client w1;
    w1.show();
    return a.exec();
}
//伺服器端頭文件
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QFile>

namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private slots:
    void on_buttoncheck_clicked();
    void on_buttonsend_clicked();
private:
    Ui::Widget *ui;
    QTcpServer *sv;
    QTcpSocket *ss;

    QString sendFileName;
    qint64 sendFileSize;
    qint64 sendedSize;
    QFile sendFile;
};

#endif // WIDGET_H
//伺服器cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("服務端-埠:9527");
    sv = new QTcpServer(this);
    sv->listen(QHostAddress::Any, 9527);
    ui->buttonsend->setEnabled(false);
    ui->buttoncheck->setEnabled(false);
    sendedSize = 0;
    connect(sv, &QTcpServer::newConnection, [=]()
    {
        ss = sv->nextPendingConnection();
        if(ss->isValid())
        {
            ui->buttoncheck->setEnabled(true);
            QString pAd = ss->peerAddress().toString().remove(0, 7);
            QString pNa = ss->peerName();
            quint16 pPo = ss->peerPort();
            ui->textdis->setText(QString("已成功連接到用戶IP:%1:%2:Name:%3").arg(pAd).arg(pPo).arg(pNa));
        }else
        {
            ui->textdis->setText("連接套節字無效");
        }
        connect(ss, &QTcpSocket::readyRead,[=]()
        {
           if(ss->readAll() == "ok")
           {
               qint64 lenW = 0;
               if(sendFile.open(QIODevice::ReadOnly))
               {
                   do{
                       char tempdata[1024] = {0};
                       lenW = sendFile.read(tempdata, sizeof(tempdata));
                       ss->write(tempdata, lenW);
                       sendedSize += lenW;
                   }while(lenW > 0);
                   if(sendedSize >= sendFileSize)
                   {
                       ui->textdis->append("發送完成");
                       ss->disconnectFromHost();
                       sendFile.close();
                   }
               }else
               {
                   QMessageBox *abot = new QMessageBox(this);
                   abot->setText("打開文件失敗");
               }
           }
        });

    });
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_buttoncheck_clicked()
{
    QString File = QFileDialog::getOpenFileName(this, QString("選擇文件"), QString("../"));
    if(!File.isEmpty())
    {
        ui->buttoncheck->setEnabled(false);
        ui->textdis->append(File);
        sendFile.setFileName(File);
        QFileInfo info(File);
        sendFileName = info.fileName();
        sendFileSize = info.size();
        ui->buttonsend->setEnabled(true);
    }
}

void Widget::on_buttonsend_clicked()
{
    QString temp = QString("head@@%1@@%2").arg(sendFileName).arg(sendFileSize);
    ss->write(temp.toUtf8());
    ui->buttonsend->setEnabled(false);
}
//客戶端頭文件
#ifndef CLIENT_H
#define CLIENT_H

#include <QWidget>
#include <QTcpSocket>
#include <QFile>

namespace Ui {
class client;
}
class client : public QWidget
{
    Q_OBJECT

public:
    explicit client(QWidget *parent = 0);
    ~client();

private slots:
    void on_Buttonlink_clicked();
    void on_Buttonclose_clicked();
    void on_ButtonSelect_clicked();
    void on_ButtonSave_clicked();
private:
    Ui::client *ui;
    QTcpSocket userSocket;

    QFile acpFile;
    QString headFile;
    QString savePath;
    bool head;
    bool openFile;
    QString _name;
    QString _size;
    qint64 nowsize;

};

#endif // CLIENT_H
//客戶端cpp
#include "client.h"
#include "ui_client.h"
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>

client::client(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::client)
{
    ui->setupUi(this);
    setWindowTitle("客戶端");
    head = true;
    openFile =false;
    nowsize = 0;
    ui->progressBar->setMinimum(0);
    ui->progressBar->setValue(0);
    ui->ButtonSave->setEnabled(false);
    ui->Buttonclose->setEnabled(false);
    savePath = nullptr;
    connect(&userSocket, &QTcpSocket::connected,
            [=](){
        ui->Buttonlink->setEnabled(false);
        ui->Buttonclose->setEnabled(true);
        ui->lineStatus->setText("連接成功");});
    connect(&userSocket, &QTcpSocket::readyRead,
            [=](){
        if(head)
        {
            headFile = QString(userSocket.readAll());
            if(!headFile.isEmpty())
            {
                head = false;
                _name = headFile.section("@@", 1, 1);
                _size = headFile.section("@@", 2, 2);
                ui->progressBar->setMaximum(_size.toLongLong());
                ui->lineStatus->setText(QString("文件名:[%1]總大小:[%2]等待接收").arg(_name).arg(_size));
                acpFile.setFileName(QString("%1/%2").arg(savePath).arg(_name));
                if(!savePath.isEmpty())
                {
                    ui->ButtonSave->setEnabled(true);
                }
            }
        }
        else
        {
            if(openFile)
            {
                nowsize += acpFile.write(userSocket.readAll());
                ui->progressBar->setValue(nowsize);
            }
            else
            {
                QMessageBox *abot = new QMessageBox(this);
                abot->setText("打開文件失敗");
            }
            if(acpFile.size() >= _size.toLongLong())
            {
                nowsize = 0;
                acpFile.close();
                ui->lineStatus->setText("文件傳送完成");
                head = true;
            }
        }

    });
}

client::~client()
{
    delete ui;
}

void client::on_Buttonlink_clicked()
{
    QString linkIp = ui->lineIp->text();
    quint16 linkPort = ui->linePort->text().toUShort();
    userSocket.connectToHost(linkIp, linkPort);
}

void client::on_Buttonclose_clicked()
{
    userSocket.disconnectFromHost();
    userSocket.close();
    ui->lineStatus->setText("斷開連接");
    head = true;
    ui->Buttonlink->setEnabled(true);
    ui->Buttonclose->setEnabled(false);
    ui->progressBar->setValue(0);
}

void client::on_ButtonSelect_clicked()
{
    savePath = QFileDialog::getExistingDirectory(this, QString("保存路徑"),QString("../"));
    if(!savePath.isEmpty())
    {
        ui->linePath->setText(savePath);
        if(!head)ui->ButtonSave->setEnabled(true);
    }
}

void client::on_ButtonSave_clicked()
{
    ui->ButtonSave->setEnabled(false);
    userSocket.write("ok");
    openFile = acpFile.open(QIODevice::WriteOnly);
}

主要運用函數:

  • QString QFileDialog::getExistingDirectory() //靜態函數 獲取文件路徑

  • QString QFileDialog::getOpenFileName()//靜態函數 獲取要打開文件的文件路徑文件名

  • QTcpSocket -> isValid();            //如果套節字有效返回true,否則返回false;

  • void setenable();  //設置控制項激活狀態

註意事項:

在伺服器端發送文件,採取的是分段讀取文件,邊讀邊發的方式發送,而在客戶端採取每次接收每次發送的全部內容方式保存文件

起初有採取過在伺服器端all = readAall();write(all);一次性讀取發送;但在測試中發現,實際發送大文件時還是會自動被分成多次(只是簡單測試,不排除受其他因素影響)

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JS排序之快速排序 一個數組中的數據,選擇索引為(2/數組長度)的那個數據作為基數,數組中的其他數據與它對比,比它數值小的放在做數組,比它數值大的放在右數組,最後組合 左數組+基數+右數組,其中,左數組和右數組再調該方法,也就是遞歸調用,當數組長度小於2時,停止。 ...
  • 文章系國內領先的 ITOM 管理平臺供應商 OneAPM 編譯呈現。 網頁性能是一個豐富且又複雜的話題。在本帖中,我們會將討論的範圍局限在前端 JavaScript 框架上,探究相對於另外一種框架而言,使用當前的框架會如何影響您的應用程式的性能。我們會特別關註兩點: (1)某種框架要使用多長的時間來 ...
  • $("[id*='Custom']").removeAttr("disabled") ...
  • 一、學習目標 瞭解工廠模式基本概念 瞭解工廠模式優點 工廠模式實例 二、基本概念 工廠模式(Factory Pattern)是Java中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共 ...
  • 本篇內容基於 Python3 TensorFlow 1.4 版本。 本節內容 本節通過最簡單的示例 —— 平面擬合來說明 TensorFlow 的基本用法。 構造數據 TensorFlow 的引入方式是: 接下來我們構造一些隨機的三維數據,然後用 TensorFlow 找到平面去擬合它,首先我們用 ...
  • Web應用中通常需要訪問的Servlet API就是HttpServletRequest、HttpSession和ServletContext,這三個介面分別代表JSP內置對象中的request、session和application。 1.使用Struts2提供的ActionContext類來訪問 ...
  • Java DBCP連接池設置以及說明 ...
  • Markdown 語法快速入門 [TOC](博客園Markdown引擎暫不支持) 標題 第一級標題 第二級標題 第六級標題 強調 斜體字 : _斜體字_: 加粗 : __斜體字__: 列表 無序列表: 有序列表: 任務列表: [ ]未完成任務 [x] 已完成任務 插入鏈接及圖片 插入鏈接: Eg. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...