jdk 源码 java.net包

661 阅读4分钟

我还有机会吗?

一、大名鼎鼎的net包

net包应该是比较出名的一个包了,这个包的内容不复杂,但是从这个包的java实现中,可以更好的网络编程的相关知识,熟悉这个包的相关知识后,遇到相关的网络异常可以瞬间定位到源码级别,cool。

二、Socket

Socket结构是对网络操作的抽象,主要的方法如下

  • connect 连接服务端
  • close 关闭socket
  • bind 设定客户端的端口,如果不绑定的话,客户端会随机生成一个端口号
  • setImpl 这个方法在jdk内部会经常使用,主要是用来设置socket的实现

下面的内容 是windows平台下的jdk实现
一个socket的通信通过五元组进行标识(协议,本地地址,本地端口号,远地地址,远地端口号)。connect操作前必须设置好这个五元组标识

Socket的具体实现都在SocketImpl这个方法中,Socket只一个简化操作调用的对外接口。

SocketImpl实现采用了适配器模式,如下图:

PlainSokcetImpl是一个适配器模式,里面的具体实现是DualStackPlainSocketImpl、TwoStacaksPlainSocketImpl,具体的实现(native方法)都放在这个两个类里面,可以简单的理解成这个两个类会调用os提供的函数库

在看下面这个类

SocksSokcetImpl 这个类继承了PlainSocketImpl。

看看下面的一个connection 异常,刚好能够体现各种调用关系

完美反应了调用关系,perfect!总结起来就是SocksSocket-->PlainSocketImpl-->AbstractSocketImpl->DualStackPlainSocketImpl(具体的实现,里面有对应的native方法)

三、关于文件描述符

这一块实在是太有趣了,accept进行阻塞式监听。每当监听到一个连接进来时,就会创建一个FD(file description)文件描述副。这个fd提供本次本次的通信。通信完毕后,这个描述符号需要进行手动关闭。客户端在进行连接的时候(生成SocketImp对象)也会创建FD。实现方式如下图

每个连接都会打开要给文件描述符,文件描述符的个数是有限制的,超出限制会报错,linux平台可以通过ulimit 命令进行调节

注意:只有在进行connection的时候,代表连接的文件描述符才真正创建成功。在linux环境下可以在/tcp/$/fd 下面查看对应的文件描述符。tcp建立三次握手的过程最终结果便是创建了这个socket文件。socket套接字一定时是(schema,sourceIp,sourcePort,destIp,destPort )形式,建立连接就是创建文件(linux下面)。

四、常见的Socket异常

socket中常常抛出的都是IOException的子类,数据checkException,需要在程序中指定跑错时候的处理方式

绑定时异常: 

  • BindException : 绑定异常,客户端或者服务端启动的时候如果两个端口重复会抛出这个异常

connect时候的异常

  • SocketException : 由远程服务端口已经关闭、当前socket已经关闭、ip:port不正确、已经连接的socket、Connection Reset 
  • ConnectException:连接时,服务端连接不通,会出现这个异常

五、相关本地方法

   /**   
    * @Param fd 文件描述符    
    * @Param localAddress 本机地址    、
    * @Param localPort 本地端口   
    */    
    static native void bind0(int fd, InetAddress localAddress, int localport,boolean exclBind) throws IOException;  
  /**创建对应socket
     @stream true 表示tcp,false表示udp
     @param v6only
     @resturn 返回一个文件描述符
     */
    static native int socket0(boolean stream, boolean v6Only) throws IOException; //connect操作,需要对应文件描述符
    /**     * 进行tcp三次握手建立连接
     * @Param fd 文件描述符
     * @Param remote 远程ip
     * @Param remotePort 远程端口
     */
    static native int connect0(int fd, InetAddress remote, int remotePort)throws IOException;
    /**
     * 服务端进行监听操作
     * @Param fd 文件描述符
     * @Param isaa 服务端监听的地址信息
     */
    static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException;
    /**
     * 进行监听设置
     * @Param fd 文件描述符
     * @Param backlog 队列大小
     */
   static native void listen0(int fd, int backlog) throws IOException;}

六、finalize方法

Socket中的AbstractPlainSocketImpl 中有对应finalize方法,会关闭socket对应的文件描述符信息。所以这个地方会有fianlize方法的共有问题,Socket对象不对立即被垃圾回收期回收,大量的Socekt连接,可能会导致频发的垃圾回收

七、URLConnection 

java之父写的类,值的好好深入里面的设计细节。

URL是统一资源定为符号,是URI的一种类型,简单来说就是通过一个固定格式的字符串来标示资源。

根据schema的不同,jdk 里面提供了HttpURLConnection、FtpURLConnection。

八、jdk自带的 Http 服务

九、Restmplate

这个是spring提供的http请求服务,底层还是基于socket进行开发的。

十、总结

涉及网络的东西,不会的知识点真多。看着看着,能把以前的一些背的概念慢慢清晰化

  • 文件描述符,一个进程打开的文件描述符时有限制的
  • jdk 里面的native方法很有意思,好多native方法都带有数字0结尾,

写的比较随意,内容比较零散,对网络编程相关的知识需要加强