Qt Tcp网络编程 Tcp通信 服务端编程

Qt Tcp网络编程 Tcp通信 服务端编程


一、界面

1.效果

在这里插入图片描述

2.文件结构
在这里插入图片描述

3.相关部件

在这里插入图片描述

说明一下,我用的是Qt 5.15写的,还有就是这是Tcp服务端,功能方面应该看见界面就都知道了,就不再重复说了。

二、代码
代码也没啥好说的了,注释我都基本写上了,就是别忘了在pro文件里加上QT += network

1.TcpServer_zlh.pro


QT       += core gui
QT       += network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target

2.widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QMessageBox>
#include <QDebug>
#include <QFileDialog>
#include <QFile>
#include <QFileDevice>
#include <QFileInfo>
#include <QDateTime>
#include <QDir>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    QTcpServer *tcpserver;
    QList<QTcpSocket*> client_connections;
    QTcpSocket *current_client;

    void recv_display();
    //void recv_open_file();
    void recv_file();
    void file_infomation();

    void send_file_infomation();
    void send_display();
    void send_file();

private slots:
    void on_clean_recv_clicked();
    void on_clean_send_clicked();

    void new_connection_slot();
    void disconnect_slot();
    void recv();
    //void send();

    void on_listen_clicked();
    void on_send_clicked();
    void on_file_box_stateChanged(int arg1);


    void on_send_from_file_stateChanged(int arg1);

private:
    Ui::Widget *ui;



};
#endif // WIDGET_H

3.main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.setWindowTitle("Tcp Server");
    w.show();
    return a.exec();
}

4.widget.cpp

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

QString ip_port,ip_port_pre;
bool listen_flag = 0;
QByteArray buffer;
QString file_name;
QString send_file_name;
QFile recv_data;
QFile send_data;
bool file_open_flag=false;
bool send_file_open_flag = false;
QTcpSocket *client;

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

    tcpserver = new QTcpServer(this);

    ui->ip->setText(QNetworkInterface().allAddresses().at(1).toString());//获取本机IP地址,并设置IP
    ui->port->setText("59001");//设置端口号
    ui->send->setEnabled(false);//将发送按钮设置为不可点击
    connect(tcpserver,SIGNAL(newConnection()),this,SLOT(new_connection_slot()));

}

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

//获取连接过来的客户端信息,并保存
void Widget::new_connection_slot()
{
    current_client = tcpserver->nextPendingConnection();//获取连接过来的客户端信息
    client_connections.append(current_client);//保存

    //将客户端的信息添加到QcomboBox部件上
    ui->connection->addItem(tr("%1:%2").arg(current_client->peerAddress().toString().split("::ffff:")[1]).arg(current_client->peerPort()));

    ui->connection->setEditable(true);//将QcomboBox设置为可编辑状态
    ui->connection->setCurrentText(tr("%1:%2").arg(client_connections[0]->peerAddress().toString().split("::ffff:")[1])\
            .arg(client_connections[0]->peerPort()));//将客户端的信息在QcomboBox上显示出来

    //已经有连接,所以将发送按钮设置为可点击状态
    ui->send->setEnabled(true);

    connect(current_client,SIGNAL(readyRead()),this,SLOT(recv()));
    connect(current_client,SIGNAL(disconnected()),this,SLOT(disconnect_slot()));

}

//监听与取消监听
void Widget::on_listen_clicked()
{
    //先判断目前是处于监听还是非监听状态
    if(listen_flag)//非监听状态,断开所有的连接
    {
        int num = client_connections.length();//得到连接到此服务器的客户端数量
        for(int i =0;i<num;i++)
        {
            QString ipport = tr("%1:%2").arg(client_connections[i]->peerAddress().toString().split("::ffff:")[1])\
                    .arg(client_connections[i]->peerPort());
            ui->connection->removeItem(ui->connection->findText(ipport));//首先在QcomboBox部件上移除
            client_connections[i] ->disconnectFromHost();//然后断开连接,并删除保存的客户端信息
        }
        listen_flag = 0;//是否监听标志位
        ui->listen->setText("已停止监听");
        ui->send->setEnabled(false);//将发送按钮设置为不可点击状态
        tcpserver->close();//停止监听

        ui->connection->setEditable(true);
        ui->connection->setCurrentText("无连接");
    }
    else
    {
        //开始监听,第一个参数表示监听本机所有的网口,第二个参数是监听的端口。
        listen_flag = tcpserver->listen(QHostAddress::Any,ui->port->text().toInt());
        if(listen_flag ==0)
        {
            QMessageBox::information(this,"提示","开启监听错误,请重试");
        }
        else
        {
            ui->listen->setText("已开始监听");
        }
    }
}

