java nio SocketChannel 底层编程踩坑(适用于高级程序员)

闲着想做一个服务器之间的高速文件传输工具
突然发现了一些关于NIO的新坑,顺带把NIO这块会遇到的问题都整理一下

本次心得

1. NIO 其实不适合做 长连接通信,BIO更适合

因为NIO 的原理:由调度员(Selector) 把来客户端上送的数据缓冲到 SocketChannel通道中, 等待用户处理,处理好后,关闭来客连接,等待其他人上送数据
而BIO 更容易为每个客户保持一个长连接通道. 虽然NIO也可以做到,但是非常不方便.

2. 现在我非要用NIO做长连接,并上传大数据,就会遇到一系列问题

  • 避免重复实例化ByteBuffer
    NIO的数据是放在HeapByteBuffer 堆缓存中然后过渡给你自己定义的ByteBuffer ,这类对象不要重复实例化,因为没有必要 ,只要两个足矣,一个是读,一个是写, 我为了研究 故意用队列 插入了一堆接收到的Buf缓存,结果反而因为实例化更慢.

  • ByteBuffer 会被覆盖吗?
    如果读缓存只有一个,那接收数据时会不会被后续的新数据覆盖呢?
    答: 如果你没有及时处理堆缓存, 堆缓存是会被新数据覆盖, 我称为数据洪峰的影响,就像城市下暴雨,下水道来不及排水, 发生水涝一样.

  • 如何解决数据洪峰问题?
    在高速且连续发送大数据块的情况下,客户端是无法不丢失数据
    只有改进通信机制才可以解决, 有两种简单方案可以解决:
    1 每发送一批次 就 让对方应答,对方应答后,再发送下一批次
    2 每发送一批次 等待一段时间再发送第二批次

  • 通过延时发送的时间解决数据洪峰时,应该延时多久?
    其实我不建议用这种方法,
    因为如果你的缓冲区大,延时时间就要久一些,
    如果cpu处理的慢,那么延时也要久一些,
    这就像显微镜的细准焦螺旋,要调的适合才行,如果你不想调,
    可以粗暴的设置间隔时间在30ms以上

  • 应答方式解决数据洪峰 在nio架构中是比较难设计的,所以建议用bio

  • ByteBuffer 缓存的大小是有限定的,不能随意
    太小 传输次数多, 传输时长变长
    太大 连续发送大数据时会导致数据丢失(这点是我多次实验才发现的)
    不同计算机因为网卡和内存,磁盘,cpu等不同,会导致ByteBuffer 最佳尺寸有不同,

  • 为了适应 网络卡,或者信号不好的环境, ByteBuffer的 尺寸建议 512~2k

  • 局域网 1千兆带宽的环境, ByteBuffer的 尺寸建议 16~64k

  • 一定要用while {}来取缓存数据
    SocketChannel通道切换后, 会用read取数据到缓存( ByteBuffer),这个时候一定要用while, 因为通道的数据用read()一次,是无法取完的,只能while才能取干净.

  • 网络上经常说NIO是同步且非阻塞的,这一点不能盲信.
    同步是有条件的! 前提是接收方要来得及处理.
    非阻塞的 前提也是接收方要来得及处理. NIO割裂了发送和反馈. 从发送到反馈这个过程如果要花较长时间,那也会造成数据洪涝现象.

总结: NIO也好BIO也好,只要接收方处理来不及都会出问题

网络编程常见问题

粘包

粘包就是两个数据包收到时连在一起,怎么分割?
答: 传统解决方法有两种:
例如单片机领域喜欢用时序脉冲, 间隔一段时间收取;
而在高级编程领域喜欢用通信协议, 比如协议在包头定义每个包的大小,这样按照包头信息指示,读取指定长度的包体就可以了.

丢包

很早以前只知道UDP会丢包,其实TCP也会丢包,只是TCP丢包是困难的
TCP丢包的常见原因有4种:

  1. 就像之前说的 数据洪峰来临 ,导致客户端无法及时处理丢包
  2. 网络差, 网络断断续续,导致数据离奇丢失(非常少见)
  3. 伪代理基站或路由器被植入木马,篡改TCP包数据
  4. 程序有bug ,发送与接收的处理不严谨,导致
    丢包处理需要针对性设计:
    数据洪峰的解决方案前面写过了;
    数据被篡改,需要记录日志,具体问题具体分析,甚至要改动程序安全架构;
    非黑客原因直接断开客户端,等待客户重连即可;

半包

半包就是你当前只收到一半的数据,
另一半的数据在上一次已经收到,或在下一次接收时才可以收到.
这种问题,只能程序处理,解决方案:
就是实例化一个半包管理器,对半包进行处理

坏包

坏包,来源不明,包格式非协议约定,导致无法解读,遇到这种情况,因为不知道坏包长度多少,所以最好办法断开客户端,等待重新连接后,再按照双方约定的通信协议 发送有效的数据包.

拆包

就是把一个大的数据包,分成若干固定大小的数据子包,最后一个子包的大小可以小于等于固定大小.

双通

数据从客户端发送到服务端, 看上去有一条单通道, 实际上这个通道是双向的, 服务端可以利用该通道把数据发回给客户端.

数据重发

就是数据包坏了,如何重新发送.
如果数据非常大,几个T 数据重发显然不现实
那么大数据如何数据重发,就要涉及到拆包,先把打包拆开,分开发,哪个错了就重新发哪个.
最后把所有收到的子包按照顺序重新组装成完整的包,这也需要算法支持

数据包并发

如果我们正在发送一个很大的包 有好几个G
那么期间能不能 穿插着发送其他数据呢?
就比如说我正在发送文件给对方,能不能在发送消息呢?
如果不拆包,肯定是不行的,只有拆包了,才可以在中途穿插发送其他的数据包.

总结:

拆包,应答,半包,粘包, 是网络编程必须解决的底层问题

你可能感兴趣的:(网络,java,编程,java,nio,开发语言)