精华内容
下载资源
问答
  • C#原始套接字数据包转发代码,可以检测到传入和发出的所有端口的数据包。
  • LBSALE[300]LBSALELinus Torvalds的Linux原始代码! 好东西,看的顶一下! LINUX , 东西 35_804.rar
  • 用Matlab2014 a的程序可运行,里面含有低通滤波器、中值频率、平均功率频率、频谱图的程序
  • 五分钟学Java:为什么不应该使用Java的原始类型?

    千次阅读 多人点赞 2019-12-19 17:37:04
    在逛 Stack Overflow 的时候,发现了一些访问量像熊耳山一样高的问题,比如说这个:为什么不应该使用Java的原始类型?访问量足足有 205K+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,本文...

    在逛 Stack Overflow 的时候,发现了一些访问量像熊耳山一样高的问题,比如说这个:为什么不应该使用Java的原始类型?访问量足足有 205K+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,本文之前的就是其中之一。

    来回顾一下提问者的问题吧:

    Java 的原始类型是什么?为什么不要使用原始类型?如果不能使用原始类型,有什么更好的选择呢?

    如果大家也被这个问题困扰过,或者正在被困扰,就请随我来,咱们肩并肩手拉手一起梳理一下这个问题,并找出最佳答案。Duang、Duang、Duang,打怪进阶喽!

    01、Java 的原始类型是什么?

    要理解 Java 的原始类型是什么,可以先看一下什么是泛型

    List<String> list = null;
    

    其中 list 就是一个泛型,我们通常称之为字符串(String)列表(List),也就是说 list 中只能放字符串类型的元素。

    如果我们按照下面这种方式声明 list 的话,它就是一个原始类型。

    List list = null;
    

    从 list 的声明当中我们可以对比发现,原始类型没有为容器指定明确的元素类型,所以我们可以在容器中放入一个 String,也可以放入一个 Integer,甚至任意的类型,就像下面这样。

    public class RawType {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("沉默王二");
            list.add(18);
            list.add(new RawType());
        }
    }
    

    注意哦,编译器没有任何提醒!这预示着 Java 这门强类型的语言竟然有点弱类型的影子了。

    PS:关于 Java 中的类型术语,大家可以参照下表。

    术语含义举例
    Parameterized type参数化类型List<String>
    Actual type parameter实际类型参数String
    Generic type泛型类型List<E>
    Formal type parameter形式类型参数E
    Unbounded wildcard type无限制通配符类型List<?>
    Raw type原始类型List
    Bounded type parameter限制类型参数<E extends Number>
    Bounded wildcard type限制通配符类型List<? extends Number>

    02、为什么不要使用原始类型?

    大家可能会有一个疑惑,原始类型用起来很爽啊!因为不用关心放入 List 的元素到底是什么类型,想放什么就可以放什么,不要太爽啊!

    可当我们想要从 List 中把元素取出来使用的时候,可就遇到大麻烦了。

    List list = new ArrayList();
    list.add("沉默王二");
    list.add(18);
    list.add(new RawType());
    
    for (Object o : list ) {
        String s = (String) o;
        System.out.println(s);
    }
    

    上面这段代码编译的时候没有任何问题,但输出的时候就会抛出 ClassCastException

    沉默王二
    Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    	at com.cmower.java_demo.programcreek.RawType.main(RawType.java:14)
    

    除非我们使用 instanceof 关键字进行类型判断,就像下面这样。

    List list = new ArrayList();
    list.add("沉默王二");
    list.add(18);
    list.add(new RawType());
    
    for (Object o : list ) {
        if (o instanceof String) {
            String s = (String) o;
            System.out.println(s);
        } else if (o instanceof Integer) {
            Integer i = (Integer) o;
            System.out.println(i);
        } else if (o instanceof RawType) {
            RawType raw = (RawType) o;
            System.out.println(raw);
        }
    }
    

    可假如代码写成这样,可真真算得上是糟糕的代码了。

    通常来说,为了代码的安全性起见,我们希望代码的错误发生得越早越好,能在编译时就不要在运行时。可使用原始类型的时候,我们发现错误一直到运行时才可能会被检出。

    还记得《扁鹊见蔡桓公》的故事吗?

    扁鹊见蔡桓公,立有间。扁鹊曰:“君有疾在腠理,不治将恐深。”桓侯曰:“寡人无疾。”扁鹊出,桓侯曰:“医之好治不病以为功。”…居十日,扁鹊望桓侯而还走。桓侯故使人问之,扁鹊曰:“疾在腠理,汤熨之所及也;在肌肤,针石之所及也;在肠胃,火齐之所及也;在骨髓,司命之所属,无奈何也。今在骨髓,臣是以无请也。”居五日,桓侯体痛,使人索扁鹊,已逃秦矣。桓侯遂死。

    病情发现得越早,治疗的可能性就越大。同理,代码隐藏的问题发现的越晚,找出根源花费的精力就越大、时间就越多。

    03、有什么更好的选择呢?

    如果不能使用原始类型,有什么更好的选择呢?

    为了让 List 能够容纳任意类型的元素,我们可以使用 List<Object>,尽管这并不是一个最优的选择。

    List<Object> list = new ArrayList<>();
    list.add("沉默王二");
    list.add(18);
    list.add(new RawType());
    

    鹅鹅鹅,这样的参数化类型 List<Object> 和原始类型 List 之间有区别吗?

    当然有了!

    List<Object> 至少明确地告诉编译器,该容器可以存放任意类型的对象,没有丢失类型的安全性。

    可能我这样的解释会遭到某些抨击:“这不五十步笑百步吗?呵呵。”但我要想表达的是登月男神阿姆斯特朗的那句话:“这是我个人的一小步,却是人类的一大步。”能向前迈一步是一步啊。

    那最优的选择是什么呢?

    从一开始就为 List 声明具体的类型,比如说 List<String> list,当我们尝试放入一个 int 值的时候就会编译出错。

    从另一种层面上来说,这样做削弱了程序的灵活性,但保证了程序的绝对安全性,以及在表达上的明确性。

    04、为什么 Java 允许使用原始类型?

    既然原始类型是不安全的,那为什么 Java 一直允许使用原始类型呢?并且泛型擦除后仍然是个原始类型呢?

    答案很简单、很无厘头、很苍白——为了版本兼容!

    引入泛型的时候,Java 已经进入到第二个十年(年纪大了),市面上存在大量没有使用 Java 泛型的代码。如果因为版本升级导致它们不能使用,恐怕 Java 也活不到现在,毕竟对用户友好才是一个软件存在的硬道理。

    当然了,Java 已经对开发者做出了警示:强烈建议不要在 Java 代码中使用原始类型,未来的版本中可以会禁止使用原始类型,请小心点。

    05、鸣谢

    好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了👍。如果觉得不过瘾,我把本系列文章做了汇总,这里推荐给大家。

    五分钟学Java:打印数组最优雅的方式是什么?

    五分钟学Java:为什么不应该使用Java的原始类型?

    五分钟学Java:为什么会发生ArrayIndexOutOfBoundsException?

    五分钟学Java:Java 到底是值传递还是引用传递?

    五分钟学Java:如何比较 Java 的字符串?

    五分钟学Java:什么是 NullPointerException?

    谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力

    展开全文
  • 非利用WINPCAP,而是直接利用WINDOWS的原始套接字,可以把网卡改为混杂模式!是新手学习好好例子!
  • 毕业设计任务书原始版本,明天中午之前发过来啊!
  • 【GAN】二、原始GAN论文详解

    千次阅读 2019-09-20 21:17:39
    本篇博客主要介绍了原始GAN的基恩概念、网络架构、训练方法、相关数学结论与实验结果。

    写在前面

    在前面一篇文章:【GAN】一、利用keras实现DCGAN生成手写数字图像中我们利用keras实现了简单的DCGAN,并生成了手写数字图像。程序结果让我们领略了GAN的强大,接下来我们开始一步一步地介绍各种GAN模型。那么我们首先从最基础的GAN开始说起。


    一、GAN简介

    GAN(Generative Adversarial Network)全名叫做对抗生成网络或者生成对抗网络。GAN这一概念是由Ian Goodfellow于2014年提出,并迅速成为了非常火热的研究话题。目前,GAN的变种更是有上千种,2019年计算机界的诺贝尔奖“图灵奖”得主,深度学习先驱之一的Yann LeCun也曾说:“GAN及其变种是数十年来机器学习领域最有趣的想法。”

    原始GAN的论文连接为:Generative Adversarial Nets

    首先我们用一句话来概括下原始GAN。原始GAN由两个有机中整体构成——生成器 G G G和判别器 D D D,生成器的目的就是将随机输入的高斯噪声映射成图像(“假图”),判别器则是判断输入图像是否来自生成器的概率,即判断输入图像是否为假图的概率。

    GAN的训练也与CNN大不相同,CNN是定义好特定的损失函数,然后利用梯度下降及其改进算法进行优化参数,尽可能用局部最优解去逼近全局最优解。但是GAN的训练是个动态的过程,是生成器 G G G与判别器 D D D两者之间的相互博弈过程。通俗点讲,GAN的目的就是无中生有,以假乱真。即要使得生成器 G G G生成的所谓的"假图"骗过判别器 D D D,那么最优状态就是生成器 G G G生成的所谓的"假图"在判别器 D D D的判别结果为0.5,不知道到底是真图还是假图。

    接下来我们要对GAN中相关术语进行解释。首先给出解释的是生成器 G G G。生成器 G G G用于捕获输入高斯噪声的数据分布,产生"假图"。接下来即使的是判别器。判别器 D D D则是评估输入样本是来自训练集而非生成器的概率。训练生成器 G G G是为了最大化判别器 D D D犯错的概率。原始GAN整个框架是生成器 G G G与判别器 D D D两者之间的相互博弈的动态过程。


    二、GAN训练

    接下来我们来介绍GAN的训练。首先我们给出原始GAN的目标函数(损失函数),损失函数表入下所示:
    min ⁡ G   max ⁡ D   V ( D , G ) = E x ∼ p d a t a ( x ) [ log ⁡ D ( x ) ] + E z ∼ p d a t a ( z ) [ log ⁡ ( 1 − D ( G ( z ) ) ) ] (1) \underset{G}{\mathop{\min }}\,\underset{D}{\mathop{\max }}\,V(D,G)={{\mathbb{E}}_{x\sim {{p}_{data}}(x)}}[\log D(x)]+{{\mathbb{E}}_{z\sim {{p}_{data}}(z)}}[\log (1-D(G(z)))]\tag1 GminDmaxV(D,G)=Expdata(x)[logD(x)]+Ezpdata(z)[log(1D(G(z)))](1)

    其中, G G G代表生成器, D D D代表判别器, x x x代表真实数据, p d a t a p_{data} pdata代表真实数据概率密度分布, z z z代表了随机输入数据,该数据是随机高斯噪声。

    从上式可以看出,从判别器 D D D角度来看判别器 D D D希望能尽可能区分真实样本 x x x和虚假样本 G ( z ) G(z) G(z),因此 D ( x ) D(x) D(x)必须尽可能大, D ( G ( z ) ) D(G(z)) D(G(z))尽可能小, 也就是 V ( D , G ) V(D,G) V(D,G)整体尽可能大。从生成器 G G G的角度来看,生成器 G G G希望自己生成的虚假数据 G ( z ) G(z) G(z)可以尽可能骗过判别器 D D D,也就是希望 D ( G ( z ) ) D(G(z)) D(G(z))尽可能大,也就是 V ( D , G ) V(D,G) V(D,G)整体尽可能小。GAN的两个模块在训练相互对抗,最后达到全局最优。

    下面原始论文也给出了GAN的训练过程示意图。在上图中,平行线代表噪声 z z z,它映射到了 x x x,蓝色点线代表判别器 D D D的输出,黑色圆点代表真实数据分布 p d a t a p_{data} pdata,绿色实线代表生成器 G G G的虚假数据的概率分布 p g p_g pg。可以从下面图看出,在GAN的训练过程中,生成器 G G G的概率密度分布慢慢的逼近真实数据集的概率密度分布,而判别器预测值也在不断下降,当出现下图(d)的情况时, D ( G ( z ) ) = 0.5 D(G(z))=0.5 D(G(z))=0.5,即分不清输入图像到底是真实图像还是生成器伪造的假图。
    在这里插入图片描述
    接下来,我们给出GAN训练的算法,如下图所示。从下图训练算法可以看出,首先利用上述目标函数结合梯度上升训练K次判别器,之后结合梯度下降去训练1次器。
    在这里插入图片描述
    在论文中,作者也给出了GAN相关结论数学证明 ,CSDN上已经有大神给出相关详细推导过程,在此我就不给出相关证明了,若有兴趣请移步:GAN论文阅读——原始GAN(基本概念及理论推导)。不过我在此也对相关接结论进行一个归纳:

    1. 生成器概率密度分布 p g p_g pg与真实数据分布 p d a t a p_{data} pdata相等时,GAN的目标函数取得全局最优解。
    2. 最优判别器 D D D的表达式为: D G ∗ ( x ) = p d a t a ( x ) p d a t a ( x ) + p G ( x ) D_{G}^{*}(x)=\frac{{{p}_{data}}(x)}{{{p}_{data}}(x)+{{p}_{G}}(x)} DG(x)=pdata(x)+pG(x)pdata(x),那么GAN取得最优时 p g = p d a t a p_g = p_{data} pg=pdata,,那么 D G ∗ ( x ) = 0.5 D_{G}^{*}(x)=0.5 DG(x)=0.5
    3. 综合1,2两点,虽然在实际训练GAN中,我们最终不能使得 p g = p d a t a p_g = p_{data} pg=pdata,但是我们v必须尽可能去逼近这个结果,这样才能使得生成的“假图”能够以假乱真。

    三、 实验结果

    下面给出原始GAN实验结果。首先给出在MNIST数据集和TFD上对数极大似然估计,实验结果如下如所示。
    在这里插入图片描述
    接下来是是实验结果可视化,分别在mnist数据集、TFD数据集和CIFAR-10数据集进行训练,然后生成中间训练结果,如下图所示。其中图a是mnist数据集的训练结果,图b是TFD数据集的训练结果,图c是利用全连接网络的GAN在CIFAR-10数据集上的训练结果,图d是利用卷积和反卷积的GAN在CIFAR-10数据集上的训练结果。
    在这里插入图片描述


    后记

    至此GAN系列第二篇——原始GAN论文详解到此结束,在下一篇博客我们将详细介绍DCGAN的。

    展开全文
  • JDBC原始写法

    千次阅读 2018-01-08 17:02:52
    代码之前,先创建数据库,建表,导入jdbc驱动包 package cn.itcast.jdbc; import java.sql.*; public class JDBCTest { public static void main(String[] args) throws ClassNotFoundException, ...

    在写代码之前,先创建数据库,建表,导入jdbc驱动包


    package cn.itcast.jdbc;
    
    import java.sql.*;
    
    public class JDBCTest {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                //1.加载驱动
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取连接
                String url ="jdbc:mysql://127.0.0.1:3306/mybatis-01";
                String username = "root";
                String paaaword = "821365";
                connection = DriverManager.getConnection(url, username, paaaword);
                //3.获取statement,preparedStatement
                String sql = "SELECT * FROM tb_user WHERE id = ?";
                preparedStatement = connection.prepareStatement(sql);
                //4.设置参数,这边的第一个参数代表了id=,第二个参数代表了id的值,如果查询条件有两个则第二个parameterIndex写2
                preparedStatement.setLong(1,2);
                //5.执行查询,获取结果集
                resultSet = preparedStatement.executeQuery();
                //6.处理结果集
                while (resultSet.next()){
                    //参数可以写下标也可以写列名(表中的字段)
                    System.out.println( resultSet.getString("name"));
                    System.out.println(resultSet.getString("user_name"));
                    System.out.println(resultSet.getInt("age"));
    
    
                   //报错是因为表中没有这个字段,这是类中的属性   resultSet.getString("username");
    
                }
            }  finally {
                //7.关闭连接,释放资源
                resultSet.close();
                preparedStatement.close();
                connection.close();
            }
    
        }
    }
    


    展开全文
  • 网络编程——原始套接字实现原理

    千次阅读 多人点赞 2018-10-26 22:25:10
    1.2、链路层原始套接字  1.3、网络层原始套接字 2、原始套接字的实现 2.1 原始套接字报文收发流程 2.2链路层原始套接字的实现  2.2.1 套接字创建 2.2.2 报文接收 2.2.3 报文发送 2.2.4 其它  2.3 网络层...

    目录

    1. 基础知识

     1.1、概述

    1.2、链路层原始套接字

     1.3、网络层原始套接字

    2、原始套接字的实现

    2.1  原始套接字报文收发流程

    2.2链路层原始套接字的实现

        2.2.1  套接字创建

    2.2.2  报文接收

    2.2.3  报文发送

    2.2.4  其它

     2.3  网络层原始套接字的实现

    2.3.1  套接字创建

    2.3.2  报文接收

    2.3.3  报文发送

    2.3.4  其它

    3、应用及注意事项

    3.1  使用链路层原始套接字

    3.2  使用网络层原始套接字

    3.3  网络诊断工具使用原始套接字


    1. 基础知识

     1.1、概述


              原始套接字(SOCK_RAW)可以用来自行组装IP数据包,然后将数据包发送到其他终端。也就是说原始套接字是基于IP数据包的编程(SOCK_PACKET是基于数据链路层的编程)。协议栈的原始套接字从实现上可以分为“链路层原始套接字”和“网络层原始套接字”两大类。本节主要描述各自的特点及其适用范围。

    链路层原始套接字可以直接用于接收和发送链路层的MAC帧,在发送时需要由调用者自行构造和封装MAC首部。而网络层原始套接字可以直接用于接收和发送IP层的报文数据,在发送时需要自行构造IP报文头(取决是否设置IP_HDRINCL选项)。另外,必须在管理员权限下才能使用原始套接字。
                原始套接口提供了普通TCP和UDP socket不能提供的3个能力: 
                (1)进程使用raw socket 可以读写ICMP、IGMP等分组。
    这个能力还使得使用ICMP或IGMP构造的应用程序能够完全作为用户进程处理,而不必往内核中添加额外代码。 
                 (2)大多数内核只处理IPv4数据报中一个名为协议的8位字段的值为1(ICMP)、2(IGMP)、6(TCP)、17(UDP)四种情况。然而该字段的值还有许多其他值。进程使用raw socket 就可以读写那些内核不处理的IPv4数据报了。因此,可以使用原始套接字定义用户自己的协议格式。 
                (3)通过使用raw socket ,进程可以使用IP_HDRINCL套接口选项自行构造IP头部。这个能力可用于构造特定类型的TCP或UDP分组等。

     

    1.2、链路层原始套接字


            链路层原始套接字调用socket()函数创建。第一个参数指定协议族类型为PF_PACKET,第二个参数type可以设置为SOCK_RAW或SOCK_DGRAM,第三个参数是协议类型(该参数只对报文接收有意义)。协议类型protocol不同取值的意义具体见表1所示:

     socket(PF_PACKET, type, htons(protocol))

            a)参数type设置为SOCK_RAW时,套接字接收和发送的数据都是从MAC首部开始的。在发送时需要由调用者从MAC首部开始构造和封装报文数据。type设置为SOCK_RAW的情况应用是比较多的,因为某些项目会使用到 自定义的二层报文类型。

     socket(PF_PACKET, SOCK_RAW, htons(protocol))

            b)参数type设置为SOCK_DGRAM时,套接字接收到的数据报文会将MAC首部去掉。同时在发送时也不需要再手动构造MAC首部,只需要从IP首部(或ARP首部,取决于封装的报文类型)开始构造即可,而MAC首部的填充由内核实现的。若对于MAC首部不关心的场景,可以使用这种类型,这种用法用得比较少。 

     socket(PF_PACKET, SOCK_DGRAM, htons(protocol)) 

    表1protocol不同取值

     1.3、网络层原始套接字


             创建面向连接的TCP和创建面向无连接的UDP套接字,在接收和发送时只能操作数据部分,而不能对IP首部或TCP和UDP首部进行操作。如果想要操作IP首部或传输层协议首部,就需要调用如下socket()函数创建网络层原始套接字。第一个参数指定协议族的类型为PF_INET,第二个参数为SOCK_RAW,第三个参数protocol为协议类型(不同取值的意义见表2)。产品线有使用OSPF和RSVP等协议,需要使用这种类型的套接字。
        a)发送报文       网络层原始套接字接收到的报文数据是从IP首部开始的,即接收到的数据包含了IP首部, TCP/UDP/ICMP等首部, 以及数据部分。
        b)发送报文        网络层原始套接字发送的报文数据,在默认情况下是从IP首部之后开始的,即需要由调用者自行构造和封装TCP/UDP等协议首部。这种套接字也提供了发送时从IP首部开始构造数据的功能,通过setsockopt()给套接字设置上IP_HDRINCL选项,就需要在发送时自行构造IP首部。

    表2 protocol不同取值

     

    2、原始套接字的实现


            本节主要首先介绍链路层和网络层原始套接字报文的收发总体流程,再分别对两类套接字的创建、接收、发送等具体实现细节进行介绍。


    2.1  原始套接字报文收发流程


    图1 原始套接字接收流程
             如上图1所示为链路层和网络层原始套接字的收发总体流程。网卡驱动收到报文后在软中断上下文中由netif_receive_skb()处理,匹配是否有注册的链路层原始套接字,若匹配上就通过skb_clone()来克隆报文,并将报文交给相应的原始套接字。对于IP报文,在协议栈的ip_local_deliver_finish()函数中会匹配是否有注册的网络层原始套接字,若匹配上就通过skb_clone()克隆报文并交给相应的原始套接字来处理。
    注意:这里只是将报文克隆一份交给原始套接字,而该报文还是会继续走后续的协议栈处理流程。 


    图2  原始套接字发送流程
            如图2所示,链路层原始套接字的发送,直接由套接字层调用packet_sendmsg()函数,最终再调用网卡驱动的发送函数。网络层原始套接字的发送实现要相对复杂一些,由套接字层调用inet_sendmsg()->raw_sendmsg(),再经过路由和邻居子系统的处理后,最终调用网卡驱动的发送函数。若注册了ETH_P_ALL类型套接字,还需要将外发报文再收回去。 


    2.2链路层原始套接字的实现


        2.2.1  套接字创建


        调用socket()函数创建套接字的流程如下,链路层原始套接字最终由packet_create()创建。
    sys_socket()->sock_create()->__sock_create()->packet_create()
        当socket()函数的第三个参数protocol为非零的情况下,会调用dev_add_pack()将链路层套接字packet_sock的packet_type结构链到ptype_all链表或ptype_base链表中。

     

    void dev_add_pack(struct packet_type *pt)
    {
            ……
            if (pt->type == htons(ETH_P_ALL)) {
                     netdev_nit++;
                     list_add_rcu(&pt->list, &ptype_all);
             } else {
                      hash = ntohs(pt->type) & 15;
                     list_add_rcu(&pt->list, &ptype_base[hash]);
              }
             ……
    }


        当protocol为ETH_P_ALL时,会将套接字加入到ptype_all链表中。如图3所示,这里创建了两个链路层原始套接字。
      

    图3 ptype_all链表 

        当protocol为其它非0值时,会将套接字加入到ptype_base链表中。如图3所示,协议栈本身也需要注册packet_type结构,图中浅色的两个packet_type结构分别是IP协议和ARP协议注册的,其处理函数分别为ip_rcv()和arp_rcv()。图中另外3个深色的packet_type结构则是链路层原始套接字注册的,分别用于接收类型为ETH_P_IP、ETH_P_ARP和0x0810类型的报文。
     
     

    图4 ptype_base链表 
     
     

    2.2.2  报文接收


    网卡驱动程序接收到报文后,在软中断上下文由netif_receive_skb()处理。首先会逐个遍历ptype_all链表中的packet_type结构,若满足条件“(!ptype->dev || ptype->dev == skb->dev)”,即套接字未绑定或者套接字绑定网口与skb所在网口匹配,就增加报文引用计数并交给packet_rcv()函数处理(若使用PACKET_MMAP收包方式则由tpacket_rcv()函数处理)。
    网卡驱动->netif_receive_skb()->deliver_skb()->packet_rcv()/tpacket_rcv()
        以非PACKET_MMAP收包方式为例进行说明,packet_rcv()函数中比较重要的代码片段如下。当报文skb到达packet_rcv()函数时,其skb->data所指的数据是不包含MAC首部的,所以对于type为非SOCK_DGRAM(即SOCK_RAW)类型,需要将skb->data指针前移,以便数据部分可以包含MAC首部。最后将skb放到套接字的接收队列sk->sk_receive_queue中,并唤醒用户态进程来读取套接字中的数据。

     

     ……
    if (sk->sk_type != SOCK_DGRAM) //即SOCK_RAW类型
          skb_push(skb, skb->data - skb->mac.raw);
    ……
    __skb_queue_tail(&sk->sk_receive_queue, skb);
    sk->sk_data_ready(sk, skb->len); //唤醒进程读取数据
    ……


    PACKET_MMAP收包方式的实现有所不同,tpacket_rcv()函数将skb->data拷贝到与用户态mmap映射的共享内存中,最后唤醒用户态进程来读取数据。由于报文的内容已存放在内核空间和用户空间共享的缓冲区中,用户态可以直接读取以减少数据的拷贝,所以这种方式效率比较高。
        上面介绍了报文接收在软中断的处理流程。下面以非PACKET_MMAP收包方式为例,介绍用户态读取报文数据的流程。用户态recvmsg()最终调用skb_recv_datagram(),如果套接字接收队列sk->sk_receive_queue中有报文就取skb并返回。否则调用wait_for_packet()等待,直到内核软中断收到报文并唤醒用户态进程。
    sys_recvmsg()->sock_recvmsg()->…->packet_recvmsg()->skb_recv_datagram()


    2.2.3  报文发送


    用户态调用sendto()或sendmsg()发送报文的内核态处理流程如下,由套接字层最终会调用到packet_sendmsg()。
    sys_sendto()->sock_sendmsg()->__sock_sendmsg()->packet_sendmsg()->dev_queue_xmit()
        该函数比较重要的函数片段如下。首先进行参数检查及skb分配,再调用驱动程序的hard_header函数(对于以太网驱动是eth_header()函数)来构造报文的MAC头部,此时的skb->data是指向MAC首部的,且skb->len为MAC首部长度(即14)。对于创建时指定type为SOCK_RAW类型套接字,由于在发送时需要自行构造MAC头部,所以将skb->tail指针恢复到MAC首部开始的位置,并将skb->len设置为0(即不使用内核构造的MAC首部)。接着再调用memcpy_fromiovec()从skb->tail的位置开始拷贝报文数据,最终调用网卡驱动的发送函数将报文发送出去。
    注:如果创建套接字时指定type为SOCK_DGRAM,则使用内核构造的MAC首部,用户态发送的数据中不含MAC头部数据。

    ……
    res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len); //构造MAC首部
    if (sock->type != SOCK_DGRAM) {
            skb->tail = skb->data; //SOCK_RAW类型
            skb->len = 0;
    }
    ……
    err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); //拷贝报文数据
    ……
    err = dev_queue_xmit(skb); //发送报文
    …… 


    2.2.4  其它


    a) 套接字的绑定
    链路层原始套接字可调用bind()函数进行绑定,让packet_type结构dev字段指向相应的net_device结构,即将套接字绑定到相应的网口上。如2.2.2节报文接收的描述,在接收时如果套接口有绑定就需要进一步确认当前skb->dev是否与绑定网口相匹配,只有匹配的才会将报文上送到相应的套接字。
    sys_bind()->packet_bind()->packet_do_bind()
    b)套接字选项
    以下是比较常用的套接字选项
    PACKET_RX_RING:用于PACKET_MMAP收包方式设置接收环形队列
    PACKET_STATISTICS:用于读取收包统计信息
    c)信息查看
    链路层原始套接字的信息可通过/proc/net/packet进行查看。如下为图2和图3中创建的原始套接字的信息,可以查看到创建时指定的协议类型、是否绑定网口、已使用的接收缓存大小等信息。这些信息对于分析和定位问题有帮助cat /proc/net/packet
    1.    
    1.    sk RefCnt Type Proto Iface R Rmem User Inode
    2.    ffff810007df8400 3 3 0810 0 1 0 0 1310
    3.    ffff810007df8800 3 3 0806 0 1 0 0 1309
    4.    ffff810007df8c00 3 3 0800 0 1 560 0 1308
    5.    ffff810007df8000 3 3 0003 0 1 560 0 1307
    6.    ffff810007df3800 3 3 0003 0 1 560 0 1306

     
    2.3  网络层原始套接字的实现

    2.3.1  套接字创建


         如图5所示,在IPV4协议栈中一个传输层协议(如TCP,UDP,UDP-Lite等)对应一个inet_protosw结构,而inet_protosw结构中又包含了proto_ops结构和proto结构。网络子系统初始化时将所有的inet_protosw结构hash到全局的inetsw[]数组中。proto_ops结构实现的是从与协议无关的套接口层到协议相关的传输层的转接,而proto结构又将传输层映射到网络层。
      

    图5  inetsw[]数组结构 
        调用socket()函数创建套接字的流程如下,网络层原始套接字最终由inet_create()创建。
    sys_socket()->sock_create()->__sock_create()->inet_create()
        inet_create()函数除用于创建网络层原始套接字外,还用于创建TCP、UDP套接字。首先根据socket()函数的第二个参数(即SOCK_RAW)在inetsw[]数组中匹配到相应的inet_protosw结构。并将套接字结构的ops设置为inet_sockraw_ops,将套接字结构的sk_prot设置为raw_prot。然后对于SOCK_RAW类型套接字,还要将inet->num设置为协议类型,以便最后能调用proto结构的hash函数(即raw_v4_hash())。
            

     

    ……
    sock->ops = answer->ops; //将socket结构的ops设置为inet_sockraw_ops
    answer_prot = answer->prot;
    ……
    if (SOCK_RAW == sock->type) { //SOCK_RAW类型的套接字,设置inet->num
            inet->num = protocol;
            if (IPPROTO_RAW == protocol) //protocol为IPPROTO_RAW的特殊处理,
                    inet->hdrincl = 1; 后续在报文发送时会再讲到
    }
    ……
    if (inet->num) {
             inet->sport = htons(inet->num);
             sk->sk_prot->hash(sk); //调用raw_v4_hash()函数将套接字链到raw_v4_htable中
    }
    …… 


    经过如上操作后,相应的套接字结构sock会通过raw_v4_hash()函数链到raw_v4_htable链表中,网络层原始套接字报文接收时需要使用到raw_v4_htable。如图6所示,共创建了3个网络层原始套接字,协议类型分别为IPPROTO_TCP、IPPROTO_ICMP和89。 
     

    图6  raw_v4_htable链表 

    2.3.2  报文接收


        网卡驱动收到报文后在软中断上下文由netif_receive_skb()处理,对于IP报文且目的地址为本机的会由ip_rcv()最终调用ip_local_deliver_finish()函数。ip_local_deliver_finish()主要功能的代码片段如下,先根据报文的L4层协议类型hash值在图5中的raw_v4_htable表中查找是否有匹配的sock。如果有匹配的sock结构,就进一步调用raw_v4_input()处理网络层原始套接字。不管是否有原始套接字要处理,该报文都会走后续的协议栈处理流程。即会继续匹配inet_protos[]数组,根据L4层协议类型走TCP、UDP、ICMP等不同处理流程。

    ……
    hash = protocol & (MAX_INET_PROTOS - 1); //根据报文协议类型取hash值
    raw_sk = sk_head(&raw_v4_htable[hash]); //在raw_v4_htable中查找
    ……
    if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash)) //处理原始套接字
    ……
    if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { //匹配inet_protos[]数组
            ……
            ret = ipprot->handler(skb); //调用传输层处理函数
             ……
     } else { //如果在inet_protos[]数组中未匹配到,则释放报文
             ……
             kfree_skb(skb);
     }
     …… 


    如图7所示的inet_protos[]数组,每项由net_protocol结构组成。表示一个协议(包括传输层协议和网络层附属协议)的接收处理函数集,一般包括一个正常接收函数和一个出错接收函数。图中TCP、UDP和ICMP协议的接收处理函数分别为tcp_v4_rcv()、udp_rcv()和icmp_rcv()。如果在inet_protos[]数组中未配置到相应的net_protocol结构,报文就会被丢弃掉。比如OSPF报文(协议类型为89)在inet_protos[]数组中没有相应的项,内核会将其丢弃掉,这种报文只能提供网络层原始套接字接收到用户态来处理。

    图7  inet_protos[]数组结构  
     网络层原始套接字的总体接收流程如下,最终会将skb挂到相应套接字上,并唤醒用户态进程读取报文数据。
    网卡驱动->netif_receive_skb()->ip_rcv()->ip_rcv_finish()->ip_local_deliver()->ip_local
    _deliver_finish()->raw_v4_input()->raw_rcv()->raw_rcv_skb()->sock_queue_rcv_skb()

    ……
    skb_queue_tail(&sk->sk_receive_queue, skb); //挂到接收队列
    if (!sock_flag(sk, SOCK_DEAD))
            sk->sk_data_ready(sk, skb_len); //唤醒用户态进程5.    ……


           上面介绍了报文接收在软中断的处理流程,下面介绍用户态进程读取报文是如何实现的。用户态的recvmsg()最终会调用raw_recvmsg(),后者再调用skb_recv_datagram。如果套接字接收队列sk->sk_receive_queue中有报文就取skb并返回。否则调用wait_for_packet()等待,直到内核软中断收到报文并唤醒用户态进程。
    sys_recvmsg()->sock_recvmsg()->…->sock_common_recvmsg()->raw_recvmsg()
     

    2.3.3  报文发送
     

    用户态调用sendto()或sendmsg()发送报文的内核态处理流程如下,最终由raw_sendmsg()进行发送。
    sys_sendto()->sock_sendmsg()->__sock_sendmsg()->inet_sendmsg()->raw_sendmsg()
        此函数先进行一些参数合法性检测,然后调用ip_route_output_slow()进行选路。选路成功后主要执行如下代码片段,根据inet->hdrincl是否设置走不同的流程。raw_send_hdrinc()函数表示用户态发送的数据中需要包含IP首部,即由调用者在发送时自行构造IP首部。如果inet->hdrincl未置位,表示内核会构造IP首部,即调用者发送的数据中不包含IP首部。不管走哪个流程,最终都会经过ip_output()->ip_finish_output()->…->dev_queue_xmit()将报文交给网卡驱动的发送函数发送出去。

    ……
    if (inet->hdrincl) { //调用者要构造IP首部
            err = raw_send_hdrinc(sk, msg->msg_iov, len,
                                  rt, msg->msg_flags);
    } else {
            …… //由内核构造IP首部
           err = ip_push_pending_frames(sk);
    }
    ……


       注:inet->hdrincl置位表示用户态发送的数据中要包含IP首部,inet->hdrincl在以下两种情况下被置位。
        a). 给套接字设置IP_HDRINCL选项
              setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val))
        b). 调用socket()创建套接字时,第三个参数指定为IPPROTO_RAW,见2.3.1节。
              socktet(PF_INET, SOCK_RAW, IPPROTO_RAW)

    2.3.4  其它


    a) 套接字绑定
    若原始套接字调用bind()绑定了一个地址,则该套接口只能收到目的IP地址与绑定地址相匹配的报文。内核的具体实现是raw_bind(),将inet->rcv_saddr设置为绑定地址。在原始套接字接收时,__raw_v4_lookup()在设置了inet->rcv_saddr字段的情况下,会判断该字段是否与报文目的IP地址相同。
    sys_bind()->inet_bind()->raw_bind()
    b) 信息查看
    网络层原始套接字的信息可通过/proc/net/raw进行查看。如下为图5所创建的3个网络层原始套接字的信息,可以查看到创建套接字时指定的协议类型、绑定的地址、发送和接收队列已使用的缓存大小等信息。这些信息对于分析和定位问题有帮助。
    1.    cat /proc/net/raw
    2.    sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
    3.    1: 00000000:0001 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 1323 2 ffff8100070b2380
    4.    6: 00000000:0006 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 1322 2 ffff8100070b2080
    5.    89: 00000000:0059 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 1324 2 ffff8100070b2680

     

    3、应用及注意事项

    3.1  使用链路层原始套接字


    注意事项:
    a)       尽量避免创建过多原始套接字,且原始套接字要尽量绑定网卡。因为收到每个报文除了会将其分发给绑定在该网卡上的原始套接字外,还会分发给没有绑定网卡的原始套接字。如果原始套接字较多,一个报文就会在软中断上下文中分发多次,造成处理时间过长。
    b)      发包和收包尽量使用同一个原始套接字。如果发包与收包使用两个不同的原始套接字,会由于接收报文时分发多次而影响性能。而且用于发送的那个套接字的接收队列上也会缓存报文,直至达到接收队列大小限制,会造成内存泄露。
    c)       若只接收指定类型二层报文,在调用socket()时指定第三个参数的协议类型,而最好不要使用ETH_P_ALL。因为ETH_P_ALL会接收所有类型的报文,而且还会将外发报文收回来,这样就需要做BPF过滤,比较影响性能。
     

    3.2  使用网络层原始套接字


    注意事项:
    a)       由于IP报文的重组是在网络层原始套接字接收流程之前执行的,所以该原始套接字不能接收到UDP和TCP的分组数据。
    b)      若原始套接字已由bind()绑定了某个本地IP地址,那么只有目的IP地址与绑定地址匹配的报文,才能递送到这个套接口上。
    c)       若原始套接字已由connect()指定了某个远地IP地址,那么只有源IP地址与这个已连接地址匹配的报文,才能递送到这个套接口上。
     

    3.3  网络诊断工具使用原始套接字


    很多网络诊断工具也是利用原始套接字来实现的,经常会使用到的有tcpdump, ping和traceroute等。
    tcpdump
    该工具用于截获网口上的报文流量。其实现原理是创建ETH_P_ALL类型的链路层原始套接字,读取和解析报文数据并将信息显示出来。
    ping
    该工具用于检查网络连接。其实现原理是创建网络层原始套接字,指定协议类型为IPPROTO_ICMP。检测方构造ICMP回射请求报文(类型为ICMP_ECHO),根据ICMP协议实现,被检测方收到该请求报文后会响应一个ICMP回射应答报文(类型为ICMP_ECHOREPLY)。然后检测方通过原始套接字读取并解析应答报文,并显示出序号、TTL等信息。
    traceroute
    该工具用于跟踪IP报文在网络中的路由过程。其实现原理也是创建网络层原始套接字,指定协议类型为IPPROTO_ICMP。假设从A主机路由到D主机,需要依次经过B主机和C主机。使用traceroute来跟踪A主机到D主机的路由途径,具体步骤如下,在每次探测过程中会显示各节点的IP、时间等信息。
    a)       A主机使用普通的UDP套接字向目的主机发送TTL为1(使用套接口选项IP_TTL来修改)的UDP报文;
    b)      B主机收到该UDP报文后,由于TTL为1会拒绝转发,并且向A主机发送code为ICMP_EXC_TTL的ICMP报文;
    c)       A主机用创建的网络层原始套接字读取并解析ICMP报文。如果ICMP报文code是ICMP_EXC_TTL,就将UDP报文的TTL增加1并回到步骤a)继续进行探测;如果ICMP报文的code是ICMP_PROT_UNREACH,表示UDP报文到达了目的地。
                  A主机―>B主机―>C主机―>D主机

    展开全文
  • 原始套接字学习总结

    千次阅读 2016-05-06 11:24:17
    raw socket(原始套接字)工作原理与规则 原始套接字是一个特殊的套接字类型,它的创建方式跟TCP/UDP创建方法几乎是 一摸一样,例如,通过  int sockfd;  sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP); ...
  • Linux网络编程——原始套接字编程

    万次阅读 多人点赞 2015-03-27 17:47:16
    原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据。区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧...
  • Java中的原始类型与原始封装类型

    千次阅读 2019-08-05 15:36:42
    Java代码的过程中,一直对Java提供的一些类参数又疑问,今天查了一下,进行了解学习。 type argument cannot be of primitive type 问题: Java中什么是原生类型? Java中什么是封装类型? Java 中的原始...
  • 方舟是我最近才开始玩的一款游戏,经过无数个日夜的摸索呢,也总结到一些规律,这里出来给大家分享一下。因为方舟是没有新手教程的,所以我对方舟进行一些详细的解释。推荐大家第一次玩的时候先选择单机。这样就...
  • 解释:这里最重要的一点就是,把checkbox的原始样式取消,用新的样式来代替的, visibility: hidden; 就是取消样式的。其他的css代码不做多解释的,那个复选框最初的样式是红色的,然后checked的样式...
  • 原始套接字编程(C++)

    千次阅读 2019-04-02 21:16:48
    但是这第三个实验坑比较多,了好长时间,百度了好久才对。 我是先启动的服务器,在启动数据包捕获,最后在启动客户端,这样最初的通信也能捕获到。而且我的客户端与服务器时双向通信,也就是两者都会经过...
  • 原始需求的来龙去脉和核心要求

    千次阅读 2011-09-24 06:01:34
    在软件工程术语中,“原始需求”并不是个常见的说法。 这个“原始需求”的提法是本博主自己提出来的。它的起源与CMM有关,CMM中,有“客户需求”的说法。 当时没有直接采用“客户需求”而采用“原始需求”的主要...
  •   JS1997年标准化以后,定义了六种内置类型,包括五种原始(Primitive)类型以及一种引用类型,ES6又新增了一种原始类型-----symbol,接下来咱们一一来分析下这七种内置类型: 六种原始类型 分别是: number string ...
  • 使用Retrofit获取原始的json数据

    千次阅读 2018-08-22 22:41:11
    //把原始数据转为字符串 KLog.e("retrofit获取到的数据", jsonStr); jsonToObj(jsonStr);//这是对字符串数据解析具体数据方法 } catch (Exception e) { e.printStackTrace(); } } @Override public void ...
  • MPU6050原始数据分析——学习笔记

    千次阅读 2020-08-04 14:41:12
    MPU6050原始数据分析——学习笔记个人学习笔记MPU6050简介原始数据分析加速度计陀螺仪 个人学习笔记 用于记录自己学习的成果,并且分享给大家一起看看。希望对看到这篇的朋友有所帮助。 MPU6050简介 MPU-6050是一款6...
  • 在前面:大家发表文章或者储存数据等,可能会用到NCBI。那么,到底怎么上传呢?初传着可能会碰到各种疑问,可能试了好多次,无果,咨询同门仍是无果,大家涉及的内容有动植物微生物等,转录组、基因组等等不一,...
  • Linux原始套接字抓取底层报文

    千次阅读 热门讨论 2018-12-16 00:08:09
    1.原始套接字使用场景  我们平常所用到的网络编程都是在应用层收发数据,每个程序只能收到发给自己的数据,即每个程序只能收到来自该程序绑定的端口的数据。收到的数据往往只包括应用层数据,原有的头部信息在传递...
  • Android 获取GNSS原始数据

    千次阅读 热门讨论 2020-04-05 12:03:17
    2016 年 5 月,Google 在 I/O 开发者会议上宣布,将为 Android Nougat 操作系统中的应用程序提供原始 GNSS 观测数据。 API参考(Google中文官网)DEMO参考(GNSSLogger) 1. 用到的类 类 注释 ...
  • 欢迎使用Markdown编辑器博客本Markdown编辑器使用StackEdit修改而来,用它博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML序列图和流程图 ...
  • 【Java】原始数据类型与其包装类

    千次阅读 热门讨论 2018-04-01 21:23:40
      Java语言中默认定义了8个原始数据类型,大致可以分为4类: 整型:包含byte、short、int、long 浮点型:float、double 布尔型:boolean 字符:char   这8个基本的数据类型如果在声明的时候没有初始化,...
  • AIS原始数据

    万次阅读 热门讨论 2016-07-14 17:28:55
    最近做AIS相关项目,费了好大劲搜集了一些AIS原始数据,汇总之后共享给大家,希望能够帮到大家!  List<string> aisStr = new List()  {  "!AIVDO,1,1,0,A,B2@0F6P003?8mP=18D0000E3QP06,0*3C\n",  "!...
  • swift 对枚举的进行了更加灵活的实现,比如支持关联值的枚举,还有可以设置原始值的枚举。这都扩展了枚举类型的用途。下面我们就来品味下枚举以及它在 swift 中的实现吧。 枚举定义语法 首先,我们来看看在 swift 中...
  • 基于原始套接字编程

    千次阅读 2015-09-06 16:23:21
    基于原始套接字编程  在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证:  也就是说,对于TCP或UDP的程序开发,焦点在Data字段,我们没法...
  • 修改input file 原始样式,img标签/button覆盖原始上传样式,input file @change传值input file 原始样式很...证件照片,后台处理返回结果前端自动反input file 原始样式效果如下:img标签覆盖原始上传文件样式CSS...
  • 使用Retrofit获取原始返回的json数据

    千次阅读 2019-09-30 15:40:38
    call.enqueue(new Callback() { @Override public void onResponse(@Nullable Call<ResponseBody> call, @NonNull Response<ResponseBody> response) { try { //把原始数据转为字符串 String jsonStr = new ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 729,458
精华内容 291,783
关键字:

原始的是怎么写的