//接收数据
void Widget::recv()
{
    if(ui->file_box->isChecked()&&!file_name.isEmpty())
    {
        //打开选中的文件
        recv_data.setFileName(file_name);
        file_open_flag = recv_data.open(QFile::Append|QFile::ReadWrite);
    }
    for(int i = 0;i<client_connections.length();i++)
    {
        buffer = client_connections[i]->readAll();//获取某个客户端发来的信息

        //如果信息为空,则跳过
        if(buffer.isEmpty())
            continue;

        //获取当前客户端的IP和端口号
        ip_port = tr("[%1:%2]:").arg(client_connections[i]->peerAddress().toString().split("::ffff:")[1])\
                .arg(client_connections[i]->peerPort());
        if(ip_port !=ip_port_pre)//如果这里的客户端和上次的不同,则显示客户端的信息,便于明白是那个客户端发送的信息
        {
            ui->recvwindow->append(ip_port);
        }

        //将发送信息到服务器的客户端信息在QcomboBox中显示出来
        ui->connection->setEditable(true);
        ui->connection->setCurrentText(tr("%1:%2").arg(client_connections[i]->peerAddress().toString().split("::ffff:")[1])\
                .arg(client_connections[i]->peerPort()));
        ui->send->setEnabled(true);
        recv_display();//设置一下接收的格式
        ip_port_pre = ip_port;//本次客户端成为上次的客户端了
    }
}


//根据选择的条件设置数据的显示格式
void Widget::recv_display()
{
    if(ui->file_box->isChecked())//选择了将接收的数据保存到文件
    {
        recv_file();
    }
    else
    {
        if(ui->change_line_box->isChecked())//选择了自动换行
        {

            if(ui->hex_box->isChecked())//选择了将数据以十六进制显示
            {
                //将数据转换为十六进制
                QString hex(buffer.toHex().toUpper());
                int len = hex.length()/2+1;
                for(int i =0;i<len;i++)
                {
                    hex.insert(2*i+i-1," ");
                }
                ui->recvwindow->append(hex);//将数据在界面中显示出来
            }
            else
            {
                ui->recvwindow->append(buffer);
            }
        }
        else
        {
            if(ui->hex_box->isChecked())
            {
                //同上
                QString hex(buffer.toHex().toUpper());
                int len = hex.length()/2+1;
                qDebug()<<len;
                for(int i =0;i<len;i++)
                {
                    hex.insert(2*i+i-1," ");
                }
                ui->recvwindow->insertPlainText(hex);
            }
            else
            {
                ui->recvwindow->insertPlainText(buffer);
            }
        }
    }
}

//file_box状态改变了就调用
void Widget::on_file_box_stateChanged(int arg1)
{
    //判断file_box是否处于勾选状态
    if(ui->file_box->isChecked())//如果是
    {
        file_infomation();//选择文件,并获得文件的相关信息
    }

    //判断是否选择了文件
    if(file_name.isEmpty())//如果打开了文件对话框,但是没有选自文件
    {
        //将file_box设置为非勾选状态
        ui->file_box->setCheckState(Qt::Unchecked);
    }
    qDebug()<<arg1;//没用到,但是看见警告难受,就输出了一下
}

