Qt网络编程基础(TCP通信)

Qt为我们封装好了QTcpServer和QTcpSocket这两个类,我们只需要学习搭建流程以及接口使用就行,本文章主要实现了简易的客户端和服务器之间的tcp通信,服务器可指定客户端端口单发信息,也可群发。

下面这张图展示服务器和客户端怎么建联和通信流程

TCP服务器

下面我们来搭建一个简易的服务器,

最主要的功能:监听(listen),处理新的连接,处理收到的数据,处理断开连接

创建Widget工程

注意再.pro文件添加network模块

添加private成员

将光标移动到类类型上,使用快捷键Alt+Enter添加缺省的头文件

这里缺省的是<QTcpServer>,<QTcpSocket>

ui设计

控件名字参考红框

逻辑代码编写

Widget.h

成员函数功能见注释

#ifndef WIDGET_H
#define WIDGET_H

#include <QTcpServer>
#include <QTcpSocket>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    QString getCurrentTime();//获取系统当前时间
    QString getLocalIP();//获取本机ip
    void highLight(QString displayString,QColor color);//设置高亮格式
    void myComboBox_refresh();//刷新已连接的socket端口号
public slots:
    void do_newConnection();//处理新来的连接
    void do_readyRead();//处理读到的数据
    void do_disConnection();//设置收到的客户端数据为高亮
    void do_stateChanged();//处理客户端的状态变化
private slots:
    void on_openServerBtn_clicked();//点击打开服务器

    void on_closeServerBtn_clicked();//点击关闭服务器

    void on_sendBtn_clicked();//点击发送信息

    void on_comboBox_socket_activated(int index);//获取comboBox下标

    void on_pushButton_refresh_clicked();//点击刷新comboBox列表

private:
    Ui::Widget *ui;
    QTcpServer* server;
    QTcpSocket* socket;
    int childrenIndex = 0;//记录comboBox的某一个下标
};
#endif // WIDGET_H

Widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QDateTime>
#include <QHostInfo>
#include <QMessageBox>
#include <QTextCursor>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    server = new QTcpServer(this);
    //socket = new QTcpSocket(this);

    ui->lineEdit_port->setText("8888");//设置服务器默认端口
    ui->closeServerBtn->setEnabled(false);
    ui->sendBtn->setEnabled(false);

    connect(server,&QTcpServer::newConnection,this,&Widget::do_newConnection);//处理连接的信号槽

}


 //获取当前时间
QString Widget::getCurrentTime(){
    //获取当前时间
    QDateTime currentTime = QDateTime::currentDateTime();
    QString timeString = currentTime.toString("yyyy-MM-dd hh:mm:ss");
    return timeString;
}

//处理新来的连接
void Widget::do_newConnection(){
    socket = server->nextPendingConnection();//下一个待处理的连接
    connect(socket,&QTcpSocket::disconnected,this,&Widget::do_disConnection);
    connect(socket,&QTcpSocket::readyRead,this,&Widget::do_readyRead);
    connect(socket,&QTcpSocket::stateChanged,this,&Widget::do_stateChanged);
    QString displayString = "\n客户端:["+getCurrentTime()+"]["+socket->peerAddress().toString() +"|"
                            +QString::number(socket->peerPort())+"] connected!!!";
    highLight(displayString,Qt::green);
    myComboBox_refresh();//刷新连接列表
    ui->sendBtn->setEnabled(true);

}

//处理断开连接
void Widget::do_disConnection(){
    QTcpSocket *tmpSocket = qobject_cast<QTcpSocket *>(sender());//获取断开连接的发送者
    QString displayString = "\n客户端:["+getCurrentTime()+"]["+socket->peerAddress().toString() +"|"
                            +QString::number(socket->peerPort())+"] disConnected!!!!!";
    highLight(displayString,Qt::red);
    tmpSocket->deleteLater();
    myComboBox_refresh();//刷新连接列表
    ui->comboBox_socket->setCurrentIndex(ui->comboBox_socket->count()-1);
    if(server->findChildren<QTcpSocket*>().empty()) ui->sendBtn->setEnabled(false);
}

//处理读到的数据
void Widget::do_readyRead(){
    QTcpSocket *tmpSocket = qobject_cast<QTcpSocket *>(sender());//获取断开连接的发送者
    QString buf;
    buf = QString::fromUtf8(tmpSocket->readAll());//读取内容
    QString displayString = "\n客户端:[" + getCurrentTime() + "][" + tmpSocket->peerAddress().toString() + "|"
                            + QString::number(tmpSocket->peerPort()) + "] " + buf;
    //设置高亮
    highLight(displayString,Qt::blue);
}

//处理socket状态变化
void Widget::do_stateChanged(){

}

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

//点击打开服务器
void Widget::on_openServerBtn_clicked()
{
    if(ui->lineEdit_port->text().isEmpty()){
        QMessageBox::information(this,"错误","端口号未设置",QMessageBox::Ok);
        return ;
    }
    if(! server->listen(QHostAddress(getLocalIP()),ui->lineEdit_port->text().toInt())){
        QMessageBox::information(this,"错误","端口号被占用",QMessageBox::Ok);
        return;
    }
    //server->listen(QHostAddress(getLocalIP()),ui->lineEdit_port->text().toInt());//监听所有来连接服务器的客户端
    ui->openServerBtn->setEnabled(false);
    ui->closeServerBtn->setEnabled(true);
}

