底层和基础的清晰得理解,是解决网络开发所必须得
两个程序系统进行通信,底层是Tcp/Ip协议
,我们开发应用程序是在这个Tcp/Ip协议之上,制定自己Application Protocol
,具体实现是有个encode 与 decode 的过程,最重要的是通信协议!
关于粘包和半包问题的讨论
之所以出现粘包和半包现象,是因为TCP当中,只有流的概念,没有包的概念. 可以使用UDP协议.
这样可以就可以区分每个包了.但是要确保包的丢失处理
.为了提到效率,可以考虑写一个滑动窗口进行收发包. 若采用TCP协议进行传输,就要将每个包区分开来.可以有三种方式.因为TCP是面向流的.流只有打开和关闭,你要用一个流传输多个包
,那就要向办法区分出每个包.
一:: 可以每次发送同样大小的包,过大的包不予发送,过小的包,后面部分用固定的字符'\0'进行填充.
二:: 将流按字符处理,抽出一个字符做转义字符(通常Java用'\'来做转义字符,比如"\n"表示换行).假如就设'\'为转义字符,发送方如果流当中出现'\',就在后面在追加一个'\',如果包结束,则用'\'做包的结束符.这样,在接收方,若读取一个单独的'\'或者流结束,就标示前面的内容构成一个包,如果连续读取两个'\',就将两个'\'用一个'\'进行替换.这样,就可以保证原来包中的信息不变,同时也能区分出每个包了.
三:: 在发送方发送一个包的时候,先将这个包的长度发送给对方(一般是4个字节表示包长),然后再将包的内容发送过去.接收方先接收4个字节,看看包的长度,然后按照长度来接收包,这样就不会出错了.
以上三种方法,是网络传输中经常用到的方法.后两种很常见.最后一种,在TCP长连接传输中应用最多.
综合以上的说法,就是要在TCP协议以上再封装一层协议,用来做分包的信息交换.
一些处理方法:
一个BUFFER,用于保存当前连接的读缓存
有数据时,Buffer = Buffer + DataIn,不停的接收
收完成后,开始解析Buffer,
根据包的协议,不停的解析Buffer,并形成一个个包进行处理,处理后,Buffer = Buffer - Data,并继续解包
完成。
JDK里面.BufferedReader是用来处理字符流的.在网络通信当中,一般不用这个类.
而用这个类来处理的.一般是我讲到的第二种处理方式.只不过,是用换行符作为包的分隔符(讲的第二方式比较通用,特例的情况下如果传输的是ACSII字符流,可以指定换行符为包的分隔符).接收端使用BufferedReader的readLine()方法.发送端在发送字符串之后,再追发一个换行符'\n',用于进行包的分隔.
可以试一下,这个效率会很高,而且,不会有粘包,半包的现象.
由于网络传输数据的不确定性(也就是说有可能传输的是图像,文件什么的),所以,一般直接使用InputStream这个类来操作.它的read方法都是阻塞读的.参数为byte数组的情况下,如果流没有结束,该方法直到byte数组填满才会返回.可以试一下,直接用InputStream这个类来处理.就不会出现使用BufferedReader读出半包的情况了.当然也不用再去递归补读了.
直接使用InputStream一般效率还是很高的,如果还要提高效率,那就要自己编写一个缓冲区了.使用多线程(线程数量3个就可以)并发处理.性能会显著提高的.
NIO 保证数据完整
IO与NIO最重要的区别是数据打包和传输的方式
。原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
面向流
的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。
面向块
的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
通道
是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。
Buffer
实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。
Channel
是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。
通道类型
通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream
或者 OutputStream
的子类), 而 通道
可以用于读、写或者同时用于读写。
因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。
异常情况
*比如说客户端发过来1024个字节.这样的话正好 requestLineBuffer 一次读到1024个字节.这时 responseSize==requestLineBuffer里面数据的大小. 这正是你需要的完整数据.
*如果客户端发送过来100个字节.这时responseSize<requestLineBuffer里面数据的大小.这时你就需要判断当读到100个字节的时候就不要再往下读了,即使再读的话也是空数据.
前两种情况正是你所希望的,responseSize<=requestLineBuffer里面数据的大小.
*如果客户端发送过来2048个字节,第一次最多也就读1024个字节的数据.这时responseSize>requestLineBuffer,你就要判断如果第一次没有把数据读完整的话,还要再去把剩下的1024个字节的数读过来.
这种情况也许你已经想到了.但下面还有2种诡异的情况.
*如果客户端发送过来 2048 个字节,但是分2次或多次发送过来,比如说前两次读到 1548 个字节,这时你判断发现还没有接收到完整数据但再去用int count = socketChannel.read(requestLineBuffer);读,就是读不到数据.这样的话你就需要把这次读到的数据保存下来,再注册一个读的事件去读,等到下次把剩下的 500 个字节数据读过来后再去处理逻辑.这样的情况就是socketChannel.read的数据并不是一次把数据读到的,而是分多次.
*最后一种情况就是 如果客户端发送过来 5000 个字节,但是每次请求的数据只有 1000 个字节,这说明客户端发送太快,或是服务器处理太慢,系统把这5次的请求的数据加到一起了.这时你就要把这5个请求一个一个的分别取出来一个一个的去处理相应的逻辑.
分享到:
相关推荐
- 调试代码以定位并修复可能出现的问题,如网络延迟、数据丢失等。 这个“java nio 聊天室源码”项目涵盖了NIO的多个核心概念,为学习和理解NIO提供了实际的应用场景。通过分析和理解这个项目,开发者不仅可以深入...
此外,对于缓冲区的管理,要确保正确地清空和填充,避免数据丢失或混乱。在处理大文件时,可以利用NIO的文件通道进行零拷贝操作,提高读写效率。 总的来说,MongoDB的开发结合NIO技术,可以构建出高性能、高并发的...
同时,为了防止数据丢失,还可以实现重传机制。 通过这种方式,NIO使得客户端之间的通信变得更加高效和灵活。它允许服务器处理大量的并发连接,而不需要为每个连接创建新的线程,从而降低了系统的资源消耗。此外,...
同时,为了防止数据丢失,可能需要使用Buffer进行缓冲,并确保所有数据都正确写入。 文件下载服务则相反,服务器使用FileChannel读取文件内容,然后通过Socket发送给客户端,客户端接收数据并写入本地文件系统。 ...
`sun.io.MalformedInputException`是Java的标准异常类`java.nio.charset.UnmappableCharacterException`和`java.nio.charset.IllegalCharSequenceException`的子类,它属于`IOException`家族,通常表明输入数据不是...
2. 错误处理和重试机制:在网络环境中,连接中断或数据丢失是常见的问题,需要有适当的错误处理和重传策略。 3. 日志记录:为了调试和监控,系统应记录详细的日志信息。 4. 性能优化:如使用缓冲区减少频繁的I/O操作...
开发者需要对这些环节的编码解码规则有清晰的理解,以确保数据在整个生命周期中的一致性,从而避免乱码和数据丢失的问题。通过合理配置和使用相关API,可以有效地解决这些问题,保证应用的正常运行和用户体验。
redis哨兵架构及数据丢失问题分析 redis Cluster数据分布算法之Hash slot redis使用常见问题及性能优化思路 redis高可用及高并发实战 缓存击穿、缓存雪崩预防策略 Redis批量查询优化 Redis高性能集群之...
为了实现这些功能,开发者可能使用了Java的`java.io`和`java.nio`包进行数据读写,使用`java.net`包进行网络编程。此外,可能还涉及到`java.util.concurrent`包中的线程管理和同步工具。 总的来说,这个项目提供了...
备份过程涉及到复制这些数据到另一个位置,以便在原始数据丢失时恢复。Java可以通过JDBC(Java Database Connectivity)接口与各种数据库进行交互,执行SQL语句来导出和备份数据。 3. **JDBC**:JDBC是Java标准版...
UDP不进行连接建立,直接发送数据包,因此适用于实时性要求高但对数据丢失不敏感的应用,如在线游戏和视频流媒体。 Java Socket是Java API提供的用于实现TCP和UDP通信的接口和类。在Java中,`java.net.Socket`类...
这不仅可以释放系统资源,还能避免可能的数据丢失。不关闭流可能导致数据丢失或内存泄漏。 8. **NIO (New IO)**:Java 1.4 引入了NIO(Non-blocking Input/Output)框架,提供了更高效、非阻塞的I/O操作方式。虽然...
它通过建立三次握手的连接,确保数据的顺序发送和接收,以及丢失数据的重传,从而提供了高度可靠的数据传输服务。而UDP则是一种无连接的、不可靠的协议,它不保证数据的顺序或完整性,但其优点在于传输速度快,适合...
7. **IO与NIO**:Java的IO流用于读写数据,而NIO(非阻塞I/O)提供了选择器(Selector)、通道(Channel)等机制,提高了网络通信的效率。 8. **异常处理**:在设计网络通信系统时,必须考虑到网络中断、数据包丢失...
4. **NIO(非阻塞IO)**:Java NIO提供了一种更高效的IO模型,它允许程序在等待数据时不必阻塞,可以处理其他任务。Selector和Channel是NIO的核心组件,适用于高并发、低延迟的网络应用。 5. **TCP与UDP协议**:TCP...
在Java编程中,删除目录是一项常见的操作,尤其是在文件系统管理和应用程序清理方面。本教程将深入讲解如何使用Java API来删除目录及其包含的所有文件和子...记得在删除前进行适当的检查和确认,以防止意外的数据丢失。
- 聊天室通常基于TCP协议,因为它提供了一种可靠的数据传输方式,确保了数据的顺序性和无丢失。TCP连接是面向连接的,先建立连接再传输数据,保证了通信的稳定性。 5. **设计模式** - 为了实现多人聊天功能,可能...
TCP 还确保了数据按顺序到达,并且在数据丢失时能够重新发送。 - **特性:** 确认回应;分组序号;流量控制。 - **应用场景:** 适用于需要保证数据完整性和顺序的应用,如远程登录、文件传输等。 - **UDP (User ...
Java中通常使用`java.io`或`java.nio`包下的流类进行数据交换。例如,使用`BufferedReader`和`PrintWriter`进行文本数据的读写,或者使用`DataInputStream`和`DataOutputStream`处理二进制数据。 6. 关闭连接: ...
7. **IO与NIO**:掌握Java的输入输出流体系,理解阻塞IO和非阻塞IO的区别,以及NIO(New IO)在高并发场景下的优势。 8. **泛型**:理解泛型的基本概念,包括类型擦除、通配符、边界限定等,并能在代码中灵活运用。...