欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)

程序员文章站 2023-08-12 17:23:13
在上章 48.QT-网络通信讲解1,我们学习了网络通信基础后,本章便来实战一篇.源码正在整理中,等下贴地址. PS:支持客户端和服务器,提供源码,并且服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据. 1.效果图所下所示: 如下图所示,当服务器状态下,如果有 ......

在上章 48.qt-网络通信讲解1,我们学习了网络通信基础后,本章便来实战一篇.源码正在整理中,等下贴地址.

ps:支持客户端和服务器,提供源码,并且服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.

1.效果图所下所示:

 49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)

如下图所示,当服务器状态下,如果有客户端连入,会提示客户端信息:

 49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)

2.效果操作

客户端操作:

 49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)

服务器操作:

49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)

从上面操作可以看出,服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.

3.首先创建ui

 49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)

4.注意事项

不管是服务器还是客户端,都可以通过peeraddress()和peerport()来获取目标地址和目标端口

4.1服务器监听时

比如服务器,则可以通过qtcpsocket的peeraddress()则可以获取连入的客户端地址

也可以通过children()来获取所有连入的客户端(需要注意的是也会获取到服务器本身的tcp地址和端口),示例如下:

qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>();
foreach (qtcpsocket *tcp, m_tcps)
{
        qdebug() << "address:" << tcp->peeraddress ();
        qdebug() << "port:" << tcp->peerport  ();
}

如果我们只向连入的客户端某个端口发送数据时,就可以通过上面的方式筛选出来.

这样做的话如果觉得很麻烦,也可以将之前连接上的客户端存到qlist里再进行筛选.

4.2 qtcpsocket步骤

  • 首先通过connecttohost()来连接服务器.
  • 然后调用waitforconnected()来判断是否连接服务器超时
  • 当我们接收到服务器数据的时候,则会发出readyread()信号,然后再进行read ()读取发来的数据
  • 发送数据时,则调用write()函数进行发送,当byteswritten()信号函数触发时,便可以获取成功发送的数据长度.

注意:如果read到的数据长度量不是自己想要的,此时我们便可以通过bytesavailable()来读取接收到的数据长度量.当达到多少时,再进行read ()读取.

 

4.3 qtcpserver步骤

  • 首先通过listen(qhostaddress::anyipv4, port)来监听所有来自ipv4的客户端
  • 当有新的客户端连接服务器的时候,会自动触发newconnection()信号函数,然后我们可以通过通过qtcpsocket * nextpendingconnection()成员函数来获取当前连接上的新的客户端类.然后再对qtcpsocket来进行信号槽绑定
  • 当客户端发来数据的时候,则可以通过我们定义的onserverdataready()来读取数据
  • 当我们向某个连接的客户端发送数据时,则通过m_server.findchildren()来筛选出来,然后write即可.

 

5.代码介绍

5.1  头文件介绍

#ifndef widget_h
#define widget_h

#include <qwidget>
#include <qtcpsocket>
#include <qtcpserver>
#include <qmessagebox>

namespace ui {
class widget;
}

class widget : public qwidget
{
    q_object

    qtcpsocket m_client;
    qtcpserver m_server;
    qstring targetaddr;
    int targetport;
public:
    explicit widget(qwidget *parent = 0);
    ~widget();
private slots:
    void on_btn_switch_clicked();
    void on_tcpmode_currentindexchanged(int index);
    void on_btn_send_clicked();             //发送按钮
    void on_betn_clear_clicked();           //清空按钮

    //客户端槽函数
    void onclientconnected();
    void onclientdisconnected();
    void onclientdataready();
    void onclientbyteswritten(qint64 bytes);
    void onclienterr(qabstractsocket::socketerror socketerror);

    //服务器槽函数
    void onservernewconnection();
    void onserverconnected();
    void onserverdisconnected();
    void onserverdataready();
    void onserverbyteswritten(qint64 bytes);
private:
    void startconnect(bool ison);

    void initclientsignals();   //初始化客户端信号槽
    bool startclient();         //启动客户端

    void initserversignals();   //初始化客户端信号槽
    bool startserver();     //启动服务器

    ui::widget *ui;
};

#endif // widget_h

5.2  widget.cpp介绍

该cpp主要是用来处理界面操作的函数

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