//选择文件,获取文件信息
void Widget::file_infomation()
{
    //打开文件按对话框,选择文件
    file_name = QFileDialog::getOpenFileName(
                this,
                tr("文件选择"),
                QDir::currentPath(),
                "Text Files(*.txt);;Word Files(*.doc,*.docx);;C++ Files(*.cpp);;C Files(*.c);;Python Files(*.py)"
                );

    //获取文件信息
    QStringList file_info_list;
    if(!file_name.isEmpty())
    {
        QFileInfo file_info(file_name);
        file_info_list<<tr("文件路径: ")+file_info.absoluteFilePath();
        file_info_list<<tr("文件名: ")+file_info.fileName();
        file_info_list<<tr("文件类型: ")+file_info.suffix();

        qint64 size = file_info.size();
        size = size/1024;
        file_info_list<<tr("文件大小: ")+QString::number(size)+" KB";

        QDateTime time_info = file_info.birthTime();
        file_info_list<<tr("创建日期: ") + time_info.toString("yyyy-MM-dd hh:mm:ss");
        file_info_list<<tr("修改日期: ") + file_info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
        file_info_list<<tr("最后打开日期: ") + file_info.lastRead().toString("yyyy-MM-dd hh:mm:ss");

        QString file_info_str = file_info_list.join('\n');
        ui->recvwindow->append(file_info_str);
    }
    else
    {
        //获取当前时间
        QDateTime current_time = QDateTime::currentDateTime();
        QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");

        ui->recvwindow->append(time_str+" :未选择文件");//没选则文件的提示
    }
}

//将接收的信息保存到文件
void Widget::recv_file()
{
    //判断文件是否打开
    if(file_open_flag)//如果是
    {
        QTextStream out(&recv_data);//使用文本流写文本文件

        if(ui->change_line_box->isChecked())//选则了自动换行
        {
            if(ui->hex_box->isChecked())//选择了转换位十六进制
            {
                //转换位十六进制
                QString hex(buffer.toHex().toUpper());
                int len = hex.length()/2+1;
                for(int i =0;i<len;i++)
                {
                    hex.insert(2*i+i-1," ");
                }
                out<<hex<<"\n";//写入文件

                //获取当前时间
                QDateTime current_time = QDateTime::currentDateTime();
                QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
                ui->recvwindow->append(time_str+" :数据已写入文件");
                recv_data.close();//关闭文件
            }
            else
            {
                //同上
                out<<buffer<<"\n";
                QDateTime current_time = QDateTime::currentDateTime();
                QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
                ui->recvwindow->append(time_str+" :数据已写入文件");
                recv_data.close();
            }
        }
        else
        {
            //同上
            if(ui->hex_box->isChecked())
            {
                QString hex(buffer.toHex().toUpper());
                int len = hex.length()/2+1;
                for(int i =0;i<len;i++)
                {
                    hex.insert(2*i+i-1," ");
                }
                out<<hex;
                QDateTime current_time = QDateTime::currentDateTime();
                QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
                ui->recvwindow->append(time_str+" :数据已写入文件");
                recv_data.close();
            }
            else
            {
                out<<buffer;
                QDateTime current_time = QDateTime::currentDateTime();
                QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
                ui->recvwindow->append(time_str+" :数据已写入文件");
                recv_data.close();
            }

        }
    }
    else
    {
        QDateTime current_time = QDateTime::currentDateTime();
        QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
        ui->recvwindow->append(time_str+" :文件未能打开");
    }

}




//发送数据
void Widget::on_send_clicked()
{
    QString client_ip;
    int client_port;
    if(ui->send_from_file->isChecked()&&!send_file_name.isEmpty())
    {
        //打开文件
        send_data.setFileName(send_file_name);
        send_file_open_flag = send_data.open(QFile::ReadWrite|QIODevice::Text);
    }

    //得到QcomboBox部件上当前显示的客户端的IP和端口号
    client_ip = ui->connection->currentText().split(":")[0];
    client_port = ui->connection->currentText().split(":")[1].toInt();
    for(int i = 0;i<client_connections.length();i++)
    {
        //找到对应的客户端信息
        if(client_connections[i]->peerAddress().toString().split("::ffff:")[1]==client_ip\
                &&client_connections[i]->peerPort()==client_port)
        {
            client = client_connections[i];
            send_display();//一些发送的设置
            break;
        }
    }

}


void Widget::send_display()
{
    //在接收数据中解释过了,不重复了
    if(ui->send_from_file->isChecked())
    {
        send_file();
    }
    else
    {
        QString send_str=ui->sendwindow->toPlainText();
        if(ui->hex_send_box->isChecked())
        {
            QByteArray bu = send_str.toLatin1();
            QString hex(bu.toHex().toUpper());
            int len = hex.length()/2+1;
            qDebug()<<len;
            for(int i =0;i<len;i++)
            {
                hex.insert(2*i+i-1," ");
            }
            client->write(hex.toLatin1());
        }
        else
        {
            client->write(send_str.toLatin1());
        }
    }
}

