程序间达成的某种包含了信息交换的形式和意义的共识称为协议,用来实现特定应用程序的协议叫做应用程序协议。大部分应用程序协议是根据由字段序列组成的离散信息定义的,其中每个字段中都包含了一段以位序列编码(即二进制字节编码,也可以使用基于文本编码的方式,但常用协议如:TCP、UDP、HTTP等在传输数据时,都是以位序列编码的)的特定信息。应用程序协议中明确定义了信息的发送者应该如何排列和解释这些位序列,同时还要定义接收者应该如何解析,这样才能使信息的接收者能够抽取出每个字段的意义。TCP/IP协议唯一的约束:信息必须在块中发送和接收,而块的长度必须是8位的倍数,因此,我们可以认为TCP/IP协议中传输的信息是字节序列。

由于协议通常处理的是由一组字段组成的离散的信息,因此应用程序协议必须指定消息的接收者如何确定何时消息已被完整接收。成帧技术就是解决接收端如何定位消息首尾位置问题的,  由于协议通常处理的是由一组字段组成的离散的信息,因此应用程序协议必须指定消息的接收者如何确定何时消息已被完整。主要有两种技术使接收者能够准确地找到消息的结束位置:

1、基于定界符:消息的结束由一个唯一的标记指出,即发送者在传输完数据后显式添加的一个特定字节序列,这个特殊标记不能在传输的数据中出现(这也不是绝对的,应用填充技术能够对消息中出现的定界符进行修改,从而使接收者不将其识别为定界符)。该方法通常用在以文本方式编码的消息中。

2、显式长度:在变长字段或消息前附加一个固定大小的字段,用来指示该字段或消息中包含了多少字节。该方法主要用在以二进制字节方式编码的消息中。

由于UDP套接字保留了消息的边界信息,因此不需要进行成帧处理(实际上,主要是DatagramPacket负载的数据有一个确定的长度,接收者能够准确地知道消息的结束位置),而TCP协议中没有消息边界的概念,因此,在使用TCP套接字时,成帧就是一个非常重要的考虑因素(在TCP连接中,接收者读取完最后一条消息的最后一个字节后,将受到一个流结束标记,即read()返回-1,该标记指示出已经读取到了消息的末尾,非严格意义上来讲,这也算是基于定界符方法的一种特殊情况)。

下面给出一个自定义实现上面两种成帧技术的Demo(书上的例子),先定义一个Framer接口,它由两个方法:frameMag()方法用来添加成帧信息并将指定消息输出到指定流,nextMsg()方法则扫描指定的流,从中抽取出下一条消息。

import java.io.IOException;
import java.io.OutputStream; public interface Framer {
void frameMsg(byte[] message, OutputStream out) throws IOException;
byte[] nextMsg() throws IOException;
}

下面的代码实现了基于定界符的成帧方法,定界符为换行符“\n”,frameMsg()方法并没有实现填充,当成帧的字节序列中包含有定界符时,它只是简单地抛出异常;nextMsg()方法扫描刘,直到读取到了定界符,并返回定界符前面所有的字符,如果流为空则返回null,如果直到流结束也没找到定界符,程序将抛出一个异常来指示成帧错误。

import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; public class DelimFramer implements Framer { private InputStream in; // 数据来源
private static final byte DELIMITER = '\n'; // 定界符 public DelimFramer(InputStream in) {
this.in = in;
} public void frameMsg(byte[] message, OutputStream out) throws IOException {
for (byte b : message) {
if (b == DELIMITER) {
//如果在消息中检查到界定符,则抛出异常
throw new IOException("Message contains delimiter");
}
}
out.write(message);
out.write(DELIMITER);
out.flush();
} public byte[] nextMsg() throws IOException {
ByteArrayOutputStream messageBuffer = new ByteArrayOutputStream();
int nextByte; while ((nextByte = in.read()) != DELIMITER) {
//如果流已经结束还没有读取到定界符
if (nextByte == -1) {
//如果读取到的流为空,则返回null
if (messageBuffer.size() == 0) {
return null;
} else {
//如果读取到的流不为空,则抛出异常
throw new EOFException("Non-empty message without delimiter");
}
}
messageBuffer.write(nextByte);
} return messageBuffer.toByteArray();
}
}

下面的代码实现了基于长度的成帧方法,适用于长度小于65535个字节的消息。发送者首先给出指定消息的长度,并将长度信息以big-endian顺序(从左边开始,由高位到低位发送)存入2个字节的整数中,再将这两个字节存放在完整的消息内容前,连同消息一起写入输出流;在接收端,使用DataInputStream读取整型的长度信息,readFully()方法将阻塞等待,直到给定的数组完全填满。使用这种成帧方法,发送者不需要检查要成帧的消息内容,而只需要检查消息的长度是否超出了限制。

import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; public class LengthFramer implements Framer {
public static final int MAXMESSAGELENGTH = 65535;
public static final int BYTEMASK = 0xff;
public static final int SHORTMASK = 0xffff;
public static final int BYTESHIFT = 8; private DataInputStream in; public LengthFramer(InputStream in) throws IOException {
this.in = new DataInputStream(in); //数据来源
} //对字节流message添加成帧信息,并输出到指定流
public void frameMsg(byte[] message, OutputStream out) throws IOException {
//消息的长度不能超过65535
if (message.length > MAXMESSAGELENGTH) {
throw new IOException("message too long");
}
out.write((message.length >> BYTESHIFT) & BYTEMASK);
out.write(message.length & BYTEMASK);
out.write(message);
out.flush();
} public byte[] nextMsg() throws IOException {
int length;
try {
//该方法读取2个字节,将它们作为big-endian整数进行解释,并以int型整数返回它们的值
length = in.readUnsignedShort();
} catch (EOFException e) { // no (or 1 byte) message
return null;
}
// 0 <= length <= 65535
byte[] msg = new byte[length];
//该方法处阻塞等待,直到接收到足够的字节来填满指定的数组
in.readFully(msg);
return msg;
}
}

