QT 进程间通信——Socket通信

简述

通过Qt提供的QtNetwork模块即可实现进程间通信,TCP/IP在实现应用程序和进程内部通信或与远程进程间的通信方面非常有用。QtNetwork模块提供的类能够创建基于TCP/IP的客户端与服务端应用程序。为实现底层的网络访问,可以使用QTcpSocket、QTcpServer和QUdpSocket,并提供底层网络类。还提供了使用常规协议实现网络操作的QNetworkRequest、QNetworkReply、QNetworkAccessManager。

TCP/IP协议:

TCP(Transmission Control Protocol 传输控制协议)和IP(Internet Protocol 网络协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP/IP提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。它将软件通信过程抽象化为四个抽象层,采取协议堆栈的方式,分别实现出不同通信协议。

Socket接口:

Socket又称"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;TCP/IP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

代码下载:https://download.csdn.net/download/Sakuya__/12631972

 


效果

 


代码之路

使用QTcpServer和QTcpSocket之前都需要在pro文件中加入:

QT       += network

一、服务器:      

         1.创建QTcpServer对象
         2.启动服务器(监听)调用成员方法listen(QHostAddress::Any, 端口号)
         3.当有客户端链接时候会发送newConnection信号,触发槽函数接受链接(得到一个与客户端通信的套接字QTcpSocket)
         4.QTcpsocket发送数据用成员方法write,
         5.读数据当客户端有数据来,QTcpSocket对象就会发送readyRead信号,关联槽函数读取数据

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_Btn_Listen_clicked();

    void server_New_Connect();

    void Read_Data();

    void disConnected();

    void on_Btn_Send_clicked();

    void on_Btn_Clean_clicked();

private:
    Ui::MainWindow *ui;

    QTcpSocket *socket;
    QTcpServer *server;

    QList clientSocket;
};

#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);

    socket = new QTcpSocket();
    server = new QTcpServer();

    // 获取本地的IP
    for (int i = 0; i < QNetworkInterface().allAddresses().length(); ++i) {
        ui->comboBox_IP->addItem(QNetworkInterface().allAddresses().at(i).toString());
    }

    QPalette pe;
    pe.setColor(QPalette::WindowText, Qt::blue);
    ui->label_SeverState->setPalette(pe);
    ui->label_SeverState->setText("服务器未打开");

    // 设置tableWidget不可编辑
    ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);

    // 关联客户端连接信号newConnection
    connect(server, &QTcpServer::newConnection, this, &MainWindow::server_New_Connect);
}

MainWindow::~MainWindow()
{
    server->close();
    server->deleteLater();
    delete ui;
}

void MainWindow::on_Btn_Listen_clicked()
{
    if (ui->Btn_Listen->text() == tr("侦听")) {
        // 从输入端获取端口号
        int port = ui->label_Port->text().toInt();

        // 侦听指定的端口
        if(!server->listen(QHostAddress::Any, port)) {
            // 若出错,则输出错误信息
            QMessageBox::information(this, tr("错误"), server->errorString(), QMessageBox::Yes);
            return;
        }
        else {
            // 修改按键文字
            ui->Btn_Listen->setText("取消侦听");
            QPalette pe;
            pe.setColor(QPalette::WindowText, Qt::red);
            ui->label_SeverState->setPalette(pe);
            ui->label_SeverState->setText("服务器运行中...");
        }
    }
    else {
        // 如果正在连接......
        if(socket->state() == QAbstractSocket::ConnectedState) {
            // 关闭连接
            socket->disconnectFromHost();
        }
        // 取消侦听
        server->close();
        // 修改按键文字
        ui->Btn_Listen->setText("侦听");
        QPalette pe;
        pe.setColor(QPalette::WindowText, Qt::blue);
        ui->label_SeverState->setPalette(pe);
        ui->label_SeverState->setText("服务器未打开");
    }
}

void MainWindow::server_New_Connect()
{
    // 获取客户端连接
    socket = server->nextPendingConnection();
    clientSocket.append(socket);

    // 把连接到的客户端添加入tableWidget中
    int currentRow = ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(currentRow);
    QTableWidgetItem *item = new QTableWidgetItem();
    QTableWidgetItem *item_2 = new QTableWidgetItem();
    QTableWidgetItem *item_3 = new QTableWidgetItem();
    QTableWidgetItem *item_4 = new QTableWidgetItem();
    item->setText(tr("0000000%1").arg(QString::number(ui->tableWidget->rowCount())));
    item_2->setText(clientSocket[currentRow]->peerAddress().toString().mid(7));
    item_3->setText(QString::number(clientSocket[currentRow]->peerPort()));
    item_4->setText("在线");
    ui->tableWidget->setItem(currentRow, 0, item);
    ui->tableWidget->setItem(currentRow, 1, item_2);
    ui->tableWidget->setItem(currentRow, 2, item_3);
    ui->tableWidget->setItem(currentRow, 3, item_4);

    // 连接QTcpSocket的信号槽,以读取新数据
    connect(socket, SIGNAL(readyRead()), this, SLOT(Read_Data()));
    connect(socket, SIGNAL(disconnected()), this, SLOT(disConnected()));
}