//点击关闭服务器
void Widget::on_closeServerBtn_clicked()
{
    //关闭所有socket
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
    for(QTcpSocket *tmp : tcpSocketClients){
        tmp->close();
        tmp->deleteLater();
    }
    server->close();
    ui->closeServerBtn->setEnabled(false);
    ui->openServerBtn->setEnabled(true);
}

//点击发送(单发还是群发)
void Widget::on_sendBtn_clicked()
{
    //socket->write(ui->lineEdit_send->text().toLocal8Bit().data());
    QString displayString = "\n我:"+ui->lineEdit_send->text();
    highLight(displayString,Qt::black);
    //server自己会保存客户端的连接,通过findChildren找到客户端的连接
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
    //选择单独发送还是群发
    if(!ui->comboBox_socket->currentText().isEmpty() &&  ui->comboBox_socket->currentText()!="all"){
        tcpSocketClients[childrenIndex]->write(ui->lineEdit_send->text().toLocal8Bit().data());
    }
    else{
        //发送到每个客户端
        for(QTcpSocket *tmp : tcpSocketClients){
            tmp->write(ui->lineEdit_send->text().toLocal8Bit().data());
        }
    }

}
//获取本机iP
QString Widget::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 Widget::highLight(QString displayString,QColor color){
    QTextCursor cursor = ui->textEdit_history->textCursor();// 获取 QTextEdit 的文本光标
    cursor.movePosition(QTextCursor::End);// 移动光标到文档末尾
    // 设置高亮样式
    QTextCharFormat highlightFormat;
    highlightFormat.setForeground(color); // 设置为蓝色字体
    cursor.insertText(displayString, highlightFormat);// 插入带高亮的文本
    ui->textEdit_history->setTextCursor(cursor);// 确保文本编辑器的光标保持在文档末尾(这一步在这个特定情况下是可选的)
    ui->textEdit_history->ensureCursorVisible();// 如果你想要滚动到文本编辑器的底部(显示最新消息)
}

//刷新已经连接的客户端port列表
void Widget::myComboBox_refresh()
{
    //server自己会保存客户端的连接,通过findChildren找到客户端的连接
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
    ui->comboBox_socket->clear();

    //发送到每个客户端
    for(QTcpSocket *tmp : tcpSocketClients){
        if(tmp!=nullptr)
        {
            ui->comboBox_socket->addItem(QString::number(tmp->peerPort()));
        }
    }
    ui->comboBox_socket->addItem("all");
}

//获取客户端端口列表下标
void Widget::on_comboBox_socket_activated(int index)
{
    childrenIndex = index;
}

//点击刷新客户端端口列表
void Widget::on_pushButton_refresh_clicked()
{
    myComboBox_refresh();
}

TCP客户端

客户端相对服务器相对简单,

最主要的功能:处理连接,处理收到的数据,处理断开连接

创建Widget工程

同样需要再.pro文件中添加network

ui设计

逻辑代码

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QTcpSocket>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void do_readyRead();//处理收到的数据
    void do_connected();//处理连接
    void do_disconnected();//处理断开连接
private slots:
    void on_pushButton_connect_clicked();//点击连接

    void on_pushButton_disconnect_clicked();//点击断开连接

    void on_pushButton_send_clicked();//点击发送

private:
    Ui::Widget *ui;
    QTcpSocket* socket;
};
#endif // WIDGET_H

Widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QDateTime>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket = new QTcpSocket(this);
    ui->lineEdit_ip->setText("10.6.134.244");//注意:根据自己的本地ip修改
    ui->lineEdit_Port->setText("8888");
    ui->pushButton_connect->setEnabled(true);
    ui->pushButton_disconnect->setEnabled(false);
    ui->pushButton_send->setEnabled(false);
    connect(socket,&QTcpSocket::connected,this,&Widget::do_connected);
    connect(socket,&QTcpSocket::disconnected,this,&Widget::do_disconnected);
    connect(socket,&QTcpSocket::readyRead,this,&Widget::do_readyRead);
}

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

//处理连接
void Widget::do_connected(){
    ui->textEdit_history->append("连接服务器成功!!");
    ui->pushButton_connect->setEnabled(false);
    ui->pushButton_disconnect->setEnabled(true);
    ui->pushButton_send->setEnabled(true);
}

//处理断开
void Widget::do_disconnected(){
    ui->textEdit_history->append("连接服务器断开!!!");
    socket->close();
    ui->pushButton_connect->setEnabled(true);
    ui->pushButton_disconnect->setEnabled(false);
    ui->pushButton_send->setEnabled(false);
}

//点击连接
void Widget::on_pushButton_connect_clicked()
{
    socket->connectToHost(ui->lineEdit_ip->text(),ui->lineEdit_Port->text().toUInt());
}

//处理收到的数据
void Widget::do_readyRead(){
    //获取当前时间
    QDateTime currentTime = QDateTime::currentDateTime();
    QString timeString = currentTime.toString("yyyy-MM-dd hh:mm:ss");
    QString buf;
    buf = QString::fromUtf8(socket->readAll());
    ui->textEdit_history->append("服务器:["+timeString+":"+socket->peerAddress().toString()
                                 +"|"+QString::number(socket->peerPort())+"]"+buf);

}


//点击断开连接
void Widget::on_pushButton_disconnect_clicked()
{
    socket->close();
    ui->pushButton_connect->setEnabled(true);
    ui->pushButton_disconnect->setEnabled(false);
    ui->pushButton_send->setEnabled(false);
}

//点击发送
void Widget::on_pushButton_send_clicked()
{
    socket->write(ui->lineEdit_send->text().toLocal8Bit().data());
    ui->textEdit_history->append("我:"+ui->lineEdit_send->text());
}

效果展示

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值