(9)Qt---网络编程(半双工通信)

目录

1. 复习

1.1 UDP 与TCP

1.2 IP地址与端口号

2. 前期准备

3. 编程内容


1. 复习

1.1 UDP 与TCP

UDP TCP 协议相同点:都存在于传输层

  • TCP(即传输控制协议):

是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、

数据无失序、数据无重复到达的通信)

适用情况:

 1、适合于对传输质量要求较高,以及传输大量数据的通信。

 2、在需要可靠数据传输的场合,通常使用TCP协议

 3、MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议

  • UDP :用户数据报协议

UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。

适用情况:

1、发送小尺寸数据(如对DNS服务器进行IP地址查询时)

2、在接收到数据,给出应答较困难的网络中使用UDP。

3、适合于广播/组播式通信中。

4、MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议

5、流媒体、VOD、VoIP、IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输

1.2 IP地址与端口号

  • IP地址

基本概念:

IP地址是Internet中主机的标识,Internet中的主机要与别的机器通信必须具有一个IP地址。

  • 端口号

基本概念:

为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分。

众所周知端口:1~1023(1~255之间为众所周知端口,256~1023端口通常由UNIX系统占用)

已登记端口:1024~49151    

动态或私有端口:49152~65535

自定义程序的端口号建议:2000-65535,去除连号,例如8888

2. 前期准备

跟数据库一样,网络功能也需要在.pro文件中增加network模块。

Qt的TCP通信结构图,如下所示(原理大概下图所示,但是具体的API有所不同)。

3. 编程内容

本次要实现一个基于TCP的聊天程序,需要使用的类有:

  • QTcpServer

服务器管理类:管理服务器的多个连接,直接继承了QObject,因此不具备IO能力。

相关函数如下:

// 构造函数
QTcpServer::QTcpServer(QObject * parent = 0)
// 服务器开启监听,等待客户主动发起连接
// 参数1:监听来自于哪个IP地址的请求,默认值为不限制IP地址,QHostAddress类是IP地址的封装类。
// 参数2:服务器端口号
// 返回值:监听开启结果
bool QTcpServer::listen(
                const QHostAddress & address = QHostAddress::Any, 
                quint16 port = 0)
// 有新连接建立的通知信号
void QTcpServer::newConnection() [signal]
// 服务器是否还在监听
bool QTcpServer::isListening() const
// 关闭服务器
void QTcpServer::close()
// 返回一个就绪的连接对象,此对象用于跟某个客户端进行IO操作
QTcpSocket * QTcpServer::nextPendingConnection() [virtual]
  • QTcpSocket

TCP连接类:进行网络IO操作,间接继承QIODevice类。

相关函数如下:

// 构造函数
QTcpSocket::QTcpSocket(QObject * parent = 0)
// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:读写模式,默认为可读可写
void QAbstractSocket::connectToHost(const QString & hostName,
                                    quint16 port, 
                                    OpenMode openMode = ReadWrite) [virtual]
// 连接是否处于打开状态
bool QIODevice::isOpen() const
// 关闭连接
void QIODevice::close() [virtual]
// 拿到对面的IP地址封装类对象,如果没有连接返回QHostAddress::Null
QHostAddress QAbstractSocket::peerAddress() const
// 连接断开发射的信号
void QAbstractSocket::disconnected() [signal]
// 有数据可读时发射的信号
void QIODevice::readyRead() [signal]
  • QTextStream 

文本流类:是一种高效地文本数据IO的辅助类。

// 构造函数
// 参数为QIODevice的派生类对象
QTextStream::QTextStream(QIODevice * device)
// 发送字符串
// 参数必须是QString类型,注意不要使用const char*
// 返回值是当前类型的引用,表示支持链式调用,因此连续追加发送的内容
QTextStream & QTextStream::operator<<(const QString & string)
// 读取最大值为maxlen个字符的内容到返回值
QString QTextStream::read(qint64 maxlen)
// 读取最大字符数maxlen的一行字符到返回值
QString QTextStream::readLine(qint64 maxlen = 0)
// 读取所有字符到返回值
// 
QString QTextStream::readAll()

Client:(QTcpSocket)
①创建QTcpSocket对象

②当对象与Server连接成功时会发送connected 信号

③调用成员函数connectToHost连接服务器,需要的参数是地址和端口号

④connected信号的槽函数开启发送数据

⑤使用write发送数据,read接收数据

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QtWidgets>
//连接类
#include <QTcpSocket>
//文件流类
#include <QTextStream>
#include <QTcpServer>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QTcpSocket *client; //连接对象

private slots:
    void btnConnClickedSlot();
    void btnSendClickedSlot();
    //连接和断开的检测槽函数
    void connectedSlot();
    void diaconnectedSlot();
    void readReadSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButtonConn,SIGNAL(clicked()),this,SLOT(btnConnClickedSlot()));
    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickedSlot()));

    //设置窗口标记为顶层
    setWindowFlags(Qt::WindowStaysOnTopHint);

    // 创建连接对象
    client = new QTcpSocket(this);
    //client = new QTcpServer(this);
    //连接状态检测的信号槽
    connect(client,SIGNAL(connected()),this,SLOT(connectedSlot()));
    connect(client,SIGNAL(disconnected()),this,SLOT(diaconnectedSlot()));
    connect(client,SIGNAL(readyRead()),this,SLOT(readReadSlot()));

}