// 从客户端接收到的消息
void MainWindow::Read_Data()
{
    // 由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端
    for (int i = 0; i < clientSocket.length(); ++i) {
        // 读取缓冲区数据
        QByteArray buffer = clientSocket[i]->readAll();
        if(buffer.isEmpty()) {
            continue;
        }

        static QString IP_Port, IP_Port_Pre;
        IP_Port = tr("[%1:%2]:").arg(clientSocket[i]->peerAddress().toString().mid(7)).arg(clientSocket[i]->peerPort());

        // 若此次消息的地址与上次不同,则需显示此次消息的客户端地址
        if (IP_Port != IP_Port_Pre) {
            ui->textEdit_Recv->append(IP_Port);
        }

        ui->textEdit_Recv->append(buffer);

        // 更新ip_port
        IP_Port_Pre = IP_Port;
    }
}

void MainWindow::disConnected()
{
    // 遍历寻找断开连接的是哪一个客户端
    for(int i = 0; i < clientSocket.length(); ++i) {
        if(clientSocket[i]->state() == QAbstractSocket::UnconnectedState)
        {
            // 删除存储在tableWidget中的该客户端信息
            for (int j = 0; j < ui->tableWidget->rowCount(); ++j) {
                if (clientSocket[i]->peerAddress().toString().mid(7) == ui->tableWidget->item(j, 1)->text()) {
                    ui->tableWidget->removeRow(j);
                }
            }
            // 删除存储在clientSocket列表中的客户端信息
            clientSocket[i]->destroyed();
            clientSocket.removeAt(i);
        }
    }
}

void MainWindow::on_Btn_Send_clicked()
{
    // 获取选中的行
    int row = ui->tableWidget->currentRow();

    QString data = ui->lineEdit_send->text();
    if (data.isEmpty()) {
        QMessageBox::information(this, "提示", "请输入发送内容!", QMessageBox::Yes);
    }
    else {
        if (row >= 0) {
            for (int i = 0; i < clientSocket.length(); ++i) {
                if (QString::number(clientSocket[i]->peerPort()) == ui->tableWidget->item(row, 2)->text()) {
                    //以ASCII码形式发送文本框内容
                    clientSocket[i]->write(data.toLatin1());
                }
            }
        }
        else {
            socket->write(data.toLatin1());
        }
    }
}

void MainWindow::on_Btn_Clean_clicked()
{
    ui->textEdit_Recv->clear();
}

服务端源代码百度云下载地址:https://pan.baidu.com/s/1jg4uNLoHcTVmPtX2AwOTfg    提取码:l6gy

二、客户端  :  

        1.创建QTcpSocket对象
        2.链接服务器connectToHost(QHostAddress("ip"),端口号)
        3.QTcpsocket发送数据用成员方法write,
        4.读数据当对方有数据来,QTcpSocket对象就会发送readyRead信号,关联槽函数读取数据

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_Btn_Connect_clicked();

    void onStateChanged(int state);

    void onErrorOccurred();

    void Read_Data();

    void on_Btn_exit_clicked();

    void on_Btn_send_clicked();

private:
    Ui::MainWindow *ui;
    QTcpSocket *socket;
};

#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);

    //初始化TcpSocket
    socket = new QTcpSocket();
}

MainWindow::~MainWindow()
{
    delete this->socket;
    delete ui;
}

void MainWindow::on_Btn_Connect_clicked()
{
    if(ui->Btn_Connect->text() == tr("连接") && socket->state() != QTcpSocket::ConnectedState )
    {
        //获取IP地址
        QString IP = ui->lineEdit_IP->text();
        //获取端口号
        int port = ui->lineEdit_Port->text().toInt();

        connect(socket, &QTcpSocket::readyRead, this, &MainWindow::Read_Data);
        connect(socket, &QTcpSocket::stateChanged, this, &MainWindow::onStateChanged);
        connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onErrorOccurred()));

        //取消原有连接
        socket->abort();
        //连接服务器
        socket->connectToHost(IP, port);

        //等待连接成功
        if(!socket->waitForConnected(3000))
        {
            return;
        }
        else {
            ui->Btn_Connect->setText("断开\n连接");

            QMessageBox::information(this, "提示", "连接成功", QMessageBox::Yes);
        }
    }
    else
    {
        //断开连接
        socket->disconnectFromHost();
        //修改按键文字
        ui->Btn_Connect->setText("连接");
        return;
    }
}

void MainWindow::onStateChanged(int state)
{
    if (state == QTcpSocket::UnconnectedState) {
        ui->Btn_send->setEnabled(false);
        ui->Btn_Connect->setText("连接");
    }
    else if (state == QTcpSocket::ConnectedState) {
        ui->Btn_send->setEnabled(true);
        ui->Btn_Connect->setText("断开连接");
    }
}

void MainWindow::onErrorOccurred()
{
    QMessageBox::information(this, "错误", socket->errorString(), QMessageBox::Yes);
}

void MainWindow::Read_Data()
{
    QByteArray buffer;
    //读取缓冲区数据
    buffer = socket->readAll();
    //qDebug() << buffer;

    if(!buffer.isEmpty())
    {
        QMessageBox::information(this, "收到消息", buffer, QMessageBox::Yes);
    }
}

void MainWindow::on_Btn_exit_clicked()
{
    this->close();
}

void MainWindow::on_Btn_send_clicked()
{
    QString data = ui->lineEdit_Send->text();
    socket->write(data.toLatin1());
}

客户端源代码百度云下载地址:https://pan.baidu.com/s/1Jgx14Shu6Vif1Li1OQ-brQ    提取码:v243

 

你可能感兴趣的:(QT,计算机网络,Qt,进程间通信,Sockets)