基于QT的TCP通信

一、简介

TCP通信必须先建立TCP链接,通信端分为客户端和服务器端。QT提供了QTcpServer类和QTcpSocket类用于建立TCP通信应用程序。QTcpServer用于端口监听,建立服务器;QTcpSocket用于建立连接后使用套接字(Socket)进行通信。

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

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(Qt实战项目,C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

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

所以在客户端与服务器端建立TCP链接以后,具体的数据通信是通过QTcpSocket完成的。

客户端的QTcpSocket实列首先通过connectToHost()尝试链接到服务器,需要指定服务器的IP地址和端口。connectToHost()是异步方式链接服务器,不会阻塞程序运行,链接后发射connected()信号。

与服务器端建立socket链接后,就可以向缓冲区写入数据或从接收缓冲区读取数据,实现数据的通信。当缓冲区有新的数据进入时,会发射readyRead()信号,一般在此信号的槽函数里读取缓冲区数据。

QTcpSocket是从QIODevice间接继承的,所以可以使用流数据读写功能,一个QTcpSocket实列既可以接收数据也可以发送数据,且接收和发送是异步工作的,有各自的缓冲区。

二、TCPServer(TCP服务端的设计)

TCPServer程序具有如下功能:

根据指定IP地址(本机地址)和端口打开网络监听,有客户端连接时创建socket链接

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

在状态栏显示服务器监听状态和socket的状态

ui界面如下:

程序设计

TCPServer是一个窗口基于QMainWindow的应用程序,程序名称为QTCPServer。其中mainwindow.h如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QTcpServer>
#include <QHostInfo>
#include <QTcpSocket>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    QLabel *LabListen;
    QLabel *LabSocketState;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
    QStringList getLocalIP();
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    void onNewConnection();         //QTcpServer的newConnection()信号
    void onSocketStateChange(QAbstractSocket::SocketState);
    void onClientConnected();       //Client Socket connected
    void onClientDisconnected();    //Client Socket disconnected
    void onSocketReadRead();        //读取socket传入的数据
    void on_act_start_triggered();
    void on_act_stop_triggered();
    void on_pb_sendmessage_clicked();
};

#endif // MAINWINDOW_H

mainwindow.cpp如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    LabListen = new QLabel("监听状态:");
    LabListen->setMinimumWidth(200);
    ui->statusBar->addWidget(LabListen);

    LabSocketState = new QLabel("Socket 状态:");
    LabSocketState->setMinimumWidth(250);
    ui->statusBar->addWidget(LabSocketState);

    QStringList localeIP = getLocalIP(); //本机IP
    ui->cb_address->addItems(localeIP);
    tcpServer = new QTcpServer(this);
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

QStringList MainWindow::getLocalIP()
{
    //获取本机IPV4地址
    QString hostName = QHostInfo::localHostName();      //本地主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName); //根据主机名获取ip
    QStringList localIp;
    QList<QHostAddress> addList = hostInfo.addresses();

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

void MainWindow::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event);
}