widget::widget(qwidget *parent) :
    qwidget(parent),
    ui(new ui::widget)
{
    ui->setupui(this);
    startconnect(false);
    on_tcpmode_currentindexchanged(0);
    initclientsignals();        //初始化客户端信号槽
    initserversignals();        //初始化服务器信号槽

    //限制只能数字输入
    qregexp regx("[0-9]+$");
    qvalidator *validator = new qregexpvalidator(regx, this );
    ui->ipaddr1->setvalidator( validator );
    ui->ipaddr2->setvalidator( validator );
    ui->ipaddr3->setvalidator( validator );
    ui->ipaddr4->setvalidator( validator );
    ui->ipport->setvalidator( validator );
}
void widget::on_tcpmode_currentindexchanged(int index)
{
    if(index==0)        //clent
    {
        ui->ipportlabel->settext("服务器端口号:");
        ui->ipaddlabel->show();
        ui->ipadds->show();
        ui->targetlabel->hide();
        ui->targetobject->hide();
    }
    else
    {
        ui->ipaddlabel->hide();
        ui->ipadds->hide();
        ui->ipportlabel->settext("本地端口号:");
    }
}

void widget::on_btn_switch_clicked()        //切换连接开关
{
    bool ret;
    if(ui->btn_switch->text()=="打开连接")
    {
        if(ui->tcpmode->currentindex()==0)  //启动客户端
           ret=startclient() ;
        else
            ret=startserver();
        if(ret)
            startconnect(true);
    }
    else
    {
        if(ui->tcpmode->currentindex()==0)  //启动客户端
          m_client.close();
        else
        {
            if( m_server.islistening() )
            {
                qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>();
                foreach (qtcpsocket *tcp, m_tcps)
                {
                    tcp->close();
                }
                m_server.close();
            }
        }
        startconnect(false);
    }
}


void widget::startconnect(bool ison)
{
    if(!ison)
    {
        ui->btn_switch->setstylesheet("color:blue;border: 1px solid blue");
        ui->btn_switch->settext("打开连接");

        //使能
        ui->ipaddr1->setenabled(true);
        ui->ipaddr2->setenabled(true);
        ui->ipaddr3->setenabled(true);
        ui->ipaddr4->setenabled(true);
        ui->tcpmode->setenabled(true);
        ui->ipport->setenabled(true);
        ui->localport->settext("");
    }
    else
    {
        ui->btn_switch->setstylesheet("color:red;border: 1px solid red");
        ui->btn_switch->settext("关闭连接");
        //失能
        ui->ipaddr1->setenabled(false);
        ui->ipaddr2->setenabled(false);
        ui->ipaddr3->setenabled(false);
        ui->ipaddr4->setenabled(false);
        ui->tcpmode->setenabled(false);
        ui->ipport->setenabled(false);
        targetaddr="";
        targetport=0;
        ui->sendlenlabel->settext("0");
    }
}

void widget::on_betn_clear_clicked()
{
   ui->recvedit->clear();
   targetaddr="";
   targetport=0;
}

void widget::on_btn_send_clicked()
{
    if(ui->btn_switch->text()!="打开连接")
    {
        if(ui->tcpmode->currentindex()==0)  //客户端
        {
            m_client.write(ui->sendedit->toplaintext().tolocal8bit(),ui->sendedit->toplaintext().tolocal8bit().length());
        }
        else
        {
            if(ui->targetobject->currenttext()!="所有对象")
            {
                qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>();
                foreach (qtcpsocket *tcp, m_tcps)
                {
                    if(ui->targetobject->currenttext() == tcp->objectname())
                    {
                        tcp->write(ui->sendedit->toplaintext().tolocal8bit(),ui->sendedit->toplaintext().tolocal8bit().length());
                        break;
                    }
                }
            }
            else            //所有连接上的客户端都发送一遍
            {
                qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>();
                foreach (qtcpsocket *tcp, m_tcps)
                {
                    tcp->write(ui->sendedit->toplaintext().tolocal8bit(),ui->sendedit->toplaintext().tolocal8bit().length());
                }
            }
        }
    }
}

widget::~widget()
{
    qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>();
    foreach (qtcpsocket *tcp, m_tcps)
    {
        tcp->close();
    }
    if(m_client.isopen())
    {
        m_client.close();
         qdebug()<<"m_client close";
    }
    delete ui;
}

5.3 clenthandler.cpp介绍

该cpp主要用来处理客户端操作相关的文件.

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

void widget::initclientsignals()  //初始化客户端信号槽
{
    connect(&m_client, signal(connected()), this, slot(onclientconnected()));
    connect(&m_client, signal(disconnected()), this, slot(onclientdisconnected()));
    connect(&m_client, signal(readyread()), this, slot(onclientdataready()));
    connect(&m_client, signal(byteswritten(qint64)), this, slot(onclientbyteswritten(qint64)));
    connect(&m_client, signal(error(qabstractsocket::socketerror )), this, slot(onclienterr(qabstractsocket::socketerror)));
}
bool widget::startclient()         //启动客户端
{
    qstring ip = qstring("%1.%2.%3.%4").arg(ui->ipaddr1->text()).arg(ui->ipaddr2->text()).arg(ui->ipaddr3->text()).arg(ui->ipaddr4->text());
    qdebug()<<ip;
    m_client.connecttohost(ip, ui->ipport->text().toint());
    if(m_client.waitforconnected(800))
    {
        return true;
    }
    else
    {
        qmessagebox::information(this,"提示",qstring("连接超时"),qmessagebox::ok);
        return false;
    }
}

