精华内容
下载资源
问答
  • DPI系统可以在应用业务控制方面做到:业务感知:通过状态深度包检测,可以识别不同的应用程序。实时控制:在应用会话层控制连接带宽、阻塞、重定向连接。用户智能:通过用户状态管理,每用户带宽管理、定额管理、OSS...

    DPI系统可以在应用业务控制方面做到:

    • 业务感知:通过状态深度包检测,可以识别不同的应用程序。
    • 实时控制:在应用会话层控制连接带宽、阻塞、重定向连接。
    • 用户智能:通过用户状态管理,每用户带宽管理、定额管理、OSS集成系统(例如同DHCPRADIUS系统集成)
    • 对业务发展的支持:通过后续编程支持新的应用和商业需求,及时应对新的安全威胁和业务发展。对于将来NGN环境不断变化的业务需求,这一点是非常重要的。

     

    DPI系统在业务控制时的执行动作:

    DPI的第一步是审视数据包包头和数据负荷,分辨出应用程序的类型,可以进一步分辨出应用程序的意思。一般来讲,只需要分辨出某个数据流是Web traffic or HTTP traffic,并且分辨出访问网站,获取传送的内容的某些信息。同样的可以对VoIP和视频业务进行审视,在分辨出应用后,需要分辨应用的某些含义。

    一旦DPI系统识别出应用程序、并且将其同用户相对应起来后,相关的策略可以被部署。(如下图,这就是IP网络中业务控制层的作用)

     

     

     

    DPI的功能:

    DPI系统有6个关键的功能

    • 使用分析:包括容量管理、使用者地理分布、用户宽带体验监测
    • 流量优化:包括P2P流量优化、提升尽力传送业务
    • 安全业务:例如外向攻击阻止、网络滥用、网络欺骗检测
    • 业务分级和访问控制:例如SOHO业务、主动推送portal页面
    • 基于内容收费:定额业务、基于站点的收费
    • 高级业务支持:例如多媒体流量标记和OTT语音和视频流量管理。

    下图示意了DPI的用户使用分析功能和提高网络控制程度的流量优化。

    右上图表示在一个POPP2P业务消耗的带宽。这是个相当极端的例子,大概网络带宽的95%P2P流量使用。但是CISCOGadekar讲,目前全球范围内宽带网络上,P2P流量是耗费最大带宽的业务,大概消耗了60%~95%的汇聚层面的上联带宽。

    右下图说明很小部分用户(约1%)消耗了网络的大部分资源(超过60%的带宽),同时,左下图揭示并不仅是这1%的用户使用P2P、在任何时间都会有大概60~70%的活跃用户在使用P2P应用。

    左上图说明在不同网络的对等连接点(peering points,如不同运营商的互联链路、同一运营商城域网出口、省网出口等等)P2P流量的分布。

     

    展开全文
  • DPI与DFI技术分析

    2018-03-08 17:45:00
    DPI技术在分析包头的基础上,增加了对应用层的分析,是一种基于应用层的流量检测和控制技术,当IP数据包、TCP或UDP数据流经过基于DPI技术的带宽管理系统时,该系统通过深入读取IP包载荷的内容来对OSI7层协议中的应用...

        DPI全称为“Deep Packet Inspection”,称为“深度包检测”。DPI技术在分析包头的基础上,增加了对应用层的分析,是一种基于应用层的流量检测和控制技术,当IP数据包、TCP或UDP数据流经过基于DPI技术的带宽管理系统时,该系统通过深入读取IP包载荷的内容来对OSI7层协议中的应用层信息进行重组,从而得到整个应用程序的内容,然后按照系统定义的管理策略对流量进行整形操作。针对不同的协议类型,DPI识别技术可划分为以下三类: 
    第一类是特征字的识别技术:不同的应用通常会采用不同的协议,而各种协议都有其特殊的指纹,这些指纹可能是特定的端口、特定的字符串或者特定的Bit序列。基于特征字的识别技术,正是通过识别数据报文中的指纹信息来确定业务所承载的应用。根据具体检测方式的不同,基于特征字的识别技术又可细分为固定特征位置匹配、变动特征位置匹配和状态特征字匹配三种分支技术。通过对指纹信息的升级,基于特征字的识别技术可以方便的扩展到对新协议的检测。 
        第二类是应用层网关识别技术:在业务中,有一类的控制流和业务流是分离的,如与7号信令相关的业务,其业务流没有任何特征,应用层网管识别技术针对的对象就是此类业务,首先由应用层网管识别出控制流,并根据控制流协议选择特定的应用层网关对业务流进行解析,从而识别出相应的业务流。对于每一个协议,需要不同的应用层网关对其进行分析。例如:H323、SIP等协议,就属于此类,其通过信令交互过程,协商得到其数据通道,一般是RTP格式封装的语音流,纯粹检测RTP流并不能确定这条RTP流是通过那种协议建立起来的,即判断其是何种业务,只有通过检测SIP或H232的协议交互,才能得到其完整的分析。 
    第三类是行为模式识别技术:在实施行为模式技术之前,运营商首先必须先对终端的各种行为进行研究,并在此基础上建立行为识别模型,基于行为识别模型,行为模式识别技术即根据客户已经实施的行为,判断客户正在进行的动作或者即将实施的动作。
        行为模式识别技术通常用于那些无法由协议本身就能判别的业务,例如:从电子邮件的内容看,垃圾邮件和普通邮件的业务流两者间根本没有区别,只有进一步分析,具体根据发送邮件的大小、频率,目的邮件和源邮件地址、变化的频率和被拒绝的频率等综合分析,建立综合识别模型,才能判断是否为垃圾邮件。 
    这三类识别技术分别适用于不同类型的协议,相互之间无法替代,只有综合的运用这三大技术,才能有效的灵活的识别网络上的各类应用,从而实现控制和计费。

        DFI(Deep/Dynamic Flow Inspection,深度/动态流检测)与DPI进行应用层的载荷匹配不同,采用的是一种基于流量行为的应用识别技术,即不同的应用类型体现在会话连接或数据流上的状态各有不同。 
    例如,网上IP语音流量体现在流状态上的特征就非常明显:RTP流的包长相对固定,一般在130~220byte,连接速率较低,为20~84kbit/s,同时会话持续时间也相对较长;而基于P2P下载应用的流量模型的特点为平均包长都在450byte以上、下载时间长、连接速率高、首选传输层协议为TCP等。DFI技术正是基于这一系列流量的行为特征,建立流量特征模型,通过分析会话连接流的包长、连接速率、传输字节量、包与包之间的间隔等信息来与流量模型对比,从而实现鉴别应用类型。


    DPI与DFI优缺点分析 
        
        DFI处理速度相对快:采用DPI技术由于要逐包进行拆包操作,并与后台数据库进行匹配对比;采用DFI技术进行流量分析仅需将流量特征与后台流量模型比较即可,因此,目前多数基于DPI的带宽管理系统的处理能力达到线速1Gbit/s左右,而基于DFI的系统则可以达到线速10Gbit/s的流量监控能力,完全可以满足运营商需求; 
    DFI维护成本相对较低:基于DPI技术的带宽管理系统,总是滞后新应用,需要紧跟新协议和新型应用的产生而不断升级后台应用数据库,否则就不能有效识别、管理新技术下的带宽,提高模式匹配效率;而基于DFI技术的系统在管理维护上的工作量要少于DPI系统,因为同一类型的新应用与旧应用的流量特征不会出现大的变化,因此不需要频繁升级流量行为模型。 
        识别准确率方面各有千秋:由于DPI采用逐包分析、模式匹配技术,因此,可以对流量中的具体应用类型和协议做到比较准确的识别;而DFI仅对流量行为分析,因此只能对应用类型进行笼统分类,如对满足P2P流量模型的应用统一识别为P2P流量,对符合网络语音流量模型的类型统一归类为VOIP流量,但是无法判断该流量是否采用H.323或其他协议。如果数据包是经过加密传输的,则采用DPI方式的流控技术则不能识别其具体应用,而DFI方式的流控技术则不受影响,因为应用流的状态行为特征不会因加密而根本改变。

    转载于:https://www.cnblogs.com/staffyoung/p/8529863.html

    展开全文
  • 论基于DPI技术的用户行为分析系统,郑桂凤,廖青,随着互联网应用的扩展, 爆炸式的信息数据占用了有限的带宽,用户的体验也受到了负面的影响。为了提高网络服务的质量,DPI(Deep Packe
  • 第1章 绪论 第2章 指令系统 第3章 可执行文件 第4章 反汇编技术 第5章 指令的语义抽象 第6章 基本数据类型分析 第7章 高级控制流恢复 第8章 过程恢复技术 第9章 部分编译优化效果的消除 第10章 程序的调试与测试
  • 基于DPI的P2P点播视频文件格式的分析与研究,张蓉蓉,徐洁,随着P2P点播通信网络规模不断扩大,其视频流的有效地识别可以提高点播的质量。本文分析了主流P2P点播软件所采用的视频文件格式RMFF��
  • 首先通过对移动分组网络数据进行解码预处理,合成基于传输流的xDR,然后结合DPI技术和DFI技术的互补优势,实现移动分组网络的业务识别和统计。经过现网数据测试,本方案能够很好地对移动分组网进行业务xDR合成和流量...
  • 基于DPI的P2P流量监控系统的分析与设计,蒋文龙,辛阳,近年来P2P网络应用技术的迅猛发展,给用户带来了丰富快捷的网络共享资源和信息服务。然而P2P技术给网络用户带来方便的同时,引发了
  • android应用 DPI不同的适配问题分析

    千次阅读 2016-05-26 21:05:53
    文件夹,其实这样做完全没有必要,通过第一段的分析我们得知, android:anyDensity = "false" , 则应用会将大小密度转变成中密度,从而去加载 mdpi 中的资源。这里同样,当 android:anyDensity = "false" , 则应用...
      如何将一个应用程序适配在不同的手机上,虽然这不算是一个技术问题,但是对于刚刚做屏幕的开发人员来说,还真不是一件多么简单的事情。

    首先:你需要在
    AndroidManifest.xml文件的<manifest>元素如下添加子元素

    <supports-screens android:largeScreens="true"

          android:normalScreens="true"android:anyDensity="true"

          android:smallScreens="true"></supports-screens>

        名如其意,以上是为我们的屏幕设置多分辨率支持(更准确的说是适配大、中、小三种密度)。android:anyDensity="true" 这一句对整个的屏幕都起着十分重要的作用,值为true,我们的应用程序当安装在不同密度的手机上时,程序会分别加载hdpi,mdpi,ldpi文件夹中的资源。

    相反,如果值设置为false,即使我们在hdpi,mdpi,ldpi文件夹下拥有同一种资源,那么应用也不会自动地去相应文件夹下寻找资源,这种情况都是出现在高密度,以及低密度的手机上,比如说一部240×320像素的手机,如果设置android:anyDensity="false"Android系统会将240 x 320(低密度)转换为320×480(中密度),这样的话,应用就会在小密度手机上加载mdpi文件中的资源。

     

    2.细心的人会发现自android2.0开始之后drawable文件被三个文件夹drawable-hdpi,drawable-mdpi,drawable-ldpi三个文件夹所取代,有些编程人员为了让应用程序默认地加载某些图片,他们会特意地去在android2.0之后的应用程序中重新创建drawable文件夹,其实这样做完全没有必要,通过第一段的分析我们得知,android:anyDensity="false"则应用会将大小密度转变成中密度,从而去加载mdpi中的资源。这里同样,当android:anyDensity="false"则应用会去加载mdpi中的资源。

    总结一下:

    第一:android:anyDensity="true"系统会依据屏幕密度,自动去找对应的文件夹

    第二:android:anyDensity="false",

    (1)             如果drawable-hdpi,drawable-mdpi,drawable-ldpi三个文件夹中有同一张图片资源的不同密度表示,那么系统会去加载drawable_mdpi文件夹中的资源

    (2)             如果drawable-hpdi中有高密度图片,其它两个文件夹中没有对应图片资源,那么系统会去加载drawable-hdpi中的资源。

    (3)             如果drawable-hdpi,drawable-mdpi中有图片资源,drawable-ldpi中没有对应的图片资源,那么系统会加载drawable-mdpi文件夹中的资源

     

    3. 注意上图各种文件夹的不同表示。

    drawable-hdpi 该图片即适用于横屏,也适用于竖屏

    drawable-land-hdpi,当屏幕为横屏,且为高密度时,加载此文件夹中的资源

    drawable-port-hdpi,当屏幕为竖屏,且为高密度时,加载此文件夹中的资源


    3. 有时候会根据需要在代码中动态地设置某个值,比如地图,地图的pin和地图的地址提示框的相对偏移量在不同密度的手机上是不同的。这时候可以通过以下方法求出屏幕密度:

    DisplayMetrics metric = newDisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metric);

    int densityDpi =metric.densityDpi;  //屏幕密度DPI120 / 160 / 240

    然后可以在代码中为这几种密度分别设置便宜量

    但是这种方法最好不要使用,最好的方式是在xml文件中不同密度的手机进行分别设置。
    这里地图的偏移量可以在values-hpdi,values-mdpi,values-ldpi三种文件夹中的dimens.xml文件进行设置
    值得一提的是:

       <dimen name="bitmap_common_topoffset">40dp</dimen>

       <dimen name="bitmap_common_bottomoffset">-14dp</dimen>
    这里的负数是完全起作用的,系统会认为它是一个负值


    4.各大手机厂商对于Android操作系统都有或多或少的改动,当然这些改动会对我们应用程序产生某些影响
      比如:
      (1)系统源代码中连接music服务的aidl文件所在包名:com.android.music
      (2)LG则可能将该aidl文件修改所在的包(例如修改为com.android.music.player),并且修改其中的文件内容(增加一个方法,或者减少几个方法,或者修改方法名称)那么我们的应用要想在LG的手机上发布,那么我们就必须改变所要连接的aidl文件,必须跟LG厂商修改的完全一致。
    展开全文
  • 原文链接:Qt之高DPI显示器(二) - 自适配解决方案分析 最近一直在处理高DPI问题,也花费了不少功夫,前前后后使用了多种解决方案,各种方案也都有利弊,笔者最终采用了自适配方案,虽然复杂一些,但是结果可控。这里...


    原文链接:Qt之高DPI显示器(二) - 自适配解决方案分析

    最近一直在处理高DPI问题,也花费了不少功夫,前前后后使用了多种解决方案,各种方案也都有利弊,笔者最终采用了自适配方案,虽然复杂一些,但是结果可控。这里把处理的过程记录下来,留给有同样需求的同学

    一、回顾

    上一篇文章Qt之高DPI显示器(一) - 解决方案整理讲述了笔者处理高DPI显示的一系列分析过程,为了更好的阅读和排版,其中有一些实验方案没有具体写出,即使写出来也没有多大用处,而且会影响大家阅读。

    本篇文章将会接着上一篇文章的最后一小节-自适配高DPI进行讲解,由于内容比较多,而且整个解决方案代码量也会相当大,因此文章中也只会涉及到整个DPI适配架构的核心和一些关键代码,有疑问欢迎提问

    上一篇文章提到了T窗口,那么什么是T窗口呢!下面我们来具体分析。

    这里笔者贴一个适配完成以后的TWidget类,大家可以先分析分析,也可以猜猜看,每一处代码的具体含义。所有代码细节笔者后边会具体分析每一处细节

    //函数声明
    //xxx.h
    #define CreateTWidget() CreateObject(Widget)
    class TWidget : public QWidget, public ICallDPIChanged
    {
    	Q_OBJECT
    
    public:
    	TWidget(float scale, QWidget * parent = nullptr);
    	TWidget(QWidget * parent = nullptr);//不建议使用
    	TWidget(QWidget * parent, Qt::WindowFlags f);//不建议使用
    	~TWidget();
    
    public:
    	//重写大小变化相关函数
    	DECLARE_RESIZE();
    	void setLayout(QLayout * layout);
    
    public:
    	//QWidget
    	virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result)override;
    
    	//ICallDPIChanged
    	DECLARE_DPI();
    
    	//TWidget
    	virtual void AdjustReiszeHandle();
    
    	DECLARE_DPI_SYMBOL;
    
    protected:
    	WidgetResizeHandler resize_handler;// 用于支持放大缩小 拖拽 等功能
    
    private:
    	TigerUILib::ReiszeActions m_sizeActions;
    	QSize m_size;
    	QSize m_minimumSize;
    	QSize m_maximumSize;
    	ICallDPIChanged * m_pLayout = nullptr;//DPI发生变化时 通知布局
    };
    
    //函数实现
    //xxx.cpp
    TWidget::TWidget(float scale, QWidget * parent)
    	: QWidget(parent)
    	, dpi_scale(scale)
    {
    }
    
    TWidget::TWidget(QWidget * parent /*= nullptr*/)
    	: QWidget(parent)
    {
    	dpi_scale = WINDOW_SCALE;
    }
    
    TWidget::TWidget(QWidget *parent, Qt::WindowFlags f)
    	: QWidget(parent, f)
    {
    	dpi_scale = WINDOW_SCALE;
    }
    
    TWidget::~TWidget()
    {
    	DPIHelper()->RemoveDPIRecord(WINDOW_WINID);
    }
    
    void TWidget::setLayout(QLayout * layout)
    {
    	WIDGET_RELEATE_LAYOUTS(layout);
    
    	__super::setLayout(layout);
    }
    
    DEFINE_RESIZE(Widget);
    DEFINE_DPI(Widget);
    
    bool TWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
    {
    	MSG* pMsg = reinterpret_cast<MSG*>(message);
    
    	switch (pMsg->message)
    	{
    	case WM_DPICHANGED:
    	{
    		DWORD dpi = LOWORD(pMsg->wParam);
    		WId id = WINDOW_WINID;
    		if (DPIHelper()->DPIChanged(dpi, id))
    		{
    			ScaleChanged(DPIHelper()->GetDPIScale(id));
    			RefrushSheet(this, id);
    		}
    	}
    	}
    
    	return __super::nativeEvent(eventType, message, result);
    }
    
    void TWidget::ScaleChanged(float scale)
    {
    	DEFINTE_SCALE_RESIZE(Widget);
    	if (m_pLayout)
    	{
    		m_pLayout->ScaleChanged(scale);
    	}
    	AdjustReiszeHandle();
    }
    
    void TWidget::AdjustReiszeHandle()
    {
    	if (resize_handler.isWidgetMoving())
    	{
    		resize_handler.dpiChanged(WINDOW_SCALE);
    	}
    }
    

    二、框架说明

    用一段话描述一下DPI适配方案?

    答:笔者提到的DPI适配方案其实原理很简单,没有想象中那么复杂,方案也是中规中矩,其中遵守以下这么几条大的原则

    1. 首先就是window窗体自己去监测自身所在屏幕DPI发生变化,发生变化时通知布局进行缩放
    2. 局部缩放后,然后对自身所包含的widget窗体和布局进行缩放
    3. 不在布局中的窗体需要单独去控制缩放

    是不是说起来很简单,但是要实现这么一个流程还是有一些难度的,首先考虑的就是效率,如果做完效率跟不上那么一切都是瞎扯。

    为了更好的效率,笔者也是做了不需要的优化,优化的内容不在本篇文章中进行讨论,后续会单独分出一篇文章说明

    下面是两个DPI适配框架的核心接口类,分别是DPI发生变化时的回调接口类和DPi管理接口类

    struct ICallDPIChanged
    {
    	virtual void ScaleChanged(float scale) = 0;
    	virtual WId GetWID() const = 0;
    	virtual void SetScale(float scale) = 0;
    };
    
    #define STANDARD_DPI 96.0
    struct IDPIHelper
    {
    	virtual bool DPIChanged(unsigned short, WId) = 0;
    	virtual void RemoveDPIRecord(WId) = 0;//移除指定native窗体的DPI记录 一般用于native窗体析构时
    	virtual float GetDPIScale(WId) const = 0;
    	virtual float GetOldDPIScale(WId) const = 0;
    	virtual QString GetStyleSheet(WId) const = 0;//获取指定DPI下的样式表
    	virtual float GetScaleNumber(float, WId) const = 0;//获取指定DPI下的数值 缩放后数值
    	virtual QList<WId> GetAllWindowID() const = 0;//获取所有自己加载过皮肤的窗口ID
    
    	//优化接口 主要是为了适配用户主机只有一种DPI时使用
    	virtual bool IsOnlyOneDPI() const = 0;//获取用户主机是否只有一种DPI
    	virtual void RefrushDPIRecords() = 0;//显示器数量发生了变化 刷新历史显示器DPI记录
    	virtual void SetDefaultScale(float scale) = 0;//设置缺省DPI值 当显示器dpi只有一种时刷新
    	virtual float GetDefaultScale() const = 0;//获取缺省DPI缩放值 只有当机器上所有的显示器为统一dpi时起作用
    };
    
    IDPIHelper * GetDPIHelper();
    #define DPIHelper() GetDPIHelper()
    

    1、ICallDPIChanged

    dpi变化时回调类,当dpi发生变化时,通过该接口类中的ScaleChanged方法进行处理变动,比如说第一小节中的TWidget类,我们也重写了这个接口,在该接口中我们对窗体进行了大小适配和布局适配

    对象声明中的函数声明使用了宏进行包装因此没有直接显示出来

    void TWidget::ScaleChanged(float scale)
    {
    	DEFINTE_SCALE_RESIZE(Widget);
    	if (m_pLayout)
    	{
    		m_pLayout->ScaleChanged(scale);
    	}
    	AdjustReiszeHandle();//如果窗体正在被拖拽需要适配拖拽的位置
    }
    
    

    2、IDPIHelper

    IDPIHelper是整个DPi适配的核心模块,他负责整个DPI调度的核心功能,包括:DPI改变检测、获取指定window窗体缩放比、获取指定window窗体的qss内容和获取指定数值在不同DPI下的实际数值等。除过以上核心接口以外,笔者为了优化DPI适配效果,还增加了一系列优化接口,主要是针对用户主机只有一种DPI时所作的性能提升。

    由于篇幅原因,这里把一些关键实现节点列出来

    1、dpi变化入口

    如下是dpi发生变化实现接口,函数中干了三件事

    1. 首先监测dpi是否正在发生了变化,如果发生了变化则更新缓存中的window窗体的dpi缩放比
    2. 接着读取window窗体中的qss标识生成新的qss样式字符串
    3. 通知所有悬浮窗体管理器,适配所有悬浮窗体

    悬浮窗体指没有布局的窗体,当悬浮窗体的父窗体dpi发生变化时,相应的悬浮窗体也需要进行适配

    bool CDPIHelper::DPIChanged(unsigned short dpi, WId id)
    {
    #ifndef HIGHDPI_ENABLE
    	return false;
    #endif
    	float scale = dpi / STANDARD_DPI;
    
    	RefrushDPIRecords();
    
    	if (m_pWindowScale.contains(id))
    	{
    		if (m_pWindowScale[id] == scale)
    		{
    			return false;
    		}
    
    		m_pWindowOldScale[id] = m_pWindowScale[id];
    	}
    
    	m_pWindowScale[id] = scale;
    
    	QWidget * window = QWidget::find(id);
    	m_strQssFile = window->property(QSS_FIlE).toString();
    	if (m_strQssFile.isEmpty())
    	{
    		m_strQssFile = DEFAULT_QSS_FILE;
    	}
    	else
    	{
    		if (m_strQssFile.endsWith(DEFAULT_QSS_SUFFIX) == false)
    		{
    			m_strQssFile.append(DEFAULT_QSS_SUFFIX);
    		}
    	}
    
    	RefrushTimesSheet(Skin::TypeDefault, id);
    	RefrushTimesSheet(Skin::TypeLight, id);
    
    	CFloatingWidgetMgr::getInstance()->dpiChanged(id, scale);
    	return true;
    }
    

    2、获取指定DPI下的qss内容

    void CDPIHelper::RefrushTimesSheet(Skin::SKIN_TYPE skin, WId id)
    {
    	float scale = GetDPIScale(id);
    
    	int times = (int)(scale + 0.5001);//几倍图
    
    	//如果基础qss不存在 则需要从硬盘中读取  
    	//读取时按照向上取整进行读取qss文件
    	//如果高分屏qss不存在 则读取一倍qss文件
    	if (m_StyleSheets[skin].size() < times)
    	{
    		m_StyleSheets[skin].resize(times);
    	}
    
    	std::wstring filePath = ImagePath::GetSkinFilePath(skin, m_strQssFile.toStdWString(), times);
    	if (QFile::exists(QString::fromStdWString(filePath)) == false)
    	{
    		filePath = ImagePath::GetSkinFilePath(skin, m_strQssFile.toStdWString());
    	}
    	QFile qss(QString::fromStdWString(filePath));
    	qss.open(QFile::ReadOnly);
    	if (qss.isOpen())
    	{
    		QString btnstylesheet = QObject::tr(qss.readAll());
    		m_StyleSheets[skin][times - 1][SCALE_ENLARGE(m_strQssFile, scale)] = btnstylesheet;
    		qss.close();
    	}
    
    	Q_ASSERT(m_StyleSheets[skin].size() > times - 1);
    	
    	//更新缓存中的换肤文件
    	m_StyleSheetMap[skin][SCALE_ENLARGE(m_strQssFile, scale)] =
    		QtTigerHelper::ScaleSheet(m_StyleSheets[skin][times - 1][SCALE_ENLARGE(m_strQssFile, scale)], scale);
    }
    

    3、悬浮窗体管理器

    大多数的窗体都是在布局中完成的,但是也有一小部分的窗口不在布局中,需要单独去适配,这个时候就需要使用CFloatingWidgetMgr布局管理器。

    /**
    * 简介:悬浮窗口管理器 负责在DPI发生变化时通知悬浮窗口
    		支持如下类型的悬浮窗口:
    		TFrame TPushButton TLabel TTableView TWidget TDialog TMainWindow
    */
    class CFloatingWidgetMgr : public QObject
    {
    	Q_OBJECT
    
    public:
    	static CFloatingWidgetMgr * getInstance();
    
    public:
    	void addWidget(QWidget * widget);
    
    	//dpi helper call
    	void dpiChanged(WId id, float scale);
    
    private:
    	QSet<ICallDPIChanged *> m_pWidgets;
    };
    

    悬浮窗体适配高DPI也很简单,只需要把自己加入到悬浮窗体管理器中即可,是不是也很简单。

    CFloatingWidgetMgr::getInstance()->addWidget(xxx);
    

    三、方案分析

    既然我们要重写Qt控件的非virtual接口,那么这个行为在C++语法上应该叫覆盖,要想调用我们覆盖的函数,使用多态肯定是不行的,聪明的你肯定也想到了,我们在使用界面类时,只能使用T打头的控件类声明对象,这样就会调用我们覆盖后的接口

    上一篇文章大致说过,要自适配高DPI我们需要适配四个项目,分别是窗口大小、字体大小、间距和图标,那么接下来就开始我们的分析过程

    1、窗口大小

    要适配软件窗口大小,我们总共需要重写如下14个和大小相关函数,而且这只是大小相关的函数,也就是QWidget的接口,其他更复杂的接口需要针对具体的类去重写

    void resize(int w, int h);void resize(const QSize &); void setFixedHeight(int w); 
    void setFixedWidth(int w);void setFixedSize(int w, int h);void setFixedSize(const QSize &s);
    void setMinimumSize(const QSize &);void setMinimumSize(int minw, int minh);
    void setMinimumHeight(int minh);void setMinimumWidth(int minw);
    void setMaximumSize(const QSize &);void setMaximumSize(int maxw, int maxh);
    void setMaximumHeight(int minh);void setMaximumWidth(int minw);
    

    Qt的界面类我粗略估计了下,至少有几十个,如果每一个类都需要去适配,那么工作量可想而知,因此笔者想了一个办法,做了一系列宏,像下面代码这样,只需要在我们想要适配的类中添加宏即可

    //函数声明
    #define DECLARE_RESIZE()\
    	void resize(int w, int h);void resize(const QSize &); void setFixedHeight(int w); \
    	void setFixedWidth(int w);void setFixedSize(int w, int h);void setFixedSize(const QSize &s);\
    	void setMinimumSize(const QSize &);void setMinimumSize(int minw, int minh);\
    	void setMinimumHeight(int minh);void setMinimumWidth(int minw);\
    	void setMaximumSize(const QSize &);void setMaximumSize(int maxw, int maxh);\
    	void setMaximumHeight(int minh);void setMaximumWidth(int minw);\
    

    实际使用过程类似第一小节那样,非常简单。

    函数声明有了,接下来就是函数实现,方法类似,笔者还是写了一个宏来适配相关放大函数,代码下下面这样

    //函数实现
    #define DEFINE_RESIZE(name)\
    	void T##name::resize(int w, int h){	m_sizeActions |= TigerUILib::RA_Resize;	float scale = dpi_scale;	m_size = QSize(w, h);;__super::resize(m_size.width() * scale, m_size.height() * scale);}\
    	void T##name::resize(const QSize & size){	m_sizeActions |= TigerUILib::RA_Resize;	float scale = dpi_scale;m_size = size;__super::resize(m_size * scale);}\
    	void T##name::setFixedHeight(int h){m_sizeActions |= TigerUILib::RA_FixedHeight;float scale = dpi_scale;m_size.setHeight(h);__super::setFixedHeight(m_size.height() * scale);}\
    	void T##name::setFixedWidth(int w){m_sizeActions |= TigerUILib::RA_FixedWidth;float scale = dpi_scale;m_size.setWidth(w);__super::setFixedWidth(m_size.width() * scale);}\
    	void T##name::setFixedSize(int w, int h){m_sizeActions |= TigerUILib::RA_FixedSize;float scale = dpi_scale;		m_size = QSize(w, h);	__super::setFixedSize(m_size.width() * scale, m_size.height() * scale);}\
    	void T##name::setFixedSize(const QSize & size){m_sizeActions |= TigerUILib::RA_FixedSize;float scale = dpi_scale;	m_size = size;	__super::setFixedSize(m_size * scale);}\
    	void T##name::setMinimumSize(const QSize & size){m_sizeActions |= TigerUILib::RA_MinimumSize;float scale = dpi_scale;m_minimumSize = size;	__super::setMinimumSize(m_minimumSize * scale);}\
    	void T##name::setMinimumSize(int w, int h){m_sizeActions |= TigerUILib::RA_MinimumSize;float scale = dpi_scale;	m_minimumSize = QSize(w, h);	__super::setMinimumSize(m_minimumSize.width() * scale, m_minimumSize.height() * scale);}\
    	void T##name::setMinimumHeight(int h){m_sizeActions |= TigerUILib::RA_MinimumHeight;float scale = dpi_scale;m_minimumSize.setHeight(h);	__super::setMinimumHeight(m_minimumSize.height() * scale);}\
    	void T##name::setMinimumWidth(int w){m_sizeActions |= TigerUILib::RA_MinimumWidth;float scale = dpi_scale;		m_minimumSize.setWidth(w);	__super::setMinimumWidth(m_minimumSize.width() * scale);}\
    	void T##name::setMaximumSize(const QSize & size){m_sizeActions |= TigerUILib::RA_MaximumSize;float scale = dpi_scale;	m_maximumSize = size;	__super::setMaximumSize(m_maximumSize * scale);}\
    	void T##name::setMaximumSize(int w, int h){m_sizeActions |= TigerUILib::RA_MaximumSize;float scale = dpi_scale;	m_maximumSize = QSize(w, h);	__super::setMaximumSize(m_maximumSize.width() * scale, m_maximumSize.height() * scale);}\
    	void T##name::setMaximumHeight(int h){m_sizeActions |= TigerUILib::RA_MaximumHeight;float scale = dpi_scale;	m_maximumSize.setHeight(h);	__super::setMaximumHeight(m_maximumSize.height() * scale);}\
    	void T##name::setMaximumWidth(int w){m_sizeActions |= TigerUILib::RA_MaximumWidth;float scale = dpi_scale;	m_maximumSize.setWidth(w);	__super::setMaximumWidth(m_maximumSize.width() * scale);}
    

    动态调整

    仔细阅读DEFINE_RESIZE宏中的任意一个函数,就能发现每一个函数中都有一个TigerUILib::WidgetAction标记,表示该对象的此函数是否被调用过,标记之后有一个好处,那就是当我们软件所在屏幕的DPI发生变化时可以有针对性的去调用相关函数,下面是一个简单的测试代码。

    if (testflag("setfixedWidth"))
    {
        setFixedWidth(width * scale);
    }
    

    说到这里有必要介绍下DEFINTE_SCALE_RESIZE宏,如下代码,就不解释了一看应该都会明白

    #define DEFINTE_SCALE_RESIZE(name)\
    	if (m_sizeActions.testFlag(TigerUILib::RA_FixedWidth)){Q##name::setFixedWidth(m_size.width() * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_FixedHeight)){Q##name::setFixedHeight(m_size.height() * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_FixedSize)){Q##name::setFixedSize(m_size * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_Resize)){	QSize newSize = m_size * scale;if(minimumSize().width() > newSize.width()){Q##name::setMinimumSize(newSize);}Q##name::resize(newSize);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_MinimumSize)){Q##name::setMinimumSize(m_minimumSize * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_MinimumHeight)){Q##name::setMinimumHeight(m_minimumSize.height() * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_MinimumWidth)){Q##name::setMinimumWidth(m_minimumSize.width() * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_MaximumSize)){Q##name::setMaximumSize(m_maximumSize * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_MaximumHeight)){Q##name::setMaximumHeight(m_maximumSize.height() * scale);}\
    	if (m_sizeActions.testFlag(TigerUILib::RA_MaximumWidth)){ Q##name::setMaximumWidth(m_maximumSize.width() * scale); }\
    	dpi_scale = scale;
    

    2、字体大小

    Qt程序我们的字体大小都是在qss文件中进行标记,那么适配高DPI也就很简单了,只需要把96dpi下的数字大小按比例进行放大即可。

    知道方法后,做起来就很简单了,只需要写一个字符串替换函数,把qss中的数值按比例放大即可,方法如下。

    数值放大时有一个小技巧,那就是要做一个平滑处理,1.49px当做1px处理 1.5px当做2px,意思就是说在做数字当大的过程中,可能会出现小数,我们的原则是数值放大后加上0.50001然后取整数部分。

    QString QtTigerHelper::ScaleSheet(const QString & sheet, float scale)
    {
    	if (sheet.isEmpty())
    	{
    		return sheet;
    	}
    
    	//1倍图时不需要做任何处理
    	if (scale == 1.0)
    	{
    		return sheet;
    	}
    
    	//放大字体
    	QString tempStyle = sheet;
    	QRegExp rx("\\d+px", Qt::CaseInsensitive);
    	rx.setMinimal(true);
    	int index = -1;
    	while ((index = rx.indexIn(tempStyle, index + 1)) >= 0)
    	{
    		int capLen = rx.cap(0).length() - 2;
    		QString snum = tempStyle.mid(index, capLen);
    		snum = QString::number(qRound(snum.toInt() * scale));
    		tempStyle.replace(index, capLen, snum);
    		index += snum.length();
    		if (index > tempStyle.size() - 2)
    		{
    			break;
    		}
    	}
    
    	return tempStyle;
    }
    

    3、间距

    Qt中的布局有2中方式可以设置,可以在代码中通过接口设置,也可以通过qss进行设置,当然了这两种情况都需要适配。

    布局的margin

    记录调用了哪些设置大小的函数,在dpi发生变化时重新设置一遍,类似于窗口大小变化时所作调整

    if (testflag("margin"))
    {
        setContextMargin(...);
    }
    

    padding和margin

    方式和放大字体一样,可以通过统一的时机去处理

    读取原有qss文件,使用正则表达式生成scale版本的新qss文件。

    4、图标

    图标替换是一个相对来说比较复杂的事情,这里有必要细说一下。

    首先是工程中需要额外添加2x和3x分辨率的图标,1x图标为正常情况下使用的图标,2x和3x图标分别是高分辨率下的图标

    替换图标有两种情况,一种是使用qss方式贴的图,另一种是自绘贴的图

    qss方式

    预先生成高分辨率下的整数倍xxx_2x.qss和xxx_3x.qss文件,需要强调一下,2x和3xqss文件中的字号还是一倍程序中的字号,实际使用的时候在动态放大,如果想要程序的效率高一些可能还需要做一些缓存

    自绘

    如果是自绘文字和图片,那就需要自己控制缩放比,和图片压缩系数

    缩放比: 绘制文字时需要放大的比例,计算方式为当前dpi值除以96.0,结果是一个浮点数,比如说1.5

    压缩系数: 绘制图片的时候这里有一个小窍门,当我们绘制缩放比为小数情况时,需要使用距离较近的整数图片进行压缩绘制,这样的情况我们就需要使用压缩系数进行动态调整绘制图片的大小

    float ImagePath::GetStretchFactor(float scale)
    {
    	if (scale < 1.5)
    	{
    		return scale;
    	}
    	else if (scale < 2.5)
    	{
    		return scale / 2;
    	}
    	else if (scale < 3.5)
    	{
    		return scale / 3;
    	}
    	else//缺省为3倍图拉伸
    	{
    		return scale / 3;
    	}
    }
    

    以上就是DPI适配方案的大致思路了,因为篇幅原因没有针对每一个widget和layout进行详细说明,有需要的可以私聊。

    四、相关文章

    Qt之高DPI显示器(一) - 解决方案整理

    PPI vs. DPI: 有什么区别?

    High DPI Desktop Application Development on Windows

    PROCESS_DPI_AWARENESS Enumeration

    SetProcessDPIAware function:Win Vista开始支持的接口

    SetProcessDpiAwareness function:Win8.1开始支持的接口

    关于Windows高DPI的一些简单总结

    如何开发新的Qt 5.7高DPI每监视器DPI感

    值得一看的优秀文章:

    1. 财联社-产品展示
    2. 广联达-产品展示
    3. Qt定制控件列表
    4. 牛逼哄哄的Qt库

    如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!




    很重要–转载声明

    1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

    2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


    展开全文
  • 基于DPI与DFI的流量识别系统的分析与设计,吴倩,辛阳,随着互联网的快速发展,网络成为人们生活中不可或缺的一部分,然而在快速发展的过程中也出现了一些问题,如网络宽带被P2P软件耗尽
  • 当我们说到 像素、分辨率、DPI、PPI等专业术语的时候,一般会涉及到图像、屏幕、打印机等等。 像素(Pixel)为图像显示的基本单位,是用来计算数码影像的一种单位。Pix是picture的简写,加上“元素”...
  • DPI和DFI带宽管理技术分析

    千次阅读 2012-07-03 23:24:03
    DPI技术技术在分析包头的基础上,增加了对应用层的分析,是一种基于应用层的流量检测和控制技术,当IP数据包、TCP或UDP数据流经过基于DPI技术的带宽管理系统时,该系统通过深入读取IP包载荷的内容来对OSI7层协议中的...
  • 最近再学习WPF开发,其中提到一个特性“分辨率无关性”,主要功能就是实现开发的桌面程序在不同分辨率的电脑上显示时,会根据系统的DPI自动进行UI的缩放,从而不会导致应用程序的失真。  这个里面就提到了个系统...
  • 一、xxxhdpi、560dpi、xxhdpi、xhdpi、hdpi、mdpi、ldpi (还有tvdpi 主要用于电视,不去讨论) 在某个dpi缺失的情况下,如果去找寻。 二、values-xxhdpi-1920X1080 这种以dpi加屏幕分辨率的适配方案是什么规律。 一...
  • 新浪微博的分析 早上刚刚起床先刷微博,打算就分析一下新浪微博。登陆之后抓取公布微博的数据包。进行分析。 1.抓包的要点: 1.关闭其它网络应用,保证本机网络流量的干净,便于分析。 2.先开启...
  • DPI厂商简介

    千次阅读 2011-07-08 16:40:08
    DPI厂商简介本次仅仅对DPI厂商进行一个罗列,后续有时间再慢慢完善对比分析各家产品的竞争力。一、DPI厂商1、国外Sandvine,Cisco,Allot,Qosmos,Ipoque,CloudShied,Evicsson,Hillstone2、国内华赛 SIG,宽广 BTM,
  • 关于DPI的学习笔记 先看一下定义 : DPI(Deep Packet Inspection)是一种基于数据包的深度检测技术...流量和报文内容进行检测分析 要求: 有事先定义的策略 对检测的流量进行过滤控制。 完成的目标:业务精细...
  • DPI vs DFI

    2010-04-23 13:29:29
    DPI技术在分析包头的基础上,增加了对应用层的分析,是一种基于应用层的流量检测和控制技术。当IP数据包、TCP或UDP数据流经过基于DPI技术的流量管理系统时,该系统通过深入读取IP包载荷的内容来对OSI 7层协议中的...
  • dpi.exe

    2007-12-02 21:12:00
    进程知识库 dpi - dpi.exe - 进程信息进程文件:dpi 或者 dpi.exe进程名称: The DelFin Project, Spyware 描述:dpi.exe是DelFin Project公司的广告程序。这个进程监视你的浏览习惯,并将相关数据回传到其服务器上...
  • 没有复杂的计算和不可分析的图表,只是按照划分直截了当地将内容呈献给读者,便于读者理解以及直接运用到设计过程中,非常实用且专业的知识,记得收藏唷。什么是DPI、PPI?DPI(Dots Per Inch)是测量空间点密度的...
  • 这里提供的是分析的方法和思路,是为了详细了解以下几个方面:电信运营商正在部署DPI技术的趋势DPI技术提供者所面临的潜在市场空间投资者需要正确把握DPI技术的市场机会 国内外的众多厂家都看到了“应用识别”、...
  • 导读: 新型冠状病毒感染的肺炎疫情牵动着全国人民的心,在这场没有硝烟的战争中,智慧运营与大数据研究部积极进行了...深度包检测技术是在传统IP数据包检测技术(OSI L2-L4之间包含的数据包元素的检测分析)之上增加了
  • 深度数据包检测(DPI)

    2019-07-08 17:38:34
    深度数据包检测(Deep packet inspection,缩写为 DPI)是一种特殊的网络技术,一般网络设备只会查看以太网头部、IP头部而不会分析TCP/UDP里面的内容这种被称为浅数据包检测;与之对应的DPI会检查TCP/UDP里面的内容,...
  • DPI — 深度数据报文解析

    千次阅读 2020-08-20 19:52:11
    普通报文检测仅会分析数据报文的 IP 五元组(源地址、目的地址、源端口、目的端口以及协议类型)。 而 DPI 除了支持上述 L2-L4 的报文首部解析之外,还增加了对 L7 应用层有效载荷(Payload)的解析,可以识别各
  • 【WPF】DPI对控件定位产生的影响

    千次阅读 2017-08-26 18:28:27
    需求程序界面上是一个Window,当用户...分析1.系统像素与显示器像素我们知道wpf中控件宽高的单位是1/96英寸,如果你系统的dpi为96(再这里我们不考虑显示器的dpi,那是windows系统的事情),那么1/96英寸就是1个系统像
  • 深度包检测 DPI 介绍

    2020-04-20 13:10:25
    DPI(Deep Packet Inspection)深度包检测技术是在传统IP数据包检测技术(OSI L2-L4之间包含的数据包元素的检测分析)之上增加了对应用层数据的应用协议识别,数据包内容检测与深度解码。 既可以检测2~4层,又可以检测...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 289
精华内容 115
热门标签
关键字:

dpi分析