Qt的TCP通信—服务端的实现

Qt的TCP通信—服务端的实现

TCP通信必须先建立TCP连接,分为客户端和服务端,Qt提供了QTSocket类和QTcpServer类用于建立TCP通信应用程序。

客户端和服务端TCP通信示意图:

在这里插入图片描述

Qt的QTcpServer类的介绍

QTcpServer是从QObject继承的类,他主要用于服务器简历网络监听,创建网络Socket连接。其主要接口函数如图所示。

在这里插入图片描述

Qt的QTcpSocket类的介绍

QTcpSocket是从QIODevice间接继承的类,具有流读写的功能,他除了构造函数和析构函数,其他函数都是从QAbstractSocket继承或重定义的。其主要函数接口如下图所示。

在这里插入图片描述

服务端的实现流程

​ 服务器端首先要用QTcpServer::listen()开始服务端监听,可以指定监听的IP地址和端口,一般一个服务程序只监听某个端口的网络连接。

​ 当有客户端接入时,QTcpServer的内部incomingConnection()函数回创建一个与客户端连接QTcpSocket对象,然后发射信号newConnection()。在newConnection()信号的槽函数中,可以用nextPendingConnection()接受客户端的连接,然后使用QTcpSocket与客户端连接通信。

TCPServer程序的运行界面如图所示

在这里插入图片描述

实现的功能:

​ 1、根据IP地址和端口打开网络监听,有客户端连接时创建socket连接

​ 2、采用基于行的数据通信协议,可以接收客户端发来的消息,也可以向客户端发送消息

​ 3、在状态栏显示服务器监听状态和socket状态

TCP服务端代码实现

mainwindow.h的实现

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include    <QTcpServer>
#include    <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QLabel  *LabListen;//状态栏标签
    QLabel  *LabSocketState;//状态栏标签
    QTcpServer *tcpServer; //TCP服务器
    QTcpSocket *tcpSocket;//TCP通讯的Socket
    QString getLocalIP();//获取本机IP地址

protected:
    void    closeEvent(QCloseEvent *event);

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    
private slots:
//自定义槽函数
    void    onNewConnection();//QTcpServer的newConnection()信号
    void    onSocketStateChange(QAbstractSocket::SocketState socketState);
    void    onClientConnected(); //Client Socket connected
    void    onClientDisconnected();//Client Socket disconnected
    void    onSocketReadyRead();//读取socket传入的数据
//UI生成的
    void on_actStart_triggered();
    void on_actStop_triggered();
    void on_actClear_triggered();
    void on_btnSend_clicked();
    void on_actHostInfo_triggered();
    
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp的实现

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include    <QtNetwork>

QString MainWindow::getLocalIP()
{//获取本机IPv4地址
    QString hostName=QHostInfo::localHostName();//本地主机名
    QHostInfo   hostInfo=QHostInfo::fromName(hostName);
    QString   localIP="";

    QList<QHostAddress> addList=hostInfo.addresses();//

    if (!addList.isEmpty())
    for (int i=0;i<addList.count();i++)
    {
        QHostAddress aHost=addList.at(i);
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
            localIP=aHost.toString();
            break;
        }
    }
    return localIP;
}

void MainWindow::closeEvent(QCloseEvent *event)
{//关闭窗口时停止监听
    if (tcpServer->isListening())
        tcpServer->close();;//停止网络监听
    event->accept();
}

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

    LabListen=new QLabel("监听状态:");
    LabListen->setMinimumWidth(150);
    ui->statusBar->addWidget(LabListen);

    LabSocketState=new QLabel("Socket状态:");//状态栏信息
    LabSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(LabSocketState);

    QString localIP=getLocalIP();//本机IP
    this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);  //设置标题
    ui->comboIP->addItem(localIP);
    //创建QTcpServer实例 tcpServer,并将其newConnection()信号与onNewConnection()槽函数关联
    tcpServer=new QTcpServer(this);
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
}

MainWindow::~MainWindow()
{
    delete ui;
}
//开始监听后,TCPClient就可以通过IP地址和端口连接到服务器,客户端接入时,tcpServe回发射newConnection()信号
//关联的槽函数onNewConnection()就会运行
void MainWindow::onNewConnection()
{
//    ui->plainTextEdit->appendPlainText("有新连接");
    tcpSocket = tcpServer->nextPendingConnection(); //创建socket

    connect(tcpSocket, SIGNAL(connected()),
            this, SLOT(onClientConnected()));
    onClientConnected();//

    connect(tcpSocket, SIGNAL(disconnected()),
            this, SLOT(onClientDisconnected()));

    connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    onSocketStateChange(tcpSocket->state());

    connect(tcpSocket,SIGNAL(readyRead()),
            this,SLOT(onSocketReadyRead()));
}

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{//socket状态变化时
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        LabSocketState->setText("scoket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        LabSocketState->setText("scoket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        LabSocketState->setText("scoket状态:ConnectingState");
        break;

    case QAbstractSocket::ConnectedState:
        LabSocketState->setText("scoket状态:ConnectedState");
        break;

    case QAbstractSocket::BoundState:
        LabSocketState->setText("scoket状态:BoundState");
        break;

    case QAbstractSocket::ClosingState:
        LabSocketState->setText("scoket状态:ClosingState");
        break;

    case QAbstractSocket::ListeningState:
        LabSocketState->setText("scoket状态:ListeningState");
    }
}

