java源码剖析之socket(一)

    不知不觉又到了新的的一周,时间在悄悄的溜走,所辛的是自己也在缓慢的推进着自己的学习计划。 

    这周按照计划查看的是socket系列的相关类,尽管这之前就已经看过一遍,不过当时是越看越蒙,完全找不到北。 随着自己能力的提升,回过头来又去看一遍,还是看不懂其中的精髓,不过至少比起第一遍已经要好了很多很多。 

    按照惯例,先上图:

    《java源码剖析之socket(一)》

    从图中可以看出socket系列的类好像没有io流体系的类复杂。   但是实际上socket里面涉及的内容理解起来还是要难一些的。  接下来按照时间线进行一个学习的记述:

    首当其冲的是Socket,套接字:

     套接字为一个普通的java类,但是它的逻辑实现却主要依赖了一个以SocketImpl抽象类为基类的系列类,并且它的相关逻辑是默认托管给一个SocksSocketImpl(SocketImpl的后代类)的,同时SocksSocketImpl又将主要的逻辑托管给父类,最终由其兄弟类DualStackSocketImpl或者是TwoStackSocketImpl实现核心的业务代码,事实上是调用了native方法。                           

    Socket提供给了外部两种实例化方式,分别为面向连接的Socket实例,面向非连接的Socket实例。 其核心在于是否调用了本地的connect()方法。 

       《java源码剖析之socket(一)》

    《java源码剖析之socket(一)》

《java源码剖析之socket(一)》

 

    下面是面向连接的:

        《java源码剖析之socket(一)》

 

    《java源码剖析之socket(一)》

《java源码剖析之socket(一)》

    注意这个面向连接的Socket的实际构造方法为私有的,并不能被我们直接调用。 

    还有几个面向连接的构造方法,但都大同小异就不上图了。   总之吧,能够唯一确定一个终端,也就是ip地址和端口确定,那么都应该是面向连接的,除此之外应该是面向无连接的,不然不就报错了嘛哈哈,设计者早将我们可能遇到的问题给考虑进了,作为一个成熟的产品。 有一个例外就是,有个构造是只提供端口,但是也是也是面向连接的,它会将主机默认为本机localhost。   还有一种记的方法就是,面向非连接的socket只有三个,第一个是空参,第二个是代理,第三个是SocketImpl。 其余的就都是面向连接的。

    这里临时想到一个问题,那就是如果我们调用了SocketImpl的构造,那么我们实际上是为这个Socket准备了什么条件呢?  实际上通过看它们的继承和依赖关系图可以知道,SocketImpl系列的有很多,其中一级抽象的有:AbstractPlainSocketImpl,二级实现类有:DualSocketImpl,TwoSocketImpl,PlainSocketImpl。(它们同时为兄弟关系,但是在逻辑上却体现出父子关系,因为PlainSocketImpl实际上是在DualSocketImpl和TwoSocketImpl二者中选其一作为实际实现类,并且它们的选择关系是通过系统的版本确定的,因为系统版本支持的具体的协议栈有区别好像。)。 三级实现类有:SocksSocketImpl。   我们要作为这个构造方法的实参,那么传抽象类的可能性不大。实际上就算传抽象类,最终还是要走到实现类去。  因此考虑PlainSocketImpl和TwoSocketImpl。   那么应该可以大致猜测出来,其为Socket提供的是底层协议栈操作的方式或者一些相关的操作规则。至于为什么不能面向连接呢? 那就涉及到等会要介绍的依赖类了。

     在此之前补充一下Socket的另外几个我认为较重要的方法或注意的地方吧:

        《java源码剖析之socket(一)》

《java源码剖析之socket(一)》

    大体意思就是:通过toString方法可以看到这个socket的一些基本信息,同时在j2se1.3以前,所有的socket都是面向连接的。 

    接下来介绍socket的另一个主要的依赖类:InetAddress。 其全称应该为:InternetAddress,简写了还不太好识别出来。 

    依然先看图,包括这个类的官方说明:

        《java源码剖析之socket(一)》

    《java源码剖析之socket(一)》

        《java源码剖析之socket(一)》

    可以知道,它是作为一个ip地址提供socket资源,查看它的类声明,发现它为一个基本类,但是我感觉它用的时候相当于充当了一个抽象类的功能。  同时看类的继承依赖关系图可以知道:其有两个子类,Inet4Address和Inet6Address,分别对应ipv4地址和ipv6地址。  同时它的类静态成员也明确的表明了这一点,并且是通过int作为一个标识,来识别是ipv4还是ipv6。  这个类本身的构成也是比较复杂的,各种内部类,同时它的内部类有一点奇葩,它将自己也弄成了一个内部类,这将造成一种什么情况呢?那就是无限的递归,这也是我在调试的时候发现的这样的奇怪的问题,不知它是采用何种存储结构或者指针怎么指能够实现这种功能。在此就上图证明一下:

    《java源码剖析之socket(一)》

    《java源码剖析之socket(一)》

    这是在调试的某一步,查看其对象的组成。  事实上,这个impl对象可以一直打开下去,这就奇怪了,难道这样不是无限递归吗?哎,烧脑。 跳过继续介绍

    另一个比较重要的一点的内部类为:InetAddressHolder,好多类都有这种内部类,好像是一种设计模式,叫啥给搞忘了,以后有机会研究。 

        上图说明问题:

        《java源码剖析之socket(一)》

    这是InetAddress对外开放的其中一个构造方法,同时它是静态的,因此可以说是一个工具方法吧。 主要就是把主机名进行一些必要的标准化和将ip地址标准化,之后进行一个标准Inet4Address的构造或者Inet6Address的构造。 继续看它的子类,这里主要看一下ipv4:

    《java源码剖析之socket(一)》

    《java源码剖析之socket(一)》

    《java源码剖析之socket(一)》

    《java源码剖析之socket(一)》

    这几个方法主要做了这些事情:判断某个Ip地址是否为保留地址,如10.xxx.xxx.xxx,172.16.xxx.xxx,192,168.xxx.xxx,;将一个ip的byte数据通过移位操作,可以看到它是以字节(八位)进行操作的,将每个字节取出进行一个与运算,并以byte数组的方式返回;将Ip地址转化为我们平时看到的  xxx.xxx.xxx.xxx类型。 

       马上上课了,那就下午接着写。 

 

    原文作者:java源码分析
    原文地址: https://blog.csdn.net/qq_36285943/article/details/79923002
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