Qt5开发学习之网络与通信(十二)
2017-09-07 16:13
585 查看
在应用程序开发中网络编程非常重要,目前互联网通行的TCP/IP协议,自上而下分为四层:应用层、传输层、网络层和网络接口层。实际编写网络应用程序时只使用到传输层和应用层,所用到的协议主要为UDP、TCP、HTTP和FTP。 虽然目前主流的操作系统都提供了统一的套接字抽象编程接口,用于编写不同层次的网络程序,但是这种方式比较繁琐,甚至有时需要应用底层操作系统相关数据结构,Qt提供了一个网络模块QtNetwork完美解决了问题。
获取本机网络信息
在网络应用中,经常需要获得本机的主机名、IP地址和硬件地址等信息。运用QHostInfo、QNetworkInterface、QNetworkAddressEntry可获得本机信息。实例使用网络模块获取主机名和IP地址:
#include <QWidget> #include <QLabel> #include <QPushButton> #include <QLineEdit> #include <QGridLayout> #include <QMessageBox> // 使用网络模块需要在pro文件中添加:QT += network #include <QHostInfo> #include <QNetworkInterface> class NetworkInfo : public QWidget { Q_OBJECT public: NetworkInfo(QWidget *parent = 0); ~NetworkInfo(); void getHostInformation(); private: QLabel *hostLabel; QLineEdit *hostLineEdit; QLabel *ipLabel; QLineEdit *ipLineEdit; QPushButton *detailBtn; QGridLayout *mainLayout; public slots: void slotDetail(); };
#include "NetworkInfo.h" NetworkInfo::NetworkInfo(QWidget *parent) : QWidget(parent) { this->resize(500, 500); hostLabel = new QLabel(tr("HostName")); hostLineEdit = new QLineEdit; ipLabel = new QLabel(tr("ipAdress")); ipLineEdit = new QLineEdit; detailBtn = new QPushButton(tr("Detail")); mainLayout = new QGridLayout(this); mainLayout->addWidget(hostLabel, 0, 0); mainLayout->addWidget(hostLineEdit, 0, 1); mainLayout->addWidget(ipLabel, 1, 0); mainLayout->addWidget(ipLineEdit, 1, 1); mainLayout->addWidget(detailBtn, 2, 0, 1, 2); getHostInformation(); connect(detailBtn, &QPushButton::clicked, this, &NetworkInfo::slotDetail); } NetworkInfo::~NetworkInfo() { } void NetworkInfo::getHostInformation() { // 获得主机名 QString localHostName = QHostInfo::localHostName(); hostLineEdit->setText(localHostName); // 根据主机名获得相关主机信息 QHostInfo hostInfo = QHostInfo::fromName(localHostName); // 获得主机IP地址列表 QList<QHostAddress> listAddress = hostInfo.addresses(); if (!listAddress.isEmpty()) { ipLineEdit->setText(listAddress.first().toString()); } } void NetworkInfo::slotDetail() { QString detail = ""; // 主机IP地址和网络接口列表 QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); for (int i = 0; i < list.count(); ++i) { QNetworkInterface interface = list.at(i); // 获得网络接口名称 detail = detail + tr("设备:") + interface.name() + "\n"; // 获得硬件名称 detail = detail + tr("硬件地址:") + interface.hardwareAddress() + "\n"; QList<QNetworkAddressEntry> entryList = interface.addressEntries(); for (int j = 0; j < entryList.count(); ++j) { QNetworkAddressEntry entry = entryList.at(j); // 没个接口包含多个IP地址 detail = detail + "\t" + tr("IP地址:") + entry.ip().toString() + "\n"; detail = detail + "\t" + tr("子网掩码:") + entry.netmask().toString() + "\n"; detail = detail + "\t" + tr("广播地址:") + entry.broadcast().toString() + "\n"; } } QMessageBox::information(this, tr("Detail"), detail); }
获取到的如图:
基于UDP的网络广播程序
用户数据报协议(User Data Protocol,UDP)是一种简单轻量级、不可靠、面向数据报、无连接的传输层协议,可以应用在可靠性不是十分高的场合,如短消息和广播信息等。适合应用的场景有:
1、网络数据大多为短消息;
2、拥有大量客户端;
3、对数据安全性无特殊要求;
4、网络负担非常重,但对相应速度要求高。
UDP协议的工作
4000
原理:UDP客户端向UDP服务器发送一定长度的请求报文,报文大小的限制与各系统的协议实现相关,但不能超过其下层IP协议规定的64K;UDP服务器同样以报文形式做出响应。如果服务器未收到此请求,客户端不会重发,因此报文的传输是不可靠的。例如-QQ就是用UDP协议发送消息的, 因此有时会出现收不到消息的情况。
UDP的客户端与服务器是不建立连接,直接调用发送和接收函数进行数据通信的,Qt通过QUdpSocket类实现UDP协议的编程。
首先模拟UDP服务器编程:
#include <QDialog> #include <QLabel> #include <QPushButton> #include <QLineEdit> #include <QVBoxLayout> #include <QUdpSocket> #include <QTimer> class UDPServer : public QDialog { Q_OBJECT public: UDPServer(QWidget *parent = 0, Qt::WindowFlags f = 0); ~UDPServer(); private: QLabel *timerLabel; QLineEdit *textLineEdit; QPushButton *startBtn; QVBoxLayout *mainLayout; int port; bool isStarted; QUdpSocket *udpSocket; QTimer *timer; public slots: void StartBtnClicked(); void timeout(); };
#include "UDPServer.h" #include <QHostAddress> UDPServer::UDPServer(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { setWindowTitle(tr("UDPServer")); timerLabel = new QLabel(tr("计时器:")); textLineEdit = new QLineEdit(this); startBtn = new QPushButton(tr("Start")); mainLayout = new QVBoxLayout(this); mainLayout->addWidget(timerLabel); mainLayout->addWidget(textLineEdit); mainLayout->addWidget(startBtn); // 设置UDP的端口号参数,服务器 port = 9000; isStarted = false; udpSocket = new QUdpSocket(this); timer = new QTimer(this); connect(startBtn, &QPushButton::clicked, this, &UDPServer::StartBtnClicked); connect(timer, &QTimer::timeout, this, &UDPServer::timeout); } UDPServer::~UDPServer() { } void UDPServer::StartBtnClicked() { if (!isStarted) { startBtn->setText(tr("Stop")); timer->start(1000); isStarted = true; } else { startBtn->setText(tr("Start")); isStarted = false; timer->stop(); } } // 完成向端口发送广播信息的功能 void UDPServer::timeout() { QString msg = textLineEdit->text(); int length = 0; if (msg == "") { return; } // QHostAddress::Broadcast指定向地址广播发送 if ((length = udpSocket->writeDatagram(msg.toUtf8(), msg.length(), QHostAddress::LocalHost, port)) != msg.length()) { return; } }
接下来是接收服务器发送数据的客户端:
#include <QDialog> #include <QVBoxLayout> #include <QTextEdit> #include <QPushButton> #include <QUdpSocket> class UDPClient : public QDialog { Q_OBJECT public: UDPClient(QWidget *parent = 0); ~UDPClient(); public slots: void CloseBtnClicked(); void DataReceived(); private: QTextEdit *receiveTextEdit; QPushButton *closeBtn; QVBoxLayout *mainLayout; int port; QUdpSocket *udpSocket; };
#include "UDPClient.h" #include <QMessageBox> #include <QHostAddress> UDPClient::UDPClient(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("UDPClient")); receiveTextEdit = new QTextEdit(this); closeBtn = new QPushButton(tr("Close"), this); mainLayout = new QVBoxLayout(this); mainLayout->addWidget(receiveTextEdit); mainLayout->addWidget(closeBtn); port = 9000; udpSocket = new QUdpSocket(this); // 绑定到指定端口上 bool result = udpSocket->bind(port,QAbstractSocket::DontShareAddress); if (!result) { QMessageBox::information(this, tr("error"), tr("udp socket error")); return; } connect(closeBtn, &QPushButton::clicked, this, &UDPClient::CloseBtnClicked); //connect(udpSocket, &QUdpSocket::readyRead, this, &UDPClient::DataReceived); connect(udpSocket, SIGNAL(readyRead()), this, SLOT(DataReceived())); } UDPClient::~UDPClient() { } void UDPClient::CloseBtnClicked() { close(); } // 响应readyRead信号,一但UDPsocket有数据可读时即可通过 readDatagram()将数据显示出来 void UDPClient::DataReceived() { // 判断UdpSocket中是否有数据可读 while (udpSocket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(udpSocket->pendingDatagramSize()); udpSocket->readDatagram(datagram.data(), datagram.size()); QString msg = datagram.data(); receiveTextEdit->insertPlainText(msg); } }
完成之后,在服务端的输入框内输入数据点击start。客户端就会自动将接收到的数据显示出来:
基于TCP的网络聊天室程序
传输控制协议(Transmission Control Protocol ,TCP)是一种可靠地、面向连接的、面向数据流的传输协议,许多高层应用协议(HTTP、FTP)都是以它为基础的,TCP协议非常适合数据的连续传输。Qt中使用QTcpSocket和QTcpServer类实现TCP编程。
首先创建TCP服务端:
第一步,编写服务器的处理客户端的逻辑
#include <QObject> #include <QTcpSocket> class TcpClient : public QTcpSocket { Q_OBJECT public: TcpClient(QObject *parent = 0); signals: void updateClients(QString, int); void disconnected(int); protected slots: void dataReceived(); void slotDisConnect(); }; #include "TcpClient.h" TcpClient::TcpClient(QObject *parent) { // readyRead()当有数据来到时触发 connect(this, &TcpClient::readyRead, this, &TcpClient::dataReceived); // disconnected()在断开连接时触发 connect(this, SIGNAL(disconnected()), this, SLOT(slotDisConnect())); } // 当有数据到来时,触发信号 void TcpClient::dataReceived() { while (bytesAvailable() > 0) { int length = bytesAvailable(); char buf[1024]; read(buf, length); QString msg = buf; // 通知服务器向聊天室内所有成员发送广播信息 emit updateClients(msg, length); } } void TcpClient::slotDisConnect() { emit disconnected(this->socketDescriptor()); }
第二步,新建一个Server类,处理服务端逻辑
#include <QObject> #include <QTcpServer> #include "TcpClient.h" class Server : public QTcpServer { Q_OBJECT public: explicit Server(QObject *parent = 0, int port = 0); // QList用来保存每一个与客户端连接的TcpClientSocket QList<TcpClient *> tcpClientList; signals: void updateServer(QString, int); public slots: void updateClients(QString, int); void slotDisconnected(int); protected: void incomingConnection(int socketDescriptor); }; #include "Server.h" Server::Server(QObject *parent, int port) : QTcpServer(parent) { // QHostAddress::Any对指定端口的任意地址进行监听,Ipv4的任意地址为0.0.0.0 listen(QHostAddress::Any, port); // QHostAddress::Null 表示一个空地址 // QHostAddress::LocalHost 表示Ipv4的本机地址为127.0.0.1 // QHostAddress::Broadcast 表示广播地址为255.255.255.255 } // 将任意客户端发来的信息进行广播 void Server::updateClients(QString msg, int length) { // 通知服务器更新相应的显示状态 emit updateServer(msg, length); // 实现信息的广播 for (int i = 0; i < tcpClientList.count(); ++i) { QTcpSocket *item = tcpClientList.at(i); if (item->write(msg.toLatin1(), length) != length) { continue; } } } // 从tcpClientList列表中将断开连接的对象删除 void Server::slotDisconnected(int discriptor) { for (int i = 0; i < tcpClientList.count(); ++i) { QTcpSocket *item = tcpClientList.at(i); if (item->socketDescriptor() == discriptor) { tcpClientList.removeAt(i); return; } } return; } // 当出现一个新的连接时,触发函数,socketDescriptor指定了连接的Socket描述符 void Server::incomingConnection(int socketDescriptor) { TcpClient *tmp_client = new TcpClient(this); connect(tmp_client, SIGNAL(updateClients(QString,int)), this, SLOT(updateClients(QString,int))); connect(tmp_client, SIGNAL(disconnected(int)), this, SLOT(slotDisconnected(int))); tmp_client->setSocketDescriptor(socketDescriptor); tcpClientList.append(tmp_client); }
最后,制作服务端界面
#include <QDialog> #include <QListWidget> #include <QLabel> #include <QLineEdit> #incl d254 ude <QPushButton> #include <QGridLayout> #include "Server.h" class TcpServer : public QDialog { Q_OBJECT public: TcpServer(QWidget *parent = 0); ~TcpServer(); private: QListWidget *contentListWidget; QLabel *portLabel; QLineEdit *portLineEdit; QPushButton *createBtn; QGridLayout *mainLayout; int port; Server *server; public slots: void slotCreateServer(); void updateServer(QString, int); }; #include "TcpServer.h" TcpServer::TcpServer(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("TcpServer")); contentListWidget = new QListWidget; portLabel = new QLabel(tr("端口:")); portLineEdit = new QLineEdit; createBtn = new QPushButton(tr("创建聊天室")); mainLayout = new QGridLayout(this); mainLayout->addWidget(contentListWidget, 0, 0, 1, 2); mainLayout->addWidget(portLabel, 1, 0); mainLayout->addWidget(portLineEdit, 1, 1); mainLayout->addWidget(createBtn, 2, 0, 1, 2); port = 8010; portLineEdit->setText(QString::number(port)); connect(createBtn, &QPushButton::clicked, this, &TcpServer::slotCreateServer); } TcpServer::~TcpServer() { } // 创建一个TCP服务器 void TcpServer::slotCreateServer() { server = new Server(this, port); connect(server, SIGNAL(updateServer(QString,int)), this, SLOT(updateServer(QString,int))); createBtn->setEnabled(false); } // 更新服务器上的显示信息 void TcpServer::updateServer(QString msg, int length) { contentListWidget->addItem(msg.left(length)); }
接下来创建TCP聊天室的客户端:
#include <QDialog> #include <QListWidget> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QGridLayout> #include <QHostAddress> #include <QTcpSocket> class TcpClient : public QDialog { Q_OBJECT public: TcpClient(QWidget *parent = 0); ~TcpClient(); private: QListWidget *contentListWidget; QLineEdit *sendLineEdit; QPushButton *sendBtn; QLabel *userNameLabel; QLineEdit *userNameLineEdit; QLabel *serverIPLabel; QLineEdit *serverIPLineEdit; QLabel *portLabel; QLineEdit *portLineEdit; QPushButton *enterBtn; QGridLayout *mainLayout; bool status; int port; QHostAddress *serverIP; QString userName; QTcpSocket *tcpSocket; public slots: void slotEnter(); void slotConnect(); void slotDisconnect(); void slotDataRecevied(); void slotSend(); };
#include "TcpClient.h" #include <QMessageBox> #include <QHostInfo> TcpClient::TcpClient(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("TCP Client")); contentListWidget = new QListWidget; sendLineEdit = new QLineEdit; sendBtn = new QPushButton(tr("发送")); userNameLabel = new QLabel(tr("用户名:")); userNameLineEdit = new QLineEdit; serverIPLabel = new QLabel(tr("IP:")); serverIPLineEdit = new QLineEdit; portLabel = new QLabel(tr("端口:")); portLineEdit = new QLineEdit; enterBtn = new QPushButton(tr("进入聊天室")); mainLayout = new QGridLayout(this); mainLayout->addWidget(contentListWidget, 0, 0, 1, 2); mainLayout->addWidget(sendLineEdit, 1, 0); mainLayout->addWidget(sendBtn, 1, 1); mainLayout->addWidget(userNameLabel, 2, 0); mainLayout->addWidget(userNameLineEdit, 2, 1); mainLayout->addWidget(serverIPLabel, 3, 0); mainLayout->addWidget(serverIPLineEdit, 3, 1); mainLayout->addWidget(portLabel, 4, 0); mainLayout->addWidget(portLineEdit, 4, 1); mainLayout->addWidget(enterBtn, 5, 0, 1, 2); status = false; port = 8010; portLineEdit->setText(QString::number(port)); serverIP = new QHostAddress(); connect(sendBtn, &QPushButton::clicked, this, &TcpClient::slotSend); connect(enterBtn, &QPushButton::clicked, this, &TcpClient::slotEnter); sendBtn->setEnabled(false); } TcpClient::~TcpClient() { } void TcpClient::slotEnter() { if ( !status ) { // 获取输入的IP地址 QString ip = serverIPLineEdit->text(); // 判断给定的IP地址是否能够被正确解析 if (!serverIP->setAddress(ip)) { QMessageBox::information(this, tr("error"), tr("Server ip adress error")); return; } if (userNameLineEdit->text() == "") { QMessageBox::information(this, tr("error"), tr("User name error")); return; } userName = userNameLineEdit->text(); // 创建了一个TCP对象,并建立了连接 tcpSocket = new QTcpSocket(this); connect(tcpSocket, &QTcpSocket::connected, this, &TcpClient::slotConnect); connect(tcpSocket, &QTcpSocket::disconnected, this, &TcpClient::slotDisconnect); connect(tcpSocket, &QTcpSocket::readyRead, this, &TcpClient::slotDataRecevied); // 与TCP服务器端连接,连接成功后发出connected()信号 tcpSocket->connectToHost(*serverIP, port); status = true; } else { // 客户端主动断开连接并向服务器发送提示消息 int length = 0; QString msg = userName + tr(":Leave That Room"); if ( (length = tcpSocket->write(msg.toLatin1()), msg.length()) != msg.length()) { return; } // 与TCP服务器断开连接,断开后发送disconnect信号 tcpSocket->disconnectFromHost(); status = false; } } void TcpClient::slotConnect() { sendBtn->setEnabled(true); enterBtn->setText(tr("离开")); int length = 0; QString msg = userName + tr(":Enter This Room"); if ((length = tcpSocket->write(msg.toLatin1(), msg.length())) != msg.length()) { return; } } void TcpClient::slotDisconnect() { sendBtn->setEnabled(false); enterBtn->setText(tr("进入聊天室")); } void TcpClient::slotDataRecevied() { while (tcpSocket->bytesAvailable() > 0) { QByteArray datagram; datagram.resize(tcpSocket->bytesAvailable()); tcpSocket->read(datagram.data(), datagram.size()); QString msg = datagram.data(); contentListWidget->addItem(msg.left(datagram.size())); } } void TcpClient::slotSend() { if (sendLineEdit->text() == "") { return; } QString msg = userName + ":" + sendLineEdit->text(); tcpSocket->write(msg.toLatin1(), msg.length()); sendLineEdit->clear(); }
Qt网络应用初步开发
QUdpSocket、QTcpSocke、QTcpServer都是网络传输层上的类,他们封装实现的是低层次的网络进程通信的功能。而Qt网络应用开发是在此基础上进一步实现应用型的协议功能。应用协议(HTTP/FTP/SMTP)运行在TCP/UDP之上。网络请求由QNetworkRequest类来表示,作为与请求有关的统一容器,在创建请求对象时指定的URL决定了请求使用的协议。QNetworkAccessManager类用于协调网络操作,每当一个请求创建后,该类用来调度他,并发送信号来报告进度;QNetworkReply用于网络请求的应答,他会在请求呗完成调度时由QNetworkAccessManager创建。
相关文章推荐
- 【神经网络与深度学习】【Qt开发】【VS开发】从caffe-windows-visual studio2013到Qt5.7使用caffemodel进行分类的移植过程
- Qt网络通信学习笔记
- unity开发:Qt C++与unity之间UDP网络通信
- QT学习过程及简单串口和UDP通信demo开发过程及代码分享
- ios开发网络学习十二:NSURLSession实现文件上传
- 【神经网络与深度学习】【Qt开发】【VS开发】从caffe-windows-visual studio2013到Qt5.7使用caffemodel进行分类的移植过程<二>
- Qt开发:TCP和UDP网络通信
- unity开发:Qt C++与unity之间TCP网络通信
- android应用开发:android网络通信
- 异步非阻塞套接字Winsock开发网络通信程序的经典入门
- android 开发 常用到的一些网络通信包
- android网络游戏开发——网络通信——IO处理
- 开源中国iOS客户端学习——(五)网络通信ASI类库(1)
- 安卓网络通信框架Volley学习(二)基于Volley高效加载网络图片
- Winsock开发网络通信程序的经典入门
- QT-基于TCP的网络通信
- IOS开发学习二十二 网络程序开发时数据监控
- ios开发学习-网络(Networking) 效果源码分享--系列教程
- 网络与通信学习笔记
- ios开发网络学习九:NSURLSessionDownloadTask实现大文件下载