我还有机会吗?
一、大名鼎鼎的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结尾,
写的比较随意,内容比较零散,对网络编程相关的知识需要加强