概述
一、概述
1、什么是计算机网络?
2、什么是网络协议?
3、什么是协议栈?
4、OSI(Open System Interconnection)的七层协议
5、TCP/IP协议族
6、IP地址
QT的网络编程
二、UDP通信
1、UDP协议简介
2、适合使用UDP通信的情况
3、UDP通信模型
4、UDP通信类
要进行UDP数据接收,要用QUdpSocket::bind()函数先绑定一个端口,用于接收传入的数据报。当有数据报传入时会发射readRead()信号,使用readDatagram()函数来读取接收到的数据报。
相关函数原型:
UDP消息传送有单播、广播、组播三种。
单播模式:一个UDP客户端发出的数据报只发送到另一个指定的地址和端口的UDP客户端,是一对一数据传输;
广播模式:一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以接收到。QUdpSocket支持IPv4广播。广播经常用于实现网络发现的协议。要获取广播数据只需要将数据报发送到特定的接收端地址QHostAddress::Broadcast(255.255.255.255);
组播模式:也称为多播。UDP客户端加入到另一个组播IP地址指定的多播组(QUdpSocket::joinMuliticastGroup()),成员想组播地址发送的数据报组内成员都可以接收到,类似于QQ群的功能。一般即时通信都是基于UDP通信的。
服务器端:UdpServer
界面放一个QTextBroWse控件来显示客户端发送来的所有消息,并改对象名为text;
在.pro文件添加QT+=network
UdpServer.h内容
#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <QDialog>
#include <QUdpSocket>
namespace Ui {
class UdpServer;
}
class UdpServer : public QDialog
{
Q_OBJECT
public:
explicit UdpServer(QWidget *parent = 0);
~UdpServer();
private slots:
void readPendingDatagrams();
private:
Ui::UdpServer *ui;
QUdpSocket *udpSocket;
};
#endif // UDPSERVER_H
UdpServer.cpp
#include "udpserver.h"
#include "ui_udpserver.h"
UdpServer::UdpServer(QWidget *parent) :
QDialog(parent),
ui(new Ui::UdpServer)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress::LocalHost, 7755);
connect(udpSocket, SIGNAL(readyRead()),this, SLOT(readPendingDatagrams()));
}
UdpServer::~UdpServer()
{
delete ui;
}
void UdpServer::readPendingDatagrams()
{
QHostAddress sender;
quint16 senderPort;
while (udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size(),&sender, &senderPort);
QString strMes(datagram);
ui->text->insertPlainText(strMes);
}
QString text = "hello ...";
QByteArray datagram = text.toLocal8Bit();
udpSocket->writeDatagram(datagram.data(),datagram.size(),sender, senderPort);
}
#ifndef UDPCLIER_H
#define UDPCLIER_H
#include <QDialog>
#include <QUdpSocket>
namespace Ui {
class UdpClier;
}
class UdpClier : public QDialog
{
Q_OBJECT
public:
explicit UdpClier(QWidget *parent = 0);
~UdpClier();
private slots:
void readPendingDatagrams();
void on_button_clicked();
private:
Ui::UdpClier *ui;
QUdpSocket *udpSocket;
};
#endif // UDPCLIER_H
UdpClear.cpp
#include "udpclier.h"
#include "ui_udpclier.h"
UdpClier::UdpClier(QWidget *parent) :
QDialog(parent),
ui(new Ui::UdpClier)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress::LocalHost, udpSocket->localPort());
connect(udpSocket, SIGNAL(readyRead()),this, SLOT(readPendingDatagrams()));
}
UdpClier::~UdpClier()
{
delete ui;
}
void UdpClier::on_button_clicked()
{
QString text = ui->sends->toPlainText();
QByteArray datagram = text.toLocal8Bit();
udpSocket->writeDatagram(datagram.data(),datagram.size(),QHostAddress::LocalHost, 7755);
}
void UdpClier::readPendingDatagrams(){
while (udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
udpSocket->readDatagram(datagram.data(), datagram.size(),&sender, &senderPort);
QString text = QString(datagram);
ui->text->insertPlainText(text);
ui->text->insertPlainText("n");
}
}
三、TCP通信
1、TCP协议简介
2、TCP编程模型。
4、QT中的TCP通信
服务器的实现一般如下:
- 调用QTcpServer::listen()启动服务端并监听;
- 每当接收到客户端的请求,就会发送QTcpServer::newConnection()信号;
- 在槽函数中,调用QTcpServer::nextPendingConnection()接受请求,返回QTcpSocket对象用于和客户端通信(类似于UC的accept函数返回的fp),然后将QTcpSocket对象保存到链表中。
- 当有客户端发送来消息时,就会触发readyread()信号
- 在槽函数中调用readAll()函数介绍消息,并处理消息。
QTcpSocket类客户端建立TCP连接。一般使用逻辑如下:
- 调用QTcpSocket::connectToHost连接到服务器;
- 使用write函数进行发送数据;
- 当服务器有数据发送来的时候就会触发readyread()信号。
- 在槽函数中使用readAll()函数接受服务器发送来的消息,并处理消息。
#ifndef CLIENTDIALOG_H
#define CLIENTDIALOG_H
#include <QDialog>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>
#include <QDebug>
namespace Ui {
class ClientDialog;
}
class ClientDialog : public QDialog
{
Q_OBJECT
public:
explicit ClientDialog(QWidget *parent = 0);
~ClientDialog();
private slots:
//发送按钮对应的槽函数
void on_sendButton_clicked();
//连接服务器按钮对应的槽函数
void on_connectButton_clicked();
//和服务器连接成功时执行的槽函数
void onConnected();
//和服务器断开连接时执行的槽函数
void onDisconnected();
//接收聊天消息的槽函数
void onReadRead();
//网络异常是执行的槽函数
void onError();
private:
Ui::ClientDialog *ui;
bool status;//标记客户端状态,true:在线状态,false:离线状态
QTcpSocket tcpSocket;//和服务器通信的TCP的套接字
QHostAddress serverIp;//服务器的IP
quint16 serverPort;//服务器端口
QString username;//聊天室昵称
};
#endif // CLIENTDIALOG_H
Cliear.cpp - 客户端实现
#include "clientdialog.h"
#include "ui_clientdialog.h"
ClientDialog::ClientDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ClientDialog)
{
ui->setupUi(this);
status = false;//初始化离线状态
//连接服务器服务器成功时,发送信号connected,连接到和服务器连接成功时执行的槽函数
connect(&tcpSocket,SIGNAL(connected()),this,SLOT(onConnected()));
//和服务器断开连接,发送信号disconnected,连接和服务器断开连接时执行的槽函数
connect(&tcpSocket,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
//收到服务器转发的聊天消息时,发送信号readyRead,连接到接收聊天消息的槽函数
connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadRead()));
//网络通信异常时,发送信号error(),连接到网络异常是执行的槽函数
connect(&tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError()));
}
ClientDialog::~ClientDialog()
{
delete ui;
}
//发送按钮对应的槽函数
void ClientDialog::on_sendButton_clicked()
{
//获取用户输入的聊天消息
QString msg = ui->messageEdit->text();
if(msg == ""){
return;
}
msg = username + ":" + msg;
//发送聊天消息
tcpSocket.write(msg.toUtf8());
//清空消息输入框
ui->messageEdit->clear();
}
//连接服务器按钮对应的槽函数
void ClientDialog::on_connectButton_clicked()
{
if(status == false){//如果当前客户端是离线状态则建立连接
//获取服务器IP
if(serverIp.setAddress(ui->serverIpEdit->text()) == false){
//如果IP设置错误,弹出消息提示框
QMessageBox::critical(this,"Error","IP地址格式错误!");
return;
}
//获取服务器端口
serverPort = ui->serverPortEdit->text().toShort();
if(serverPort < 1024){
QMessageBox::critical(this,"Error","端口格式错误!");
return;
}
//获取聊天室昵称
username = ui->usernameEdit->text();
if(username == ""){
QMessageBox::critical(this,"Error","聊天室昵称不能为空!");
return;
}
//向服务器发送连接请求
//如果连接成功,发送信号connected
//如果连接失败,发送信号error
tcpSocket.connectToHost(serverIp,serverPort);
}
else{//如果当前客户端是在线状态则断开连接
//向服务器离开服务器的消息提示
QString msg = username + ":离开了聊天室";
tcpSocket.write(msg.toUtf8());
//断开和服务器连接,断开以后将会发送信号disconnected
tcpSocket.disconnectFromHost();
}
}
//和服务器连接成功时执行的槽函数
void ClientDialog::onConnected()
{
qDebug() << "连接服务器成功!";
status = true;//标记客户端位在线状态
ui->sendButton->setEnabled(true);//设置"发送"按钮为可用状态
ui->connectButton->setText("离开服务器");//修改"连接服务器"按钮文本为"离开服务器"
ui->serverIpEdit->setEnabled(false);//禁用IP配置输入
ui->serverPortEdit->setEnabled(false);//禁用端口配置输入
ui->usernameEdit->setEnabled(false);//禁用昵称配置输入
//向服务器发送进入聊天室提示消息
QString msg = username + ":进入了聊天室";
//toUtf8():将QString转换为QByteArray
tcpSocket.write(msg.toUtf8());
}
//和服务器断开连接时执行的槽函数
void ClientDialog::onDisconnected()
{
qDebug() << "已经断开了服务器!";
status = false;//标记客户端位离线状态
ui->sendButton->setEnabled(false);//设置"发送"按钮为禁用状态
ui->connectButton->setText("连接服务器");//修改"离开服务器"按钮文本为"连接服务器"
ui->serverIpEdit->setEnabled(true);//恢复IP配置输入
ui->serverPortEdit->setEnabled(true);//恢复端口配置输入
ui->usernameEdit->setEnabled(true);//恢复昵称配置输入
}
//接收聊天消息的槽函数
void ClientDialog::onReadRead()
{
//bytesAvailable():获取等待读取消息的字节数
if(tcpSocket.bytesAvailable()){
//读取消息
QByteArray buf = tcpSocket.readAll();
//显示消息到界面
ui->listWidget->addItem(buf);
ui->listWidget->scrollToBottom();
}
}
//网络异常是执行的槽函数
void ClientDialog::onError()
{
//errorString:获取网络异常原因的字符串
QMessageBox::critical(this,"Error",tcpSocket.errorString());
}
server.ui - 服务器界面
server.h - 服务器
#ifndef SERVERDIALOG_H
#define SERVERDIALOG_H
#include <QDialog>
#include <QTcpServer> //QT += network
#include <QTcpSocket>
#include <QHostAddress>
#include <QDebug>
#include <QList>//列表容器
#include <QTimer>//定时器
namespace Ui {
class ServerDialog;
}
class ServerDialog : public QDialog
{
Q_OBJECT
public:
explicit ServerDialog(QWidget *parent = 0);
~ServerDialog();
private slots:
//创建服务器按钮对应的槽函数
void on_pushButton_clicked();
//响应客户端连接请求的槽函数
void onNewconnection();
//接收聊天消息的槽函数
void onReadyRead();
//转发聊天消息的槽函数
void sendMessage(const QByteArray& msg);
//定时器处理的槽函数
void onTimeout();
private:
Ui::ServerDialog *ui;
QTcpServer server;//TCP服务器
quint16 serverPort;//服务器端口
QList<QTcpSocket*> tcpClientList;//列表容器:用于保存所有客户端通信的套接字
QTimer timer;//定时器,定时检查容器中的套接字的连接状态
};
#endif // SERVERDIALOG_H
server.c - 服务器实现
#include "serverdialog.h"
#include "ui_serverdialog.h"
ServerDialog::ServerDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ServerDialog)
{
ui->setupUi(this);
}
ServerDialog::~ServerDialog()
{
delete ui;
}
//创建服务器按钮对应的槽函数
void ServerDialog::on_pushButton_clicked()
{
//获取服务器端口
serverPort = ui->lineEdit->text().toShort();
//设置监听服务器和端口
if(server.listen(QHostAddress::Any,serverPort) == false){
qDebug() << "服务器创建失败!";
return;
}
qDebug() << "服务器创建成功!";
//服务器检测到有客户端连接时,发送信号newConnection,连接到响应客户端连接请求的槽函数
connect(&server,SIGNAL(newConnection()),this,SLOT(onNewconnection()));
//禁用创建服务器按钮和端口的输入
ui->pushButton->setEnabled(false);
ui->lineEdit->setEnabled(false);
//定时器到时发送timeout信号,连接定时器处理的槽函数
connect(&timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
//开始定时器,每隔3秒时间检查一次容器中是否存在断开连接的套接字
timer.start(3000);
}
//响应客户端连接请求的槽函数
void ServerDialog::onNewconnection()
{
//获取和客户端通信的套接字
QTcpSocket* client = server.nextPendingConnection();
//将和客户端通信的套接字保存到容器中
tcpClientList.append(client);
//当客户端给服务器发送消息时,client发送信号readyReaad,连接到接收聊天消息的槽函数
connect(client,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
}
//接收聊天消息的槽函数
void ServerDialog::onReadyRead()
{
//遍历检查容器中哪个套接字有消息
for(int i=0;i<tcpClientList.size();i++){
//at(i):获取容器中第i个和服务器通信的套接字
//bytesAvailable:获取当前套接字等待读取的消息字节数,如果为0说明没有消息
if(tcpClientList.at(i)->bytesAvailable()){
//读取客户端发来的消息
QByteArray buf = tcpClientList.at(i)->readAll();
//显示消息到服务器界面
ui->listWidget->addItem(buf);
ui->listWidget->scrollToBottom();
//转发消息给所有的客户端
sendMessage(buf);
}
}
}
//转发聊天消息的槽函数
void ServerDialog::sendMessage(const QByteArray& msg)
{
for(int i=0;i<tcpClientList.size();i++){
tcpClientList.at(i)->write(msg);
}
}
//定时器处理的槽函数
void ServerDialog::onTimeout()
{
for(int i=0;i<tcpClientList.size();i++){
//state:获取套接字连接状态,如果是断开连接的状态即为UnconnectedState
if(tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState){
//如果第i个套接字是断开连接的,则容器中删除
tcpClientList.removeAt(i);
--i;
}
}
}
四、HTTP
1、HTTP协议简介
2、HTTP协议请求
3、HTTP协议响应
4、QT网络应用开发相关类
5、编程实现代码下载器,实现从“http://code.tarena.com.cn/”下载代码功能
HttpClent.ui
HttpClent.h 头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager> //QT += network
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QAuthenticator> //处理登录认证
#include <QFileInfo>
#include <QFile>
#include <QDebug>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
//向服务器发送请求
void sendRequest();
private slots:
//处理登录认证的槽函数
void onAuthenticationRequired(QNetworkReply*,QAuthenticator*);
//接收响应数据的槽函数
void onReadyRead();
//通信结束执行的槽函数
void onFinished();
//处理链接的槽函数
void onAnchorClicked(const QUrl&);
//下载文件
void downloadFile(const QUrl&);
//接收下载文件内容的槽函数
void receiveFile();
//更新显示文件下载进度的槽函数
void onDownloadProgress(qint64,qint64);
//文件下载完成执行的槽函数
void receiveFileFinished();
private:
Ui::MainWindow *ui;
QNetworkAccessManager* manager;//管理HTTP通信
QNetworkRequest request;//请求
QNetworkReply* reply;//响应
QByteArray buf;//保存响应数据的缓冲区
QUrl currentUrl;//当前的URL地址
QFile* file;//维护要下载文件
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建管理通信的Manager对象
manager = new QNetworkAccessManager(this);
//初始化请求和服务器通信的URL地址
request.setUrl(QUrl("http://code.tarena.com.cn/"));
//发送请求
sendRequest();
//点击文本浏览上面链接时发送信号anchorClicked,连接到处理链接的槽函数
connect(ui->textBrowser,SIGNAL(anchorClicked(QUrl)),this,SLOT(onAnchorClicked(QUrl)));
}
MainWindow::~MainWindow()
{
delete ui;
}
//向服务器发送请求
void MainWindow::sendRequest()
{
//使用GET发送发送请求,返回用于接收响应数据的reply对象
reply = manager->get(request);
//如果连接服务器需要登录认证,将会发送信号authenticationRequired,连接到处理登录认证的槽函数
connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
//认证成功后,服务器返回响应数据,reply将会发送信号readyRead,连接到接收响应数据的槽函数
connect(reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
//响应数据接收结束(通信结束),发送信号finished,连接到通信结束执行的槽函数
connect(reply,SIGNAL(finished()),this,SLOT(onFinished()));
}
//处理登录认证的槽函数
void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator* a)
{
//qDebug() << "处理登录认证的槽函数";
a->setUser("tarenacode");
a->setPassword("code_2013");
}
//接收响应数据的槽函数
void MainWindow::onReadyRead()
{
//qDebug() << "接收响应数据的槽函数";
buf += reply->readAll();
}
//通信结束执行的槽函数
void MainWindow::onFinished()
{
//qDebug() << "本次通信结束";
ui->textBrowser->setText(buf);//将收到响应数据显示到界面
buf.clear();//清空数据缓冲区
currentUrl = reply->url();//记录当前所在的URL地址
reply->deleteLater();//销毁reply对象
}
//处理链接的槽函数,参数点击的URL地址
void MainWindow::onAnchorClicked(const QUrl& url)
{
//qDebug() << "当前URL:" << currentUrl.toString();
//qDebug() << "点击URL:" << url.toString();
//当前URL: "http://code.tarena.com.cn/"
//点击URL: "CSDCode/"
QUrl newUrl; //要进入新URL
//如果点击不是"../",新URL = 当前URL+点击URL
if(url.toString() != "../"){
newUrl = currentUrl.toString() + url.toString();
}
else{//处理"../"
//如果当前在首界面,点击"../"不做处理
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
//如果不在首界面,去掉最后一级目录链接,回到上级目录链接
//当前URL: "http://code.tarena.com.cn/CSDCode/"
//newUrl: "http://code.tarena.com.cn/"
//查找倒数第二次出现"/"位置
int pos = currentUrl.toString().lastIndexOf("/",-2);
//字符串截断,从起始位置截断到倒数第二次出现"/"位置
newUrl = currentUrl.toString().mid(0,pos+1);
}
//判断newUrl是否位文件链接,如果是文件则下载
//判断是否为文件链接方式:不是目录就是文件,目录链接都带有"/"
if(url.toString().lastIndexOf("/")==-1){
downloadFile(newUrl);
return;
}
//设置请求为新URL
request.setUrl(newUrl);
//发送请求
sendRequest();
}
//下载文件
void MainWindow::downloadFile(const QUrl& fileUrl)
{
//qDebug() << "下载文件:" << fileUrl.toString();
//下载文件: "http://code.tarena.com.cn/CSDCode/csd1912/UC/day01.zip"
//根据文件URL获取文件名
QFileInfo fileInfo = fileUrl.path();//"/CSDCode/csd1912/UC/day01.zip"
QString filename = fileInfo.fileName();//"day01.zip"
//在本地创建同名的文件
file = new QFile(filename);
//以写的方式打开,如果文件不存在则创建
file->open(QIODevice::WriteOnly);
//设置请求位下载文件URL
request.setUrl(fileUrl);
//发送下载文件链接请求
reply = manager->get(request);
//收到响应数据(文件内容)时发送信号readyRead,连接接收下载文件内容的槽函数
connect(reply,SIGNAL(readyRead()),this,SLOT(receiveFile()));
//伴随文件下载,会发送下载进度信号downloadProgress,连接到更新显示文件下载进度的槽函数
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(onDownloadProgress(qint64,qint64)));
//文件下载完成,发送信号finished,连接文件下载完成执行的槽函数
connect(reply,SIGNAL(finished()),this,SLOT(receiveFileFinished()));
}
//接收下载文件内容的槽函数
void MainWindow::receiveFile()
{
//读取响应数据(文件内容)并写入本地的文件
file->write(reply->readAll());
}
//更新显示文件下载进度的槽函数,参数:(1)已收到的字节数 (2)总字节数
void MainWindow::onDownloadProgress(qint64 bytesRead,qint64 bytesTotal)
{
//计算百分比
qint64 progress = bytesRead*100/bytesTotal;
//打印百分比
qDebug() << file->fileName() << ":" << progress << "%...";
}
//文件下载完成执行的槽函数
void MainWindow::receiveFileFinished()
{
qDebug() << file->fileName() << ":文件下载完成";
file->flush();//刷新文件流
file->close();
reply->deleteLater();
}
//注:目前只能一次下载一个文件
//扩展:
//(1)在认证登录位置,增加登录对话框
//(2)将下载文件操作,放到子线程中执行,实现多个文件的同时下载
//(3)下载文件时.指定保存在"/home/tarena/Downloads"
//参考:refer.tar.gz
最后
以上就是紧张板凳为你收集整理的QT网络编程一、概述二、UDP通信三、TCP通信的全部内容,希望文章能够帮你解决QT网络编程一、概述二、UDP通信三、TCP通信所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复