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è)無符號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è)路由器上的第多少臺,最多255臺,0一般是用來做網(wǎng)關(guān)的。
ip對應(yīng)的還有一個(gè)子網(wǎng)掩碼
子網(wǎng)掩碼(subnet mask)又叫網(wǎng)絡(luò)掩碼、地址掩碼、子網(wǎng)絡(luò)遮罩,它是一種用來指明一個(gè)IP地址的哪些位標(biāo)識的是主機(jī)所在的子網(wǎng),以及哪些位標(biāo)識的是主機(jī)的位掩碼。子網(wǎng)掩碼不能單獨(dú)存在,它必須結(jié)合IP地址一起使用。子網(wǎng)掩碼只有一個(gè)作用,就是將某個(gè)IP地址劃分成網(wǎng)絡(luò)地址和主機(jī)地址兩部分。子網(wǎng)掩碼--屏蔽一個(gè)IP地址的網(wǎng)絡(luò)部分的"全1"比特模式。
對于A類地址來說,默認(rèn)的子網(wǎng)掩碼是255.0.0.0;
對于B類地址來說默認(rèn)的子網(wǎng)掩碼是255.255.0.0;
對于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ī)對應(yīng)在網(wǎng)絡(luò)上就是一臺主機(jī),在這臺Pc機(jī)上面會有多個(gè)進(jìn)程需要訪問網(wǎng)絡(luò),所以需要在Pc機(jī)的操作系統(tǒng)上面去有處理網(wǎng)絡(luò)的東西,前人就定了一個(gè)“套接字”來專門處理網(wǎng)絡(luò)(源IP地址和目的IP地址以及源端口號和目的端口號的組合稱為套接字)。把一個(gè)主機(jī)拆分為N個(gè)網(wǎng)絡(luò)端口(Port)一共會有65536個(gè),short的最大范圍,在這些端口當(dāng)中,要注意0-5000的端口一般不用,用來給操作系統(tǒng)的進(jìn)程來使用的。一般會用靠后一點(diǎn)的端口,這樣比較安全,當(dāng)然還有一些端口,比如8080端口也會用的比較多,一個(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ò)通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API),對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ù)器來對應(yīng)多個(gè)客戶端的處理,一對多的關(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ù)端和接收客戶端的對象
QTcpServer *tcpServer;
QTcpSocket *tcpSocket;
};
#endif // WIDGET_Htcpsever.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ù)器對象
tcpSocket=NULL;
tcpServer=new QTcpServer(this);
//5、有客戶端連接服務(wù)器發(fā)送信號
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()
+" "+
"端口號:" + QString::number(tcpSocket->peerPort());
ui->textBrowser_clientInfo->setText(client_Info);
//發(fā)送信號和讀取關(guān)聯(lián)
connect(tcpSocket,
SIGNAL(readyRead()),
this,
SLOT(readyRead_Slot()));
//斷開信號關(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_Htcpclient.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,連不上會返回假
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對象
QUdpSocket *udpSocket;
};
#endif // WIDGET_Hudp_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)建對象
udpSocket=new QUdpSocket(this);
//4、關(guān)聯(lián)讀取的信號與槽
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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析
這篇文章主要為大家介紹了OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
C++ explicit構(gòu)造函數(shù)實(shí)例解析
這篇文章主要介紹了C++ explicit構(gòu)造函數(shù),需要的朋友可以參考下2014-07-07
C++?OpenCV實(shí)戰(zhàn)之零部件的自動光學(xué)檢測
這篇文章主要為大家介紹一個(gè)C++?OpenCV的實(shí)戰(zhàn)——零部件的自動光學(xué)檢測,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-09-09
C++實(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)存分配與動態(tài)內(nèi)存分配
內(nèi)存分配 (Memory Allocation) 是指為計(jì)算機(jī)程序或服務(wù)分配物理內(nèi)存空間或虛擬內(nèi)存空間的一個(gè)過程,本文主要介紹了C++的靜態(tài)內(nèi)存分配與動態(tài)內(nèi)存分配,感興趣的同學(xué)可以參考閱讀2023-06-06

