8.9更新說明html
以下圖所示,支持十六進制收發,下載地址已經更新.源碼下載地址:https://download.csdn.net/download/qq_37997682/11504836服務器
在上章 48.QT-網絡通訊講解1,咱們學習了網絡通訊基礎後,本章便來實戰一篇.網絡
PS:支持客戶端和服務器,提供源碼,而且服務器支持多客戶端連入,而且能夠指定與個別客戶端發送數據,也能夠給全部連入的客戶端發送數據.socket
1.效果圖所下所示:tcp
以下圖所示,當服務器狀態下,若是有客戶端連入,會提示客戶端信息:ide
2.效果操做函數
客戶端操做:post
服務器操做:學習
從上面操做能夠看出,服務器支持多客戶端連入,而且能夠指定與個別客戶端發送數據,也能夠給全部連入的客戶端發送數據.ui
3.首先建立UI
4.注意事項
無論是服務器仍是客戶端,均可以經過peerAddress()和peerPort()來獲取目標地址和目標端口
4.1服務器監聽時
好比服務器,則能夠經過QTcpSocket的peerAddress()則能夠獲取連入的客戶端地址
也能夠經過children()來獲取全部連入的客戶端(須要注意的是也會獲取到服務器自己的tcp地址和端口),示例以下:
QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>(); foreach (QTcpSocket *tcp, m_tcps) { qDebug() << "Address:" << tcp->peerAddress (); qDebug() << "Port:" << tcp->peerPort (); }
若是咱們只向連入的客戶端某個端口發送數據時,就能夠經過上面的方式篩選出來.
這樣作的話若是以爲很麻煩,也能夠將以前鏈接上的客戶端存到QList裏再進行篩選.
4.2 QTcpSocket步驟
注意:若是read到的數據長度量不是本身想要的,此時咱們即可以經過bytesAvailable()來讀取接收到的數據長度量.當達到多少時,再進行read ()讀取.
4.3 QTcpServer步驟
5.代碼介紹
5.1 頭文件介紹
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QTcpSocket> #include <QTcpServer> #include <QMessageBox> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT QTcpSocket m_client; QTcpServer m_server; QString targetAddr; int targetPort; public: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: void on_btn_switch_clicked(); void on_tcpMode_currentIndexChanged(int index); void on_btn_send_clicked(); //發送按鈕 void on_betn_clear_clicked(); //清空按鈕 //客戶端槽函數 void onClientConnected(); void onClientDisconnected(); void onClientDataReady(); void onClientBytesWritten(qint64 bytes); void onClientErr(QAbstractSocket::SocketError socketError); //服務器槽函數 void onServerNewConnection(); void onServerConnected(); void onServerDisconnected(); void onServerDataReady(); void onServerBytesWritten(qint64 bytes); private: void startConnect(bool ison); void initClientSignals(); //初始化客戶端信號槽 bool startClient(); //啓動客戶端 void initServerSignals(); //初始化客戶端信號槽 bool startServer(); //啓動服務器 Ui::Widget *ui; }; #endif // WIDGET_H
5.2 widget.cpp介紹
該cpp主要是用來處理界面操做的函數
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); startConnect(false); on_tcpMode_currentIndexChanged(0); initClientSignals(); //初始化客戶端信號槽 initServerSignals(); //初始化服務器信號槽 //限制只能數字輸入 QRegExp regx("[0-9]+$"); QValidator *validator = new QRegExpValidator(regx, this ); ui->ipAddr1->setValidator( validator ); ui->ipAddr2->setValidator( validator ); ui->ipAddr3->setValidator( validator ); ui->ipAddr4->setValidator( validator ); ui->ipPort->setValidator( validator ); } void Widget::on_tcpMode_currentIndexChanged(int index) { if(index==0) //clent { ui->ipPortLabel->setText("服務器端口號:"); ui->ipAddLabel->show(); ui->ipAdds->show(); ui->targetLabel->hide(); ui->targetObject->hide(); } else { ui->ipAddLabel->hide(); ui->ipAdds->hide(); ui->ipPortLabel->setText("本地端口號:"); } } void Widget::on_btn_switch_clicked() //切換鏈接開關 { bool ret; if(ui->btn_switch->text()=="打開鏈接") { if(ui->tcpMode->currentIndex()==0) //啓動客戶端 ret=startClient() ; else ret=startServer(); if(ret) startConnect(true); } else { if(ui->tcpMode->currentIndex()==0) //啓動客戶端 m_client.close(); else { if( m_server.isListening() ) { QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>(); foreach (QTcpSocket *tcp, m_tcps) { tcp->close(); } m_server.close(); } } startConnect(false); } } void Widget::startConnect(bool ison) { if(!ison) { ui->btn_switch->setStyleSheet("color:blue;border: 1px solid blue"); ui->btn_switch->setText("打開鏈接"); //使能 ui->ipAddr1->setEnabled(true); ui->ipAddr2->setEnabled(true); ui->ipAddr3->setEnabled(true); ui->ipAddr4->setEnabled(true); ui->tcpMode->setEnabled(true); ui->ipPort->setEnabled(true); ui->localPort->setText(""); } else { ui->btn_switch->setStyleSheet("color:red;border: 1px solid red"); ui->btn_switch->setText("關閉鏈接"); //失能 ui->ipAddr1->setEnabled(false); ui->ipAddr2->setEnabled(false); ui->ipAddr3->setEnabled(false); ui->ipAddr4->setEnabled(false); ui->tcpMode->setEnabled(false); ui->ipPort->setEnabled(false); targetAddr=""; targetPort=0; ui->sendLenLabel->setText("0"); } } void Widget::on_betn_clear_clicked() { ui->recvEdit->clear(); targetAddr=""; targetPort=0; } void Widget::on_btn_send_clicked() { if(ui->btn_switch->text()!="打開鏈接") { if(ui->tcpMode->currentIndex()==0) //客戶端 { m_client.write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length()); } else { if(ui->targetObject->currentText()!="全部對象") { QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>(); foreach (QTcpSocket *tcp, m_tcps) { if(ui->targetObject->currentText() == tcp->objectName()) { tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length()); break; } } } else //全部鏈接上的客戶端都發送一遍 { QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>(); foreach (QTcpSocket *tcp, m_tcps) { tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length()); } } } } } Widget::~Widget() { QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>(); foreach (QTcpSocket *tcp, m_tcps) { tcp->close(); } if(m_client.isOpen()) { m_client.close(); qDebug()<<"m_client close"; } delete ui; }
5.3 clentHandler.cpp介紹
該cpp主要用來處理客戶端操做相關的文件.
#include "widget.h" #include "ui_widget.h" void Widget::initClientSignals() //初始化客戶端信號槽 { connect(&m_client, SIGNAL(connected()), this, SLOT(onClientConnected())); connect(&m_client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); connect(&m_client, SIGNAL(readyRead()), this, SLOT(onClientDataReady())); connect(&m_client, SIGNAL(bytesWritten(qint64)), this, SLOT(onClientBytesWritten(qint64))); connect(&m_client, SIGNAL(error(QAbstractSocket::SocketError )), this, SLOT(onClientErr(QAbstractSocket::SocketError))); } bool Widget::startClient() //啓動客戶端 { QString ip = QString("%1.%2.%3.%4").arg(ui->ipAddr1->text()).arg(ui->ipAddr2->text()).arg(ui->ipAddr3->text()).arg(ui->ipAddr4->text()); qDebug()<<ip; m_client.connectToHost(ip, ui->ipPort->text().toInt()); if(m_client.waitForConnected(800)) { return true; } else { QMessageBox::information(this,"提示",QString("鏈接超時"),QMessageBox::Ok); return false; } } void Widget::onClientConnected() { startConnect(true); QMessageBox::information(this,"提示","鏈接成功",QMessageBox::Ok); ui->localPort->setText(QString("%1").arg(m_client.localPort())); //顯示本地端口號 } void Widget::onClientDisconnected() { startConnect(false); QMessageBox::information(this,"提示","斷開完成",QMessageBox::Ok); } void Widget::onClientDataReady() { if(m_client.peerAddress().toString()!=targetAddr || m_client.peerPort()!=targetPort ) { targetAddr = m_client.peerAddress().toString(); targetPort = m_client.peerPort(); ui->recvEdit->insertPlainText("[接受來自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n"); } ui->recvEdit->moveCursor(QTextCursor::End); ui->recvEdit->insertPlainText(QString::fromLocal8Bit(m_client.readAll())+"\r\n"); } void Widget::onClientBytesWritten(qint64 bytes) { qDebug() << "onBytesWritten:" << bytes; ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes)); } void Widget::onClientErr(QAbstractSocket::SocketError socketError) { qDebug()<<"onClientErr:"<<socketError; m_client.close(); startConnect(false); QMessageBox::information(this,"提示",QString("鏈接失敗:%1").arg((int)socketError),QMessageBox::Ok); }
5.4 serverHandler.cpp介紹
該cpp主要用來處理服務器操做相關的文件
#include "widget.h" #include "ui_widget.h" void Widget::initServerSignals() //初始化信號槽 { connect(&m_server, SIGNAL(newConnection()), this, SLOT(onServerNewConnection())); } bool Widget::startServer() //啓動服務器 { if(m_server.listen(QHostAddress::AnyIPv4,ui->ipPort->text().toInt())) //只監聽IPV4的全部客戶端 { ui->targetLabel->show(); ui->targetObject->show(); ui->localPort->setText(QString("%1").arg(m_server.serverPort())); return true; } else return false; } void Widget::onServerNewConnection() { qDebug() << "onNewConnection"; QTcpSocket* tcp = m_server.nextPendingConnection(); //獲取新的客戶端信息 QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort()); ui->targetObject->addItem(info); QMessageBox::information(this,"提示",QString("新的客戶端連入:%1").arg(info),QMessageBox::Ok); tcp->setObjectName(info); //設置名稱,方便查找 connect(tcp, SIGNAL(connected()), this, SLOT(onServerConnected())); connect(tcp, SIGNAL(disconnected()), this, SLOT(onServerDisconnected())); connect(tcp, SIGNAL(readyRead()), this, SLOT(onServerDataReady())); connect(tcp, SIGNAL(bytesWritten(qint64)), this, SLOT(onServerBytesWritten(qint64))); } void Widget::onServerConnected() { } void Widget::onServerDisconnected() { QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender()); if( tcp != NULL ) //從鏈接對象中移除掉 { qDebug() << "onServerDisconnected"; qDebug() << "Local Address:" << tcp->peerAddress(); qDebug() << "Local Port:" << tcp->peerPort(); QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort()); QMessageBox::information(this,"提示",QString("客戶端斷開鏈接:%1").arg(info),QMessageBox::Ok); int index = ui-> targetObject ->findText(info); if(index>=0) ui->targetObject->removeItem(index); } } void Widget::onServerDataReady() { QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender()); if(tcp->peerAddress().toString()!=targetAddr || tcp->peerPort()!=targetPort ) { targetAddr = tcp->peerAddress().toString(); targetPort = tcp->peerPort(); ui->recvEdit->insertPlainText("[接受來自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n"); } ui->recvEdit->moveCursor(QTextCursor::End); ui->recvEdit->insertPlainText(QString::fromLocal8Bit(tcp->readAll())+"\r\n"); } void Widget::onServerBytesWritten(qint64 bytes) { qDebug() << "onBytesWritten:" << bytes; ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes)); }