Qt中網(wǎng)絡(luò)編程的實(shí)現(xiàn)
由于我沒有系統(tǒng)的分享一些簡單的計(jì)算機(jī)網(wǎng)絡(luò)有關(guān)的,下面只是簡單講講tcp/ip協(xié)議簇和udp
一、tcp/IP協(xié)議簇與udp
1、TCP/IP協(xié)議族
TCP/IP實(shí)際上是一個(gè)協(xié)同工作的通信家族,為網(wǎng)絡(luò)通信提供通路。為方便討論TCP/IP協(xié)議族,大體上分為三部分:
①、Internet協(xié)議(IP)。
②、傳輸控制協(xié)議(TCP)和用戶數(shù)據(jù)報(bào)協(xié)議(UDP)。
③、處于TCP和UDP之上的一組應(yīng)用協(xié)議。它們包括:Telnet,文件傳送協(xié)議(FTP),域名服務(wù)協(xié)議(DNS)和簡單的郵件傳送程序(SMTP)等。
2、udp
udp協(xié)議(用戶數(shù)據(jù)報(bào)協(xié)議),它與TCP協(xié)議完全相反。提供不可靠、無連接和基于數(shù)據(jù)報(bào)的服務(wù)。不可靠意味著UDP協(xié)議無法保證數(shù)據(jù)從發(fā)送端正確的發(fā)送到接收端。如果數(shù)據(jù)在中途丟失,或者目的端通過數(shù)據(jù)校驗(yàn)發(fā)現(xiàn)數(shù)據(jù)錯(cuò)誤而將其丟棄,則UDP協(xié)議的應(yīng)用程序通常要自己處理數(shù)據(jù)確認(rèn)、超時(shí)重傳等邏輯性。
3、常用的通訊協(xié)議小結(jié)
1.3.1、tcp/ip
tcp只需要知道它是一種通訊方式就可以了,還有一個(gè)udp,那這兩者之間的關(guān)系是什么。TCP/IP協(xié)議是一個(gè)協(xié)議簇。里面包括很多協(xié)議的。UDP只是其中的一個(gè)。之所以命名為TCP/IP協(xié)議,因?yàn)門CP,IP協(xié)議是兩個(gè)很重要的協(xié)議,就用他兩命名了,tcp是打電話,udp是發(fā)短信。
Ip(網(wǎng)絡(luò)之間互連的協(xié)議,外文是Internet Protocol的外語縮寫,中文縮寫為“網(wǎng)協(xié)”??s寫為IP),通過設(shè)置ip地址就可以去訪問網(wǎng)絡(luò),用的最多的ip協(xié)議是ipv4(ip協(xié)議v版本4),還有一個(gè)版本為ipv6,ipv4不夠用了,Ipv4版本是32位的,一般分成4段,內(nèi)存中就是一個(gè)無符號(hào)32位的整數(shù),ipv6的話就是一個(gè)64位的整數(shù),通過位數(shù)就知道ipv4和ipv6的區(qū)別,能保存多少個(gè)的地址。只不過用戶并不需要去搞清楚。
現(xiàn)在常用的ip是127.0.0.1這個(gè)樣子,點(diǎn)分格式(一個(gè)字符串)。點(diǎn)所隔開的區(qū)間就是一個(gè)字符。Ip地址有ABC三類地址。前三段是用來確定路由器,確定主機(jī)連上外圍網(wǎng)上的哪一個(gè)路由,最后一段用來確定主機(jī),確定主機(jī)是這個(gè)路由器上的第多少臺(tái),最多255臺(tái),0一般是用來做網(wǎng)關(guān)的。
ip對(duì)應(yīng)的還有一個(gè)子網(wǎng)掩碼
子網(wǎng)掩碼(subnet mask)又叫網(wǎng)絡(luò)掩碼、地址掩碼、子網(wǎng)絡(luò)遮罩,它是一種用來指明一個(gè)IP地址的哪些位標(biāo)識(shí)的是主機(jī)所在的子網(wǎng),以及哪些位標(biāo)識(shí)的是主機(jī)的位掩碼。子網(wǎng)掩碼不能單獨(dú)存在,它必須結(jié)合IP地址一起使用。子網(wǎng)掩碼只有一個(gè)作用,就是將某個(gè)IP地址劃分成網(wǎng)絡(luò)地址和主機(jī)地址兩部分。子網(wǎng)掩碼--屏蔽一個(gè)IP地址的網(wǎng)絡(luò)部分的"全1"比特模式。
對(duì)于A類地址來說,默認(rèn)的子網(wǎng)掩碼是255.0.0.0;
對(duì)于B類地址來說默認(rèn)的子網(wǎng)掩碼是255.255.0.0;
對(duì)于C類地址來說默認(rèn)的子網(wǎng)掩碼是255.255.255.0。
子網(wǎng)掩碼,一般是255.255.255.0。
ip地址的前三段來確定路由器,最后一段是主機(jī)位置。所以子網(wǎng)掩碼理解為子網(wǎng)遮罩編碼。
1.3.2、Socket
pc機(jī)對(duì)應(yīng)在網(wǎng)絡(luò)上就是一臺(tái)主機(jī),在這臺(tái)Pc機(jī)上面會(huì)有多個(gè)進(jìn)程需要訪問網(wǎng)絡(luò),所以需要在Pc機(jī)的操作系統(tǒng)上面去有處理網(wǎng)絡(luò)的東西,前人就定了一個(gè)“套接字”來專門處理網(wǎng)絡(luò)(源IP地址和目的IP地址以及源端口號(hào)和目的端口號(hào)的組合稱為套接字)。把一個(gè)主機(jī)拆分為N個(gè)網(wǎng)絡(luò)端口(Port)一共會(huì)有65536個(gè),short的最大范圍,在這些端口當(dāng)中,要注意0-5000的端口一般不用,用來給操作系統(tǒng)的進(jìn)程來使用的。一般會(huì)用靠后一點(diǎn)的端口,這樣比較安全,當(dāng)然還有一些端口,比如8080端口也會(huì)用的比較多,一個(gè)進(jìn)程只能占用一個(gè)端口,不能多進(jìn)程占用同一個(gè)端口的情況,一個(gè)進(jìn)程可以占用多個(gè)端口的,或者嚴(yán)謹(jǐn)一點(diǎn),同一時(shí)刻一個(gè)端口只能由一個(gè)進(jìn)程使用。網(wǎng)絡(luò)上的兩個(gè)程序通過一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個(gè)連接的一端稱為一個(gè)socket。
建立網(wǎng)絡(luò)通信連接至少要一對(duì)端口號(hào)(socket)。socket本質(zhì)是編程接口(API),對(duì)TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口。通常也稱作"套接字",用于描述IP地址和端口,是一個(gè)通信鏈的句柄,可以用來實(shí)現(xiàn)不同虛擬機(jī)或不同計(jì)算機(jī)之間的通信。
1.3.3、tcp通信模型
c/s模型,客戶端(c)/服務(wù)器(s)模型,一個(gè)服務(wù)器來對(duì)應(yīng)多個(gè)客戶端的處理,一對(duì)多的關(guān)系。以下步驟沒有特殊指明,服務(wù)器和客戶端是都需要有的步驟:
1.準(zhǔn)備工作,根據(jù)自己使用語言所有庫函數(shù)導(dǎo)入;
2. 確定版本信息,要確定socket版本,ip是有v4和v6兩個(gè)版本的;
3. 創(chuàng)建socket,使用socket函數(shù)
4. 初始化協(xié)議地址簇 ;
5. 綁定,使用bind函數(shù),把協(xié)議地址簇和socket綁定在一起,客戶端不要綁定;
6. 服務(wù)器端有,需要監(jiān)聽 listen函數(shù),客戶端不需要這一步;
7. 服務(wù)器端需要接受連接,客戶端需要連接服務(wù)器;
8. 連接完成之后,開始通訊,收發(fā)數(shù)據(jù);
9. 通訊完成后關(guān)閉socket;
二、Qt中的tcp(這里只展示代碼)
開始前在項(xiàng)目的.pro文件中加入這個(gè)network
1、tcpsever
tcpsever.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QNetworkInterface>//網(wǎng)絡(luò)信息 #include <QHostAddress>//id地址 #include <QTcpServer> //tcp協(xié)議 #include <QTcpSocket> //socket套接字 #include <QDebug> #include <QMessageBox> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: void on_pushButton_listen_clicked(); void on_pushButton_send_clicked(); void newconnectslot();//連接 void readyRead_Slot();//讀取信息 void disconnected_Slot();//斷開 private slots: QString list_all_IPV4(); private: Ui::Widget *ui; //2、設(shè)置服務(wù)端和接收客戶端的對(duì)象 QTcpServer *tcpServer; QTcpSocket *tcpSocket; }; #endif // WIDGET_H
tcpsever.cpp
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //3、創(chuàng)建服務(wù)器對(duì)象 tcpSocket=NULL; tcpServer=new QTcpServer(this); //5、有客戶端連接服務(wù)器發(fā)送信號(hào) connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newconnectslot())); QMessageBox::information(this,"本機(jī)聯(lián)網(wǎng)端口顯示",this->list_all_IPV4()); } Widget::~Widget() { delete ui; } void Widget::on_pushButton_listen_clicked() { //4、開始監(jiān)聽 QString sever_Address = ui->lineEdit_address->text(); quint16 port = ui->lineEdit_port->text().toInt(); QHostAddress host = QHostAddress(sever_Address); if(!tcpServer->isListening()){ //監(jiān)聽綁定的ip地址 if(!tcpServer->listen(host,port)) { qDebug()<<tcpServer->errorString(); return; }else{ qDebug()<<"監(jiān)聽成功"; ui->pushButton_listen->setText("停止監(jiān)聽"); } }else{ tcpServer->close(); ui->pushButton_listen->setText("開始監(jiān)聽"); } } QString Widget::list_all_IPV4(){ QString str; QList<QHostAddress> list=QNetworkInterface::allAddresses(); //獲取本機(jī)的所有網(wǎng)卡的ip地址 foreach (QHostAddress address, list) { if(address.isNull()) continue; QAbstractSocket::NetworkLayerProtocol portocol=address.protocol(); //只提取IPv4地址 if(portocol!=QAbstractSocket::IPv4Protocol) continue; str = str +'\n\t'+address.toString() + '\n\t\t'; } return str; }; void Widget::newconnectslot(){ //6、接受客戶端 tcpSocket = tcpServer->nextPendingConnection(); QString client_Info = "客戶端:" + tcpSocket->peerAddress().toString() +" "+ "端口號(hào):" + QString::number(tcpSocket->peerPort()); ui->textBrowser_clientInfo->setText(client_Info); //發(fā)送信號(hào)和讀取關(guān)聯(lián) connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot())); //斷開信號(hào)關(guān)聯(lián)客戶端 connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected_Slot())); }; void Widget::on_pushButton_send_clicked() { if(tcpSocket != nullptr) { if(tcpSocket->isWritable()) { QString send = ui->plainTextEdit_sendInfo->toPlainText(); QByteArray sendarr = send.toLocal8Bit();//本地字符集與Unicode的轉(zhuǎn)換 tcpSocket->write(sendarr); } } } void Widget::readyRead_Slot(){ if(tcpSocket != nullptr) { if(tcpSocket->isReadable()) { QByteArray recvAll = tcpSocket->readAll();//將數(shù)據(jù)全部讀取 QString str = str.fromLocal8Bit(recvAll.data()); ui->textBrowser_recv->append(str); } } }; void Widget::disconnected_Slot(){ QMessageBox::information(this,"Client Close Signal","有客戶離開"); };
2、tcpclient
tcpclient,h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QNetworkInterface>//網(wǎng)絡(luò)信息 #include <QHostAddress>//id地址 #include <QTcpServer> //tcp協(xié)議 #include <QTcpSocket> //socket套接字 #include <QDebug> #include <QMessageBox> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: void on_pushButton_listen_clicked(); void on_pushButton_send_clicked(); void readyRead_Slot();//讀取信息 void disconnected_Slot();//斷開 private: Ui::Widget *ui; QTcpSocket *client; bool socket_state; }; #endif // WIDGET_H
tcpclient.cpp
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); client = new QTcpSocket(this); socket_state = false; connect(client, SIGNAL(disconnected()), this, SLOT(disconnected_Slot())); connect(client, SIGNAL(readyRead()), this, SLOT(readyRead_Slot())); } Widget::~Widget() { delete ui; } void Widget::on_pushButton_listen_clicked() { QString ipAddress = ui->lineEdit_address->text(); qint16 port = ui->lineEdit_port->text().toInt(); if(!socket_state) { client->connectToHost(ipAddress,port); if(client->waitForConnected(3000)){//等待3s,連不上會(huì)返回假 ui->pushButton_listen->setText("斷開連接"); socket_state = true; }else{ qDebug()<<client->errorString(); return; } }else{ client->close(); QMessageBox::information(this,"消息提示","已經(jīng)離開!",QMessageBox::Yes); ui->pushButton_listen->setText("連接"); socket_state = false; } } void Widget::readyRead_Slot(){ QByteArray data=client->readAll(); QString str=str.fromLocal8Bit(data.data()); ui->textBrowser_recv->append(str); }; void Widget::disconnected_Slot(){ qDebug()<<"離開"; } void Widget::on_pushButton_send_clicked() { QString datastr = ui->plainTextEdit_sendInfo->toPlainText(); QByteArray da = datastr.toLocal8Bit(); if(client->isOpen() && client->isValid()){ client->write(da); } }
三、QT中的Udp
初始操作同TCP操作
udp_test.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> //1、包含相關(guān)的頭文件 #include <QHostAddress> #include <QUdpSocket> #include <QDebug> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: void on_pushButtonSend_clicked(); void readyReadSlot(); void on_pushButtonCLose_clicked(); private: Ui::Widget *ui; //2、定義udp對(duì)象 QUdpSocket *udpSocket; }; #endif // WIDGET_H
udp_test.cpp
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //3、創(chuàng)建對(duì)象 udpSocket=new QUdpSocket(this); //4、關(guān)聯(lián)讀取的信號(hào)與槽 connect(udpSocket,SIGNAL(readyRead()),this,SLOT(readyReadSlot())); } Widget::~Widget() { delete ui; } void Widget::on_pushButtonSend_clicked() { udpSocket->writeDatagram(ui->plainTextEdit_sendInfo->toPlainText().toLocal8Bit(),//內(nèi)容 QHostAddress(ui->lineEditIp->text()),//發(fā)送ip ui->lineEditPort->text().toInt());//發(fā)送的地址 } void Widget::on_pushButtonCLose_clicked() { udpSocket->bind(ui->lineEditPort_2->text().toInt()); } void Widget::readyReadSlot(){ quint64 size = udpSocket->bytesAvailable();//讀取發(fā)過來的消息大小 QByteArray ba; ba.resize(size); QHostAddress address; quint16 port; udpSocket->readDatagram(ba.data(),size,&address,&port); QString str = QString::fromLocal8Bit(ba.data()); ui->textEdit_recvInfo->append(str); }
到此這篇關(guān)于Qt中網(wǎng)絡(luò)編程的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Qt網(wǎng)絡(luò)編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析
這篇文章主要為大家介紹了OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03C++ explicit構(gòu)造函數(shù)實(shí)例解析
這篇文章主要介紹了C++ explicit構(gòu)造函數(shù),需要的朋友可以參考下2014-07-07C++?OpenCV實(shí)戰(zhàn)之零部件的自動(dòng)光學(xué)檢測
這篇文章主要為大家介紹一個(gè)C++?OpenCV的實(shí)戰(zhàn)——零部件的自動(dòng)光學(xué)檢測,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-09-09C++實(shí)現(xiàn)幸運(yùn)大抽獎(jiǎng)(QT版)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)幸運(yùn)大抽獎(jiǎng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配
內(nèi)存分配 (Memory Allocation) 是指為計(jì)算機(jī)程序或服務(wù)分配物理內(nèi)存空間或虛擬內(nèi)存空間的一個(gè)過程,本文主要介紹了C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配,感興趣的同學(xué)可以參考閱讀2023-06-06