转自:http://blog.csdn.net/ns_code/article/details/14225541

【Java TCP/IP Socket】应用程序协议中消息的成帧与解析(含代码)的更多相关文章

  1. 【Java TCP/IP Socket】TCP Socket通信中由read返回值造成的的死锁问题(含代码)(转)

    书上示例 在第一章<基本套接字>中,作者给出了一个TCP Socket通信的例子——反馈服务器,即服务器端直接把从客户端接收到的数据原原本本地反馈回去. 书上客户端代码如下: 1 2 3 ...

  2. 【Java TCP/IP Socket】构建和解析自定义协议消息(含代码)

    在传输消息时,用Java内置的方法和工具确实很用,如:对象序列化,RMI远程调用等.但有时候,针对要传输的特定类型的数据,实现自己的方法可能更简单.容易或有效.下面给出一个实现了自定义构建和解析协议消 ...

  3. 【Java TCP/IP Socket】UDP Socket(含代码)

    UDP的Java支持 UDP协议提供的服务不同于TCP协议的端到端服务,它是面向非连接的,属不可靠协议,UDP套接字在使用前不需要进行连接.实际上,UDP协议只实现了两个功能: 1)在IP协议的基础上 ...

  4. 【Java TCP/IP Socket】TCP Socket(含代码)

    TCP的Java支持 协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构.交换方式.包含的意义以及怎样对报文所包含的信息进行解析,TCP/IP协议族有IP协议.TCP协议和UDP协议.现 ...

  5. 一个项目看java TCP/IP Socket编程

    前一段时间刚做了个java程序和网络上多台机器的c程序通讯的项目,遵循的是TCP/IP协议,用到了java的Socket编程.网络通讯是java的强项,用TCP/IP协议可以方便的和网络上的其他程序互 ...

  6. 《Java TCP/IP Socket 编程 》读书笔记之十一:深入剖析socket——TCP套接字的生命周期

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/16113083 建立TCP连接      新的Socket实例创建后,就立即能用于发送和接收 ...

  7. 【Java TCP/IP Socket】深入剖析socket——TCP套接字的生命周期

    建立TCP连接      新的Socket实例创建后,就立即能用于发送和接收数据.也就是说,当Socket实例返回时,它已经连接到了一个远程终端,并通过协议的底层实现完成了TCP消息或握手信息的交换. ...

  8. 【Java TCP/IP Socket】深入剖析socket——数据传输的底层实现

    底层数据结构 如果不理解套接字的具体实现所关联的数据结构和底层协议的工作细节,就很难抓住网络编程的精妙之处,对于TCP套接字来说,更是如此.套接字所关联的底层的数据结构集包含了特定Socket实例所关 ...

  9. 【Java TCP/IP Socket】深入剖析socket——TCP通信中由于底层队列填满而造成的死锁问题(含代码)

    基础准备 首先需要明白数据传输的底层实现机制,在http://blog.csdn.net/ns_code/article/details/15813809这篇博客中有详细的介绍,在上面的博客中,我们提 ...

随机推荐

  1. JAVA遍历map元素

    第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Ma ...

  2. Linux 中 MySQL 授权远程连接

    说明:当别的机子(IP )通过客户端的方式在没有授权的情况下是无法连接 MySQL 数据库的,如果需要远程连接 Linux 系统上的 MySQL 时,必须为其 IP 和具体用户进行授权.一般 root ...

  3. day14 迭代器,生成器,函数的递归调用

    1.什么是迭代器 迭代是一个重复的过程,但是每次重复都是基于上一次重复的结果而继续 迭代取值的工具 2.为什么要用迭代器 迭代器的优点 ​ ①不依赖于索引取值 ​ ②更节省内存 缺点: ​ 1.不如按 ...

  4. (转)WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

    [root@bak1 bak]# scp gwsyj.sql.gz root@192.168.21.65:/data/dbdata/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...

  5. (原)neuq oj 1022给定二叉树的前序遍历和后序遍历确定二叉树的个数

    题目描述 众所周知,遍历一棵二叉树就是按某条搜索路径巡访其中每个结点,使得每个结点均被访问一次,而且仅被访问一次.最常使用的有三种遍历的方式: 1.前序遍历:若二叉树为空,则空操作:否则先访问根结点, ...

  6. LeetCode(90) Subsets II

    题目 Given a collection of integers that might contain duplicates, nums, return all possible subsets. ...

  7. BZOJ 2508: 简单题

    题目大意: 加入直线,删除直线,求点到所有直线的距离的平方和. 题解: 把点到直线的距离公式写出来,然后展开.维护六个值,计算一个二元的多项式的最小值. 对x和y分别求导,导数都为零时取到极值.然后解 ...

  8. 1,python初识

    什么是变量? 变量:将程序的中间结果暂时存储起来,以便后续程序调用. 什么是字符串类型? python中被引号引起来的数据就是字符串.字符串类型,也简称str类型. 在python中 int是什么? ...

  9. Hive中文注释乱码解决方案

    本文来自网易云社区 作者:王潘安 快速解决方法 目前的hive客户端在执行desc tablexxx和show create table xxx命令的时候,字段的中文注释会出现乱码情况,如(????) ...

  10. iphone数据库(sqlite3)的用法操作oc,xcode

    首先你在用之前要在项目中加入libsqlite3.dylib 1.定义模型 [cpp] #import <Foundation/Foundation.h>   #import " ...