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

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

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

1.效果图所下所示:

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

2.效果操作

客户端操作:

服务器操作:

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

3.首先创建UI

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

原文地址:https://www.cnblogs.com/lifexy/p/11317662.html

时间: 2024-12-11 04:34:21

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

unix下网络编程之I/O复用(三)

poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息. poll函数原型: 1 2 3 #include<poll.h>    int poll (struct pollfd * fdarray , unsigned long nfds , int timeout);    //返回:就需描述字的个数,0--超时,-1--出错

linux网络编程之shutdown() 与 close()函数详解

linux网络编程之shutdown() 与 close()函数详解 参考TCPIP网络编程和UNP: shutdown函数不能关闭套接字,只能关闭输入和输出流,然后发送EOF,假设套接字为A,那么这个函数会关闭所有和A相关的套接字,包括复制的:而close能直接关闭套接字. 1.close()函数 [cpp] view plain copy print? <span style="font-size:13px;">#include<unistd.h> int 

QT核心编程之Qt线程 (c)

QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类.线程安全传递事件的方式和全局Qt库互斥量允许你从不同的线程调用Qt的方法.Qt中与线程应用相关的类如表6所示. 表6 Qt中与线程相关的类 使用线程需要Qt提供相应的线程库的支持,因此,在编译安装Qt时,需要加上线程支持选项. 当在Windows操作系统上编译Qt时,线程支持是在一些编译器上的一个

java网络编程之UDP实例

package Socket; import java.net.DatagramPacket; import java.net.InetAddress; public class Dgram { public static DatagramPacket toDatagram(String s, InetAddress destIA, int destPort) { byte[] buf = new byte[s.length() + 1]; s.getBytes(0, s.length(), b

java网络编程之TCP实例

Dgram类 package Socket; import java.net.DatagramPacket; import java.net.InetAddress; public class Dgram { public static DatagramPacket toDatagram(String s, InetAddress destIA, int destPort) { byte[] buf = new byte[s.length() + 1]; s.getBytes(0, s.leng

扯谈网络编程之Tcp SYN flood洪水攻击

简介 TCP协议要经过三次握手才能建立连接: (from wiki) 于是出现了对于握手过程进行的攻击.攻击者发送大量的FIN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries).这样的话,对于服务器的内存,带宽都有很大的消耗.攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难. 攻与防 攻击者角度 从攻击者的角度来看,有两个地方可以

[深入浅出WP8.1(Runtime)]网络编程之HttpClient类

12.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,我们还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类提供了一个简单的接口来处理最常见的任务,并为身份验证提供了适用于大多数方案的合理的默认设置.对于较为复杂的 HTTP 操作,更多的功能包括:执行常见操作(DELETE.GET.PUT 和 POST)的方法:获取.设置和删除 Cookie 的功能:支持常见的身份验证设置和模式:异步方法上提供的 HTT

黑马程序员——Java网络编程之UDP传输

网络编程 网络模型 通讯要素:InetAddress(对象):ip地址,网络中设备的标识,不可记忆,可用主机名,本地回环地址:127.0.0.1主机名localhost 端口号 传输协议:UDP,将数据的源及目的封装成数据包中,不需要建立连接,每个数据包的大小限制在64K内,无连接,是不可靠协议,不需要建立连接,速度快.力求速度,不求数据的准确性.比如聊天软件,网络会议. TCP:建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接效率稍低. S

linux网络编程之TCP/IP基础篇(一)

从今天起,将会接触到网络编程,平台是linux,实现语言C语言,最后将会实现一个简易的miniftp服务器. 主要的内容安排为:linux网络编程之TCP/IP基础篇,SOCKET编程篇,进程间通信篇,线程篇,实战ftp篇. 1.ISO/OSI参考模型:open system interconnection开放系统互联模型是由OSI(international organization for standardization )国际标准化组织定义的网络分层模型,共七层. 各层的具体含义: 物理层