void MainWindow::onNewConnection()
{
    tcpSocket = tcpServer->nextPendingConnection();  //获取socket
    connect(tcpSocket,SIGNAL(connected()),this,SLOT(onClientConnected()));  //connected()信号,客户端socket连接时发射此信号
    onClientConnected();
    connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(onClientDisconnected()));    //disconnected()信号,客户端socket断开时发射此信号
    connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadRead()));
}

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketstate)
{
    //socket状态变化
    switch (socketstate)
    {
    case QAbstractSocket::UnconnectedState:
        LabSocketState->setText("socket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        LabSocketState->setText("socket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        LabSocketState->setText("socket状态:ConnectingState");
        break;
    case QAbstractSocket::ConnectedState:
        LabSocketState->setText("socket状态:ConnectedState");
        break;
    case QAbstractSocket::BoundState:
        LabSocketState->setText("socket状态:BoundState");
        break;
    case QAbstractSocket::ClosingState:
        LabSocketState->setText("socket状态:ClosingState");
        break;
    case QAbstractSocket::ListeningState:
        LabSocketState->setText("socket状态:ListeningState");
        break;
    }
}

void MainWindow::onClientConnected()
{
    //客户端接入时
    ui->plainTextEdit->appendPlainText("**client socket connected");
    ui->plainTextEdit->appendPlainText("**peer address:"+tcpSocket->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:"+tcpSocket->peerPort());
}

void MainWindow::onClientDisconnected()
{   //客户端断开的时候
    ui->plainTextEdit->appendPlainText("**client socket disconnected");
    tcpSocket->deleteLater(); //deleteLater 会在当前对象的所有事件处理完成后再删除对象
}

void MainWindow::onSocketReadRead()
{
    //读取缓冲区行文本
    while (tcpSocket->canReadLine()) {
       ui->plainTextEdit->appendPlainText("[In] "+tcpSocket->readLine());
    }
}

void MainWindow::on_act_start_triggered()
{
    //开始监听
    QString IP = ui->cb_address->currentText(); //获取IP
    quint16 port = ui->sb_port->value();        //获取端口
//    QHostAddress addr(IP);                      //
    tcpServer->listen(QHostAddress::LocalHost,port);               //开始监听
    ui->plainTextEdit->appendPlainText("**开始监听...");
    ui->plainTextEdit->appendPlainText("**服务器地址:"+tcpServer->serverAddress().toString());
    ui->plainTextEdit->appendPlainText("**服务器端口:"+QString::number(tcpServer->serverPort()));
    ui->act_start->setEnabled(false);
    ui->act_stop->setEnabled(true);
    LabListen->setText("监听状态:正在监听");
}

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

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

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

作为TCP服务器,QTcpServer类需要调用listen()在本机某个IP地址和端口上开始TCP监听,以等待TCP客户端的接入。单击主窗口上“开始监听”按钮可以开始网络监听。

tcpServer开始监听后,TCPClient就可以通过IP地址和端口连接到此服务器。当客户端接入时,tcpServer会发射newConnection()信号。

程序首先通过nextPendingConnection()函数获取与连接进行通信的QTcpSocket对象实例tcpSocket,然后将tcpSocket的几个信号与相应的槽函数链接起来,QTcpSocket的这几个信号的作用是:

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

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

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

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

TCP服务器停止监听,只需要调用QTcpServer的close()函数即可。

三、TCPClient(TCP客户端的设计)

TCPClient程序具有如下功能:

  • 通过IP地址和端口号连接到服务器
  • 采用基于行的数据通信协议,与服务器端收发消息
  • 处理QTcpSocket的StateChange()信号,在状态栏显示socket的状态

ui界面如下:

程序设计

TCPClient也是一个窗口基于QMainWindow的应用程序,其中主窗口定义mainwindow.h如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostInfo>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    QLabel *LabSocketState;
    QTcpSocket *tcpClient;  //socket
    QStringList getLocalIP();
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    void onCennected();
    void onDiscennected();
    void onSocketStateChange(QAbstractSocket::SocketState socketState);
    void onSocketReadyRead();   //读取socket传入的数据
    void on_act_connect_triggered();
    void on_act_disconnect_triggered();
    void on_pb_send_clicked();
};

#endif // MAINWINDOW_H

mainwindow.cpp如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    tcpClient = new QTcpSocket(this);   //创建socket变量
    LabSocketState = new QLabel("socket状态:"); //状态栏标签
    LabSocketState->setMinimumWidth(250);

    ui->statusBar->addWidget(LabSocketState);
    QStringList localIp = getLocalIP();
    ui->cb_address->addItems(localIp);

    connect(tcpClient,SIGNAL(connected()),this,SLOT(onCennected()));
    connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDiscennected()));
    connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    connect(tcpClient,SIGNAL(readyRead()),SLOT(onSocketReadyRead()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

QStringList MainWindow::getLocalIP()
{
    QString localeName = QHostInfo::localHostName();
    QHostInfo ipInfo = QHostInfo::fromName(localeName);
    QStringList ipList;
    QList<QHostAddress> addList = ipInfo.addresses();
    if(!addList.isEmpty())
    {
        for(int i = 0; i < addList.count(); i++)
        {
            QHostAddress aHost = addList.at(i);
            if(QAbstractSocket::IPv4Protocol == aHost.protocol())
            {
                ipList.append(aHost.toString());
            }
        }
    }
    return ipList;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event);
}

void MainWindow::onCennected()
{
    //connected()信号槽函数
    ui->plainTextEdit->appendPlainText("**已连接到服务器");
    ui->plainTextEdit->appendPlainText("**peer address:"+tcpClient->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:"+QString::number(tcpClient->peerPort()));
    ui->act_connect->setEnabled(false);
    ui->act_disconnect->setEnabled(true);
}

void MainWindow::onDiscennected()
{
    //disconnected()信号槽函数
    ui->plainTextEdit->appendPlainText("**已断开与服务器的连接");
    ui->act_connect->setEnabled(true);
    ui->act_disconnect->setEnabled(false);
}

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

void MainWindow::onSocketReadyRead()
{
    //readyRead()信号槽函数
    while(tcpClient->canReadLine())
    {
        ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
    }
}

void MainWindow::on_act_connect_triggered()
{
    //"连接到服务器"按钮
    QString addr = ui->cb_address->currentText();
    qint16 port = ui->sb_port->value();
    tcpClient->connectToHost(addr,port);
}

void MainWindow::on_act_disconnect_triggered()
{
    if(tcpClient->state() == QAbstractSocket::ConnectedState)
    {
        tcpClient->disconnectFromHost();
    }
}

void MainWindow::on_pb_send_clicked()
{
    QString msg = ui->le_message->text();
    ui->plainTextEdit->appendPlainText("[out] "+msg);
    ui->le_message->clear();
    ui->le_message->setFocus();
    QByteArray str = msg.toUtf8();
    str.append("\n");
    tcpClient->write(str);
}

在窗口上设置服务器IP地址后,调用QTcpSocket的函数connectToHost()链接到服务器,也可以使用disconnectFromHost()函数断开与服务器的链接。

其中,槽函数onSocketStateChange()的功能和代码与TcpServer完全一样。

TCPClient与TCPServer之间采用基于行的数据通信协议。单击“发送消息”按钮将发送一行字符串。在readyRead()信号的槽函数里进行字符串的读取。

实例TCPServer和TCPClient只是简单的演示了TCP通信的原理,TCPServer只允许一个TCPClient客户接入。而一般的TCP服务器程序允许多个客户接入,为了使每个socket链接独立通信且互不影响,一般采用多线程,即为一个socket链接创建一个新的线程。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(Qt实战项目,C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

猜你喜欢

转载自blog.csdn.net/m0_73443478/article/details/132601960
今日推荐