第十四章:Qt网络编程
回顾:第一章:Qt的概述第二章:在Ubuntu编写第一个Qt程序第三章:Qt的字符串和字符编码第四章:Qt的信号和槽第五章:Qt容器窗口(父窗口)第六章:面向对象的Qt编程第七章:Qt设计师使用(designer)第八章:Qt创造器的使用(qtcreator)第九章:资源和图像第十章:目录与定时器第十一章:鼠标和键盘事件第十二章:Qt数据库(sqlite)第十三章:QT多线程(QThread)Qt
·
回顾:
第一章:Qt的概述
第二章:在Ubuntu编写第一个Qt程序
第三章:Qt的字符串和字符编码
第四章:Qt的信号和槽
第五章:Qt容器窗口(父窗口)
第六章:面向对象的Qt编程
第七章:Qt设计师使用(designer)
第八章:Qt创造器的使用(qtcreator)
第九章:资源和图像
第十章:目录与定时器
第十一章:鼠标和键盘事件
第十二章:Qt数据库(sqlite)
第十三章:QT多线程(QThread)
Qt网络编程
1、网络协议层次(OSI七层)
- 应用层
- 表示层
- 会话层
- 传输层:UDP、TCP
- 网络层:IPv4,IPv6
- 数据链路层
- 物理层
2、socket编程
- TCP
- UDP
3、Qt的socket
- QHostAddress //IP地址
QHostAddress ip("192.168.x.x");
QHostAddress::LocalHost //127.0.0.1,本地的环回地址
QHostAddress::Broadcast //255.255.255.255,广播地址
QHostAddress::Any //0.0.0.0,可以对应任意网卡地址
- QAbstractSocket
bind(ip, port);//绑定IP和端口
connectToHost(ip, port);//建立和服务器连接
disconnectFromHost();//断开连接
state(); //获取套接字状态
bytesAvailable();//获取套接字等待读取数据的字节数
信号函数:
connected()[signal]//连接服务器成功时发送
disconnected()[signal]//断开服务器成功时发送
error(QAbstractSocket::SocketError socketError)[signal]//网络异常时发送
readyRead() [signal] //套接字有数据到来时发送
- QUdpSocket
hasPendingDatagrams() //判断是否有等待读取的数据包
pendingDatagramSize() //获取等待读取数据包的大小
readDatagram() //udp读操作
writeDatagram() //udp写操作
案例:udp广播
- 发送端
- 发送网络地址:255.255.255.255
- 指定发送的端口:8888
- 输入广播消息
- 每秒发送一次广播
SenderDialog.h
#ifndef SENDERDIALOG_H
#define SENDERDIALOG_H
#include <QDialog>
#include <QUdpSocket>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class SenderDialog; }
QT_END_NAMESPACE
class SenderDialog : public QDialog
{
Q_OBJECT
public:
SenderDialog(QWidget *parent = nullptr);
~SenderDialog();
private slots:
void on_pushButton_clicked();
//timed transmission messages slot function
void sendMessage();
private:
Ui::SenderDialog *ui;
QUdpSocket *udpSocket;//UDP socket
QTimer *timer;//timer
bool isStarted;//start or stop to broadcast
};
#endif // SENDERDIALOG_H
SenderDialog.cpp
#include "SenderDialog.h"
#include "ui_SenderDialog.h"
SenderDialog::SenderDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::SenderDialog)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(sendMessage()));
isStarted = false;
}
SenderDialog::~SenderDialog()
{
delete ui;
}
void SenderDialog::on_pushButton_clicked()
{
if(isStarted == false){
isStarted = true;
timer->start(1000);
ui->pushButton->setText("stop to broadcast");
ui->messageEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
}
else{
isStarted = false;
timer->stop();
ui->pushButton->setText("start to broadcast");
ui->messageEdit->setEnabled(true);
ui->portEdit->setEnabled(true);
}
}
//timed transmission messages slot function
void SenderDialog::sendMessage()
{
//get IP and port
QString msg = ui->messageEdit->text();
quint16 port = ui->portEdit->text().toShort();
//send message
//toUtf8():QString->QByteArray(char [])
udpSocket->writeDatagram(msg.toUtf8(), QHostAddress::Broadcast, port);
}
- 接收端
- 指定接收端口:8888
- bind(8888)
- 接收广播消息并显示
ReceiverDialog.h
#ifndef RECEIVERDIALOG_H
#define RECEIVERDIALOG_H
#include <QDialog>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class ReceiverDialog; }
QT_END_NAMESPACE
class ReceiverDialog : public QDialog
{
Q_OBJECT
public:
ReceiverDialog(QWidget *parent = nullptr);
~ReceiverDialog();
private slots:
void on_pushButton_clicked();
//receive message slot function
void messageReceive();
private:
Ui::ReceiverDialog *ui;
QUdpSocket *udpSocket;
quint16 port;
bool isStarted;//start or stop receiver
};
#endif // RECEIVERDIALOG_H
ReceiverDialog.cpp
#include "ReceiverDialog.h"
#include "ui_ReceiverDialog.h"
ReceiverDialog::ReceiverDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::ReceiverDialog)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
isStarted = false;
}
ReceiverDialog::~ReceiverDialog()
{
delete ui;
}
void ReceiverDialog::on_pushButton_clicked()
{
if(isStarted == false){
isStarted = true;
//get prot
port = ui->lineEdit->text().toShort();
//bind prot
udpSocket->bind(port);
//when the port has message, send readyRead signal
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(messageReceive()));
ui->lineEdit->setEnabled(false);
ui->pushButton->setText("stop receive");
}
else{
isStarted = false;
udpSocket->close();
ui->lineEdit->setEnabled(true);
ui->pushButton->setText("start receive");
}
}
//receive message slot function
void ReceiverDialog::messageReceive()
{
//adjust has data come now
if(udpSocket->hasPendingDatagrams()){
//ready to receive data from buffer
QByteArray buffer;
buffer.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(buffer.data(), buffer.size());
ui->listWidget->addItem(buffer);
ui->listWidget->scrollToBottom();
}
}
- QTcpSocket、QTcpServer
//有客户端和服务器连接时发送
void QTcpServer::newConnection() [signal]
//获取和客户端通信的套接字
QTcpSocket *QTcpServer::nextPendingConnection()
案例:网络聊天室
- 服务器
- 使用QTcpServer创建Tcp服务器
- 获取、保存与客户端通信的套接字
- 接收客户端发送的消息
- 转发消息给其他客户端
mkdir NetChat
工程名:Server
ServerDialog.h
#ifndef SERVERDIALOG_H
#define SERVERDIALOG_H
#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class ServerDialog; }
QT_END_NAMESPACE
class ServerDialog : public QDialog
{
Q_OBJECT
public:
ServerDialog(QWidget *parent = nullptr);
~ServerDialog();
private slots:
//创建服务器按钮对应的槽函数
void on_pushButton_clicked();
//当有客户端服务器连接时执行的槽函数
void onNewConnect(void);
//当有客户端给服务器发送聊天信息时执行的槽函数
void onReadyRead(void);
private:
//转发聊天信息给所有在线的客户端
void sendMessage(const QByteArray& msg);
private:
Ui::ServerDialog *ui;
QTcpServer server;//服务器对象
quint16 port;//服务器端口
//容器:保存所有和客户端通信的套接字
QList<QTcpSocket*> tcpClientlist;
};
#endif // SERVERDIALOG_H
ServerDialog.cpp
#include "ServerDialog.h"
#include "ui_ServerDialog.h"
ServerDialog::ServerDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::ServerDialog)
{
ui->setupUi(this);
//当有客户端和服务器连接时,发送信号newConnection
connect(&server, SIGNAL(newConnection()), this, SLOT(onNewConnect()));
}
ServerDialog::~ServerDialog()
{
delete ui;
}
void ServerDialog::on_pushButton_clicked()
{
//获取端口号
port = ui->lineEdit->text().toShort();
//设置监听IP和端口
bool res = server.listen(QHostAddress::Any, port);
if(res == true){
qDebug("create server success!");
}
else{
qDebug("create server fail!");
return;
}
//禁用创建服务器按钮和端口输入
ui->pushButton->setEnabled(false);
ui->lineEdit->setEnabled(false);
}
//当有客户端服务器连接时执行的槽函数
void ServerDialog::onNewConnect(void)
{
//获取和客户端通信的套接字
QTcpSocket* tcpClientSocket = server.nextPendingConnection();
//保存套接字到容器
tcpClientlist.append(tcpClientSocket);
//当客户端给服务端发送信息时,发送readyRead
connect(tcpClientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
//当有客户端服务器连接时执行的槽函数
void ServerDialog::onReadyRead(void)
{
//检查容器中的套接字是否已经断开
for(int i=0;i<tcpClientlist.size();i++){
if(tcpClientlist.at(i)->state() == QAbstractSocket::UnconnectedState){
//剔除失效的套接字
tcpClientlist.removeAt(i);
}
}
for(int i=0;i<tcpClientlist.size();i++){
//byteAvailable():获取当前套接字等待读取信息的字节数
if(tcpClientlist.at(i)->bytesAvailable()>0){
//读取信息
QByteArray readBuf = tcpClientlist.at(i)->readAll();
//显示
ui->listWidget->addItem(readBuf);
ui->listWidget->scrollToBottom();
//转发消息给其他在线客户端
sendMessage(readBuf);
}
}
}
//转发聊天消息给所有在线的客户端
void ServerDialog::sendMessage(const QByteArray& msg)
{
for(int i=0;i<tcpClientlist.size();i++){
tcpClientlist.at(i)->write(msg);
qDebug() << "send message:" << msg;
}
}
- 客户端
- 使用QTcpSocket建立和服务器的连接
- 获取输入的聊天消息发送到服务器
- 接收和显示服务器转发的聊天消息
工程名:Client
ClientDialog.h
#ifndef CLIENTDIALOG_H
#define CLIENTDIALOG_H
#include <QDialog>
#include <QTcpSocket>
#include <QMessageBox>
#include <QHostAddress>
QT_BEGIN_NAMESPACE
namespace Ui { class ClientDialog; }
QT_END_NAMESPACE
class ClientDialog : public QDialog
{
Q_OBJECT
public:
ClientDialog(QWidget *parent = nullptr);
~ClientDialog();
private slots:
//连接服务器按钮对应的槽函数
void on_ConnectButton_clicked();
//发送消息按钮对应的槽函数
void on_sendButton_clicked();
//和服务器连接成功时执行的槽函数
void onConnected(void);
//和服务器断开连接时执行的槽函数
void onDisConnected(void);
//接收聊天服务器转发的聊天消息
void onReadyRead(void);
//连接服务器按钮对应的槽函数
void onError(QAbstractSocket::SocketError);
private:
Ui::ClientDialog *ui;
bool status;//标记连接状态:在线/离线
QTcpSocket tcpSocket;//和服务器通信的套接字
QHostAddress serverIp;//服务器IP
quint16 serverPort;//服务器端口
QString username;//聊天昵称
};
#endif // CLIENTDIALOG_H
ClientDialog.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(onReadyRead()));
//网络异常时发送信号Error
connect(&tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
}
ClientDialog::~ClientDialog()
{
delete ui;
}
//
void ClientDialog::on_ConnectButton_clicked()
{
if(status == false){
//建立连接
//获取服务器ip
QString ip = ui->serverIpEdit->text();
if(serverIp.setAddress(ip)==false){
QMessageBox::critical(this, "Error", "IP is error!");
return;
}
//获取服务器端口
serverPort = ui->serverPortEdit->text().toShort();
if(serverPort < 1024 || serverPort > 48000){
QMessageBox::critical(this, "Error", "Port is error!");
return;
}
//获取聊天昵称
username = ui->usernameEdit->text();
if(username == ""){
QMessageBox::critical(this, "Error", "Username is null!");
return;
}
//向服务器发送连接请求
//成功时发送信号:connected
//失败时发送信号:disconnected
tcpSocket.connectToHost(serverIp,serverPort);
//标记连接状态:在线
status = true;
}
else{
//断开连接
//向服务器发送离开聊天室的提示
QString msg = username + ":" + "check out the chat!";
tcpSocket.write(msg.toUtf8());
//关闭当前通信
//断开连接时发送信号:disconnected
tcpSocket.disconnectFromHost();
//标记链接而状态:离线
status = false;
}
}
//
void ClientDialog::on_sendButton_clicked()
{
//获取输入的聊天信息
QString msg = ui->messageEdit->text();
msg = username + ":" + msg;
tcpSocket.write(msg.toUtf8());
//清空信息输入
ui->messageEdit->clear();
}
//
void ClientDialog::onConnected(void)
{
//使能发送消息按钮
ui->sendButton->setEnabled(true);
//禁用ip port username
ui->serverIpEdit->setEnabled(false);
ui->serverPortEdit->setEnabled(false);
ui->usernameEdit->setEnabled(false);
//按钮文本修改:离开聊天室
ui->ConnectButton->setText("chack out");
//向服务器发送进入聊天室提示
QString msg = username+":"+"check in the chat!";
//发送信息
tcpSocket.write(msg.toUtf8());
}
//
void ClientDialog::onDisConnected(void)
{
//禁用相关组件
ui->sendButton->setEnabled(false);
ui->serverIpEdit->setEnabled(true);
ui->serverPortEdit->setEnabled(true);
ui->usernameEdit->setEnabled(true);
//按钮文本修改:连接服务器
ui->ConnectButton->setText("connect to server");
}
//
void ClientDialog::onReadyRead(void)
{
if(tcpSocket.bytesAvailable() > 0){
QByteArray readBuf;
readBuf.resize(tcpSocket.bytesAvailable());
//读取聊天信息
tcpSocket.read(readBuf.data(), readBuf.size());
//显示聊天信息
ui->listWidget->addItem(readBuf);
ui->listWidget->scrollToBottom();
}
}
//
void ClientDialog::onError(QAbstractSocket::SocketError)
{
QMessageBox::critical(this, "Error", tcpSocket.errorString());
}
更多推荐
已为社区贡献1条内容
所有评论(0)