0%

粘包、半包

定义

在网络传输中,粘包和半包应该是最常出现的问题。

TCP 传输中,客户端发送数据,实际是把数据写入到了 TCP 的缓存中,粘包和半包也就会在此时产生。

客户端给服务端发送了两条消息ABCDEF,服务端这边的接收会有多少种情况呢?有可能是一次性收到了所有的消息ABCDEF,有可能是收到了三条消息ABCDEF

上面所说的一次性收到了所有的消息ABCDEF,类似于粘包。如果客户端发送的包的大小比 TCP 的缓存容量小,并且 TCP 缓存可以存放多个包,那么客户端和服务端的一次通信就可能传递了多个包,这时候服务端从 TCP 缓存就可能一下读取了多个包,这种现象就叫粘包

上面说的后面那种收到了三条消息ABCDEF,类似于半包。如果客户端发送的包的大小比 TCP 的缓存容量大,那么这个数据包就会被分成多个包,通过 Socket 多次发送到服务端,服务端第一次从接受缓存里面获取的数据,实际是整个包的一部分,这时候就产生了半包(半包不是说只收到了全包的一半,是说收到了全包的一部分)。

阅读全文 »

为了避免逻辑判断泛滥导致的拓展性受限问题,Netty 发明了 pipelinechannelHandler 。它通过责任链设计模式来组织代码逻辑,并且能够支持逻辑的动态添加和删除 ,Netty 能够支持各类协议的扩展,比如 HTTP,WebSocket,Redis,靠的就是 pipelinechannelHandler

阅读全文 »

网络数据的基本单位总是字节。Java NIO 提供了ByteBuffer 作为它的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐。Netty 的ByteBuffer 替代品是ByteBuf,一个强大的实现,既解决了JDK API 的局限性,又为网络应用程序的开发者提供了更好的API。ByteBuf本质的原理就是引用了一段内存,这段内存可以是堆内也可以是堆外的,然后用引用计数来控制这段内存是否需要被释放,使用读写指针来控制对 ByteBuf 的读写,可以理解为是外观模式的一种使用。

阅读全文 »

JAVA NIO

概述

Java NIO 由以下几个核心部分组成:

  • Channels
  • Buffers
  • Selectors

虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类。

基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel,国内大多翻译成“通道”,有点像流。 不同的是,数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。

Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。

阅读全文 »

UDP

在 TCP/IP 协议的传输层除了一个 TCP 协议之外,还有一个 UDP 协议。UDP 协议是用户数据报协议的简称,也用于网络数据的传输。虽然 UDP 协议是一种不太可靠的协议,但有时在需要较快地接收数据并且可以忍受较小错误的情况下,UDP 就会表现出更大的优势。

下面是在 Java 中使用 UDP 协议发送数据的步骤。

  1. 使用 DatagramSocket() 创建一个数据包套接字。
  2. 使用 DatagramPacket() 创建要发送的数据包。
  3. 使用 DatagramSocket 类的 send() 方法发送数据包。

接收 UDP 数据包的步骤如下:

  • 使用 DatagramSocket 创建数据包套接字,并将其绑定到指定的端口。
  • 使用 DatagramPacket 创建字节数组来接收数据包。
  • 使用 DatagramPacket 类的 receive() 方法接收 UDP 包。
阅读全文 »

Linux IO模式

概念

用户空间与内核空间

现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对Linux操作系统而言,将最高的1G字节(从虚拟地址0xC00000000xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x000000000xBFFFFFFF),供各个进程使用,称为用户空间。

每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。

Linux 内核空间与用户空间_程序地带

阅读全文 »

Java Socket 编程

基础

首先需要了解两个类:Socket和SocketServer。

SocketServer

SocketServer实现了服务器套接字。 服务器套接字会等待通过网络进入的请求。 用户可以根据该请求执行某些操作,然后将结果返回给请求者(也可以选择不返回)。

阅读全文 »

【转】零拷贝技术详解

概述

考虑这样一种常用的情形:你需要将静态内容(类似图片、文件)展示给用户。那么这个情形就意味着你需要先将静态内容从磁盘中拷贝出来放到一个内存buf中,然后将这个buf通过socket传输给用户,进而用户或者静态内容的展示。这看起来再正常不过了,但是实际上这是很低效的流程,我们把上面的这种情形抽象成下面的过程:

1
2
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
阅读全文 »

JAVA IO流

基础

IO

数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入Input 和输出Output ,即流向内存是输入流,流出内存的输出流,统称为 IO流。 Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。

流代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象;流的本质是数据传输,我们根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。流的具体作用是数据源和目的地建立一个输送通道。

阅读全文 »

死锁

通俗的说,死锁就是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序产生严重影响。用来描述死锁的问题最有名的场景就是“哲学家就餐问题”。

阅读全文 »