void widget::onclientconnected()
{
    startconnect(true);
    qmessagebox::information(this,"提示","连接成功",qmessagebox::ok);
    ui->localport->settext(qstring("%1").arg(m_client.localport())); //显示本地端口号
}
void widget::onclientdisconnected()
{
    startconnect(false);
    qmessagebox::information(this,"提示","断开完成",qmessagebox::ok);
}
void widget::onclientdataready()
{
    if(m_client.peeraddress().tostring()!=targetaddr || m_client.peerport()!=targetport  )
    {
        targetaddr = m_client.peeraddress().tostring();
        targetport = m_client.peerport();
        ui->recvedit->insertplaintext("[接受来自"+ targetaddr+":"+qstring("%1").arg(targetport)+"]:\r\n");
    }
    ui->recvedit->movecursor(qtextcursor::end);
    ui->recvedit->insertplaintext(qstring::fromlocal8bit(m_client.readall())+"\r\n");
}
void widget::onclientbyteswritten(qint64 bytes)
{
    qdebug() << "onbyteswritten:" << bytes;
    ui->sendlenlabel->settext(qstring("%1").arg(ui->sendlenlabel->text().toint()+bytes));
}

void  widget::onclienterr(qabstractsocket::socketerror socketerror)
{
    qdebug()<<"onclienterr:"<<socketerror;
    m_client.close();
    startconnect(false);
    qmessagebox::information(this,"提示",qstring("连接失败:%1").arg((int)socketerror),qmessagebox::ok);
}

5.4 serverhandler.cpp介绍

该cpp主要用来处理服务器操作相关的文件

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

void widget::initserversignals()   //初始化信号槽
{
    connect(&m_server, signal(newconnection()), this, slot(onservernewconnection()));
}
bool widget::startserver()         //启动服务器
{
    if(m_server.listen(qhostaddress::anyipv4,ui->ipport->text().toint()))       //只监听ipv4的所有客户端
    {
        ui->targetlabel->show();
        ui->targetobject->show();
        ui->localport->settext(qstring("%1").arg(m_server.serverport()));
        return true;
    }
    else
       return false;
}
void widget::onservernewconnection()
{
    qdebug() << "onnewconnection";
    qtcpsocket* tcp = m_server.nextpendingconnection();     //获取新的客户端信息
    qstring info=tcp->peeraddress().tostring()+":"+qstring("%1").arg(tcp->peerport());
    ui->targetobject->additem(info);
    qmessagebox::information(this,"提示",qstring("新的客户端连入:%1").arg(info),qmessagebox::ok);
    tcp->setobjectname(info);       //设置名称,方便查找
    connect(tcp, signal(connected()), this, slot(onserverconnected()));
    connect(tcp, signal(disconnected()), this, slot(onserverdisconnected()));
    connect(tcp, signal(readyread()), this, slot(onserverdataready()));
    connect(tcp, signal(byteswritten(qint64)), this, slot(onserverbyteswritten(qint64)));
}
void widget::onserverconnected()
{

}

void widget::onserverdisconnected()
{
    qtcpsocket* tcp = dynamic_cast<qtcpsocket*>(sender());
    if( tcp != null )       //从连接对象中移除掉
    {
        qdebug() << "onserverdisconnected";
        qdebug() << "local address:" << tcp->peeraddress();
        qdebug() << "local port:" << tcp->peerport();
        qstring info=tcp->peeraddress().tostring()+":"+qstring("%1").arg(tcp->peerport());
        qmessagebox::information(this,"提示",qstring("客户端断开连接:%1").arg(info),qmessagebox::ok);

        int index = ui-> targetobject ->findtext(info);
        if(index>=0)
        ui->targetobject->removeitem(index);
    }
}
void widget::onserverdataready()
{
    qtcpsocket* tcp = dynamic_cast<qtcpsocket*>(sender());
    if(tcp->peeraddress().tostring()!=targetaddr || tcp->peerport()!=targetport  )
    {
        targetaddr = tcp->peeraddress().tostring();
        targetport = tcp->peerport();
        ui->recvedit->insertplaintext("[接受来自"+ targetaddr+":"+qstring("%1").arg(targetport)+"]:\r\n");
    }
    ui->recvedit->movecursor(qtextcursor::end);
    ui->recvedit->insertplaintext(qstring::fromlocal8bit(tcp->readall())+"\r\n");
}
void widget::onserverbyteswritten(qint64 bytes)
{
    qdebug() << "onbyteswritten:" << bytes;
    ui->sendlenlabel->settext(qstring("%1").arg(ui->sendlenlabel->text().toint()+bytes));
}