socket分块接收_Java TCP/IP Socket 编程 笔记(四)—发送和接收数据

1.TCP/IP协议要求信息必须在块(chunk)中发送和接收,而块的长度必须是8位的倍数,因此,我们可以认为TCP/IP协议中传输的信息是字节序列。如何发送和解析信息需要一定的应用程序协议。

2.信息编码:

首先是Java里对基本整型的处理,发送时,要注意:1)每种数据类型的字节个数;2)这些字节的发送顺序是怎样的?(little-endian还是big-endian);3)所传输的数值是有符号的(signed)还是无符号的(unsigned)。具体编码时采用位操作(移位和屏蔽)就可以了。具体在Java里,可以采用DataOutputStream类和ByteArrayOutputStream来实现。恢复时可以采用DataInputStream类和ByteArrayInputStream类。

其次,字符串和文本,在一组符号与一组整数之间的映射称为编码字符集(coded character set)。发送者与接收者必须在符号与整数的映射方式上达成共识,才能使用文本信息进行通信,最简单的方法就是定义一个标准字符集。具体编码时采用String的getBytes()方法。

最后,位操作。如果设置一个特定的设为1,先设置好掩码(mask),之后用或操作;要清空特定一位,用与操作。

3.成帧与解析

成帧(framing)技术解决了接收端如何定位消息的首位位置的问题。

如果接收者试图从套接字中读取比消息本身更多的字节,将可能发生以下两种情况之一:如果信道中没有其他消息,接收者将阻塞等待,同时无法处理接收到的消息;如果发送者也在等待接收端的响应消息,则会形成死锁(dealock);另一方面,如果信道中还有其他消息,则接收者会将后面消息的一部分甚至全部读到第一条消息中去,这将产生一些协议错误。因此,在使用TCP套接字时,成帧就是一个非常重要的考虑因素。

有两个技术:

1. 基于定界符(Delimiter-based):消息的结束由一个唯一的标记(unique marker)指出,即发送者在传输完数据后显式添加的一个特殊字节序列。这个特殊标记不能在传输的数据中出现。幸运的是,填充(stuffing)技术能够对消息中出现的定界符进行修改,从而使接收者不将其识别为定界符。在接收者扫描定界符时,还能识别出修改过的数据,并在输出消息中对其进行还原,从而使其与原始消息一致。

2. 显式长度(Explicit length):在变长字段或消息前附加一个固定大小的字段,用来指示该字段或消息中包含了多少字节。这种方法要确定消息长度的上限,以确定保存这个长度需要的字节数。

接口:

Java代码  收藏代码

import java.io.IOException;

import java.io.OutputStream;

public interface Framer {

void frameMsg(byte [] message,OutputStream out) throws IOException;

byte [] nextMsg() throws IOException;

}定界符的方式:

Java代码  收藏代码

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;//data source;

private static final byte DELIMTER=(byte)'\n';//message delimiter

public DelimFramer(InputStream in){

this.in=in;

}

@Override

public void frameMsg(byte[] message, OutputStream out) throws IOException {

//ensure that the message dose not contain the delimiter

for(byte b:message){

if(b==DELIMTER)

throw new IOException("Message contains delimiter");

}

out.write(message);

out.write(DELIMTER);

out.flush();

}

@Override

public byte[] nextMsg() throws IOException {

ByteArrayOutputStream messageBuffer=new ByteArrayOutputStream();

int nextByte;

while((nextByte=in.read())!=DELIMTER){

if(nextByte==-1){//end of stream?

if(messageBuffer.size()==0){

return null;

}else{

throw new EOFException("Non-empty message without delimiter");

}

}

messageBuffer.write(nextByte);

}

return messageBuffer.toByteArray();

}

}

显式长度方法:

Java代码  收藏代码

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 SHOTMASK=0xffff;

public static final int BYTESHIFT=8;

private DataInputStream in;// wrapper for data I/O

public LengthFramer(InputStream in) throws IOException{

this.in=new DataInputStream(in);

}

@Override

public void frameMsg(byte[] message, OutputStream out) throws IOException {

if(message.length>MAXMESSAGELENGTH){

throw new IOException("message too long");

}

//write length prefix

out.write((message.length>>BYTEMASK)&BYTEMASK);

out.write(message.length&BYTEMASK);

//write message

out.write(message);

out.flush();

}

@Override

public byte[] nextMsg() throws IOException {

int length;

try{

length=in.readUnsignedShort();

}catch(EOFException e){

//no (or 1 byte) message;

return null;

}

//0<=length<=65535;

byte [] msg=new byte[length];

in.readFully(msg);//if exception,it's a framing error;

return msg;

}

}

分享到:

2012-10-29 20:17

浏览 1044

评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值