void Widget::send_file()
{
    //同样在接收中基本都说过了
    QTextStream in(&send_data);
    if(send_file_open_flag)
    {
        if(ui->hex_send_box->isChecked())
        {
            while(!in.atEnd())
            {
                QString line = in.readLine();
                QByteArray bu = line.toLatin1();
                QString hex(bu.toHex().toUpper());
                int len = hex.length()/2+1;
                for(int i =0;i<len;i++)
                {
                    hex.insert(2*i+i-1," ");
                }
                client->write(hex.toLatin1());
            }
            QDateTime current_time = QDateTime::currentDateTime();
            QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
            ui->sendwindow->append(time_str+" :文件发送成功");
            send_data.close();
        }
        else
        {
            while(!in.atEnd())
            {
                QString line = in.readLine();
                client->write(line.toLatin1());
            }
            QDateTime current_time = QDateTime::currentDateTime();
            QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
            ui->sendwindow->append(time_str+" :文件发送成功");
            send_data.close();
        }
    }
    else
    {
        QDateTime current_time = QDateTime::currentDateTime();
        QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
        ui->recvwindow->append(time_str+" :文件未能读取");
        send_data.close();
    }
}


void Widget::on_send_from_file_stateChanged(int arg1)
{
    if(ui->send_from_file->isChecked())
    {
        send_file_infomation();
    }
    if(send_file_name.isEmpty())
    {
        ui->send_from_file->setCheckState(Qt::Unchecked);
    }
    qDebug()<<arg1;
}

void Widget::send_file_infomation()
{
    send_file_name = QFileDialog::getOpenFileName(
                this,
                tr("文件选择"),
                QDir::currentPath(),
                "Text Files(*.txt);;Word Files(*.doc,*.docx);;C++ Files(*.cpp);;C Files(*.c);;Python Files(*.py)"
                );
    QStringList file_info_list;
    if(!send_file_name.isEmpty())
    {
        QFileInfo file_info(send_file_name);
        file_info_list<<tr("文件路径: ")+file_info.absoluteFilePath();
        file_info_list<<tr("文件名: ")+file_info.fileName();
        file_info_list<<tr("文件类型: ")+file_info.suffix();

        qint64 size = file_info.size();
        size = size/1024;
        file_info_list<<tr("文件大小: ")+QString::number(size)+" KB";

        QDateTime time_info = file_info.birthTime();
        file_info_list<<tr("创建日期: ") + time_info.toString("yyyy-MM-dd hh:mm:ss");
        file_info_list<<tr("修改日期: ") + file_info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
        file_info_list<<tr("最后打开日期: ") + file_info.lastRead().toString("yyyy-MM-dd hh:mm:ss");

        QString file_info_str = file_info_list.join('\n');
        ui->sendwindow->append(file_info_str);
    }
    else
    {
        QDateTime current_time = QDateTime::currentDateTime();
        QString time_str = current_time.toString("yyyy-MM-dd hh::mm::ss");
        ui->sendwindow->append(time_str+" :未选择文件");
    }
}

//删除断开连接的客户端信息
void Widget::disconnect_slot()
{
    int num = client_connections.length();
    for(int i = 0;i<num;i++)
    {
        //找到断开连接的客户端信息
        if(client_connections[i]->state() == QAbstractSocket::UnconnectedState)
        {
            QString ipport = tr("%1:%2").arg(client_connections[i]->peerAddress().toString().split("::ffff:")[1])\
                    .arg(client_connections[i]->peerPort());
            ui->connection->removeItem(ui->connection->findText(ipport));
            client_connections.removeAt(i);
        }
    }
    if(client_connections.length()==0)//如果没有客户端连接了
    {
        ui->connection->setEditable(true);
        ui->connection->setCurrentText("无连接");
    }
}

void Widget::on_clean_recv_clicked()
{
    ui->recvwindow->clear();
}

void Widget::on_clean_send_clicked()
{
    ui->sendwindow->clear();
}



三、不足
一是想加边框划分一下区域,没有加上。二是窗口拉伸时部件不能一起适应变化。三是对中文数据的传输显示还没有处理好。不过,加油,共勉吧。

四、参考文章
https://blog.csdn.net/m0_73443478/article/details/128003603

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月半清风.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值