Dialog::~Dialog()
{
    // 如果客户端还在连着,关掉
    if(client->isOpen()){
        client->close();
    }
    delete ui;
}
void Dialog::btnConnClickedSlot(){
    //默认输入有效,连接到服务器
    //参数1:服务器的IP地址
    //参数2:服务器的端口号
    client->connectToHost(ui->lineEditIp->text(),8887);

}

void Dialog::btnSendClickedSlot(){
    //获取用户输入的内容
     QString msg = ui->lineEditMsg->text();
     if(msg == ""){
         QMessageBox::warning(this,"提示","请输入要发送的内容!");
         return;
     }
     //创建文本流对象
     QTextStream output(client);
     //发送内容
     output << msg;
     //清空输入框
     ui->lineEditMsg->clear();
     QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
     ui->textBrowser->append(time);
     ui->textBrowser->append("客户端:");
     ui->textBrowser->append(msg);
     ui->textBrowser->append("");
}

void Dialog::connectedSlot()
{
    //屏蔽连接按钮
    ui->pushButtonConn->setEnabled(false);
    ui->pushButtonConn->setText("已连接");
    //释放发送按钮
    ui->pushButtonSend->setEnabled(true);
}

void Dialog::diaconnectedSlot()
{
    //恢复连接按钮
    ui->pushButtonConn->setEnabled(true);
    ui->pushButtonConn->setText("连接!");
    //屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
}
void Dialog::readReadSlot(){
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    QTextStream input(client);
    QString msg = input.readAll();
    ui->textBrowser->append("服务端:");
    ui->textBrowser->append(msg);
    ui->textBrowser->append("");
}

ui界面

Server:(QTcpServer)
①创建QTcpServer对象

②监听list需要的参数是地址和端口号

③当有新的客户端连接成功回发送newConnect信号

④在newConnection信号槽函数中,调用nextPendingConnection函数获取新连接QTcpSocket对象

⑤连接QTcpSocket对象的readRead信号

⑥在readRead信号的槽函数使用read接收数据

⑦调用write成员函数发送数据

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QtWidgets>
//网络相关类
#include <QTcpServer>
#include <QTcpSocket>
#include <QDateTime>
#include <QTextStream>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    // 管理类服务器对象
    QTcpServer *server;
    QTcpSocket*socket = NULL; //简单期间,只保留一个客户端连接

private slots:
    //新连接建立的槽函数
    void newConnSlot();
    //网络连接断开的槽函数
    void disconnectedSlot();
    //读取信息的槽函数
    void readReadSlot();
    void btnSendClickSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    // 设置窗口标记,始终前台显示
    setWindowFlags(Qt::WindowStaysOnTopHint);
    // 创建管理类对象
    server = new QTcpServer(this);
    //server = new QTcpSocket(this);
    //连接服务器通知的信号槽
    connect(server,SIGNAL(newConnection()),this,SLOT(newConnSlot()));
    // 开启监听,等待客户主动发起连接
    //参数1:监听来自哪个IP地址的请求,默认值为不限制IP地址
    //QHostAddress类是ip地址的封装类
    //参数2:服务器端口号
    server->listen(QHostAddress::Any,8887);

}

Dialog::~Dialog()
{
    if(server->isListening()) // 如果在监听
            // 关闭服务器
            server->close();
    delete ui;
}


void Dialog::newConnSlot()
{
    //如果不是第一次连接,先踢掉之前的连接
    if(socket !=NULL){
        socket->close();
    }
    //拿到与客户端进行连接的QTcpSocket对象(绿蛋)
    socket = server->nextPendingConnection();
    //建立断开连接的通知的信号槽
    connect(socket,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));
    //建立读取消息的信号槽
    connect(socket,SIGNAL(readyRead()),this,SLOT(readReadSlot()));

    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickSlot()));

    //拿到对面客户端的IP与端口号
    QString ip = socket->peerAddress().toString();
    quint16 port = socket->peerPort();
    //输出信息
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("新连接来了!");
    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");


}

void Dialog::disconnectedSlot()
{
    //拿到对面客户端的IP与端口号
    QString ip = socket->peerAddress().toString();
    quint16 port = socket->peerPort();
    //输出信息
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("老连接走了!");
    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");
}

void Dialog::readReadSlot()
{
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    QTextStream input(socket);
    //读取数据
    QString msg = input.readAll();
    //展示
    ui->textBrowser->append("客户端:");
    ui->textBrowser->append(msg);
    ui->textBrowser->append("");
}

void Dialog::btnSendClickSlot()
{
    QString msg2 = ui->lineEditMsg->text();
    if(msg2 == ""){
        QMessageBox::warning(this,"提示","请输入要发送的内容!");
        return;
    }
    QTextStream output(socket);
    output << msg2;
    ui->lineEditMsg->clear();
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("服务端:");
    ui->textBrowser->append(msg2);
    ui->textBrowser->append("");

}

ui界面

 运行结果:

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值