void MainWindow::onClientConnected()
{//客户端接入时,显示状态栏信息
    ui->plainTextEdit->appendPlainText("**client socket connected");
    ui->plainTextEdit->appendPlainText("**peer address:"+
                                   tcpSocket->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:"+
                                   QString::number(tcpSocket->peerPort()));
}

void MainWindow::onClientDisconnected()
{//客户端断开连接时
    ui->plainTextEdit->appendPlainText("**client socket disconnected");
    tcpSocket->deleteLater();
    //    deleteLater();//QObject::deleteLater();
}

void MainWindow::onSocketReadyRead()
{//读取缓冲区行文本
//    QStringList   lines;
    while(tcpSocket->canReadLine())
        ui->plainTextEdit->appendPlainText("[in] "+tcpSocket->readLine());
//        lines.append(clientConnection->readLine());
}

void MainWindow::on_actStart_triggered()
{//开始监听
    QString     IP=ui->comboIP->currentText();//IP地址
    quint16     port=ui->spinPort->value();//端口
    QHostAddress    addr(IP);
    //作为服务器,QTcpServer类需要调用linsten()在本机某个IP地址和端口上开始监听,等待客户端接入
    tcpServer->listen(addr,port);//
//    tcpServer->listen(QHostAddress::LocalHost,port);// Equivalent to QHostAddress("127.0.0.1").
    ui->plainTextEdit->appendPlainText("**开始监听...");
    ui->plainTextEdit->appendPlainText("**服务器地址:"
                       +tcpServer->serverAddress().toString());
    ui->plainTextEdit->appendPlainText("**服务器端口:"
                       +QString::number(tcpServer->serverPort()));

    ui->actStart->setEnabled(false);
    ui->actStop->setEnabled(true);

    LabListen->setText("监听状态:正在监听");
}

void MainWindow::on_actStop_triggered()
{//停止监听
    if (tcpServer->isListening()) //tcpServer正在监听
    {
        tcpServer->close();//停止监听
        ui->actStart->setEnabled(true);
        ui->actStop->setEnabled(false);
        LabListen->setText("监听状态:已停止监听");
    }
}

void MainWindow::on_actClear_triggered()
{
    ui->plainTextEdit->clear();
}

void MainWindow::on_btnSend_clicked()
{//发送一行字符串,以换行符结束
    QString  msg=ui->editMsg->text();
    ui->plainTextEdit->appendPlainText("[out] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();

    QByteArray  str=msg.toUtf8();
    str.append('\n');//添加一个换行符
    tcpSocket->write(str);
}

void MainWindow::on_actHostInfo_triggered()
{//获取本机地址
    QString hostName=QHostInfo::localHostName();//本地主机名
    ui->plainTextEdit->appendPlainText("本机主机名:"+hostName+"\n");
    QHostInfo   hostInfo=QHostInfo::fromName(hostName);

    QList<QHostAddress> addList=hostInfo.addresses();//
    if (!addList.isEmpty())
    for (int i=0;i<addList.count();i++)
    {
        QHostAddress aHost=addList.at(i);
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
            QString IP=aHost.toString();
            ui->plainTextEdit->appendPlainText("本机IP地址:"+aHost.toString());
            if (ui->comboIP->findText(IP)<0)
                ui->comboIP->addItem(IP);
        }
    }
}
QTcpServer的几个信号的作用

​ 1、connected()信号,客户端socket连接建立时发射此信号

​ 2、disconnected()信号,客户端socket连接断开时发射此信号

​ 3、stateChanged(),本程序的socket状态变化时发射此信号

​ 4、readyRead(),本程序的socket的读取缓冲区有新的数据时发射此信号

与TCPClient的数据通信

QTcpSocket类间接继承于QIODevice,所以支持流读写功能。

Socket之间的数据通信协议一般有两种方式:基于行、基于数据块。

基于块的数据通信协议

用于一般的数据的传输,需要自定义具体的格式

基于行的数据通信协议

一般用于纯文本数据的通信,每一行数据以一个换行符结束。canReadLine()函数判断是否有新的一行数据需要读取,再用readLine()函数读取一行数据。代码实现如下:

void MainWindow::onSocketReadyRead()
{//读取缓冲区行文本
//    QStringList   lines;
    while(tcpSocket->canReadLine())
        ui->plainTextEdit->appendPlainText("[in] "+tcpSocket->readLine());
//        lines.append(clientConnection->readLine());
}
单击窗口上的“发送消息”,将文本框内容发送给客户端,代码实现如下
void MainWindow::on_btnSend_clicked()
{//发送一行字符串,以换行符结束
    QString  msg=ui->editMsg->text();
    ui->plainTextEdit->appendPlainText("[out] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();

    QByteArray  str=msg.toUtf8();
    str.append('\n');//添加一个换行符
    tcpSocket->write(str);
}

从上面的代码可以知道,读取文本框字符串到msg后,先将其转换为QByteArray类型字节数组str,然后在str后添加一个换行符,用QIODevice的write()函数写入缓冲区。这样就可以给客户端发送一行数据了。

程序运行结果如图所示

在这里插入图片描述

客户端实现请跳转到:

https://blog.csdn.net/weixin_56169816/article/details/131404035?spm=1001.2014.3001.5501

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值