java怎么处理socket服务端粘包

Python063

java怎么处理socket服务端粘包,第1张

socket粘包是避免不了的,主要在于接收方如何解包和控制。处理方法:

定制socket传输协议。增加包头、命令、数据长度、数据体、结束位。比如发送消息:,

String msg = "你好"

byte[] byBuffer = msg.getBytes()

//加入定制的协议该条数据位:

byte[] b = new byte[4+byBuffer.length]

b[0] = 0xFFFFF//随便定义,包头

b[1] = 0x01//命令

b[2] = byBuffer.length//数据长度

b[3 - n] = byBuffer//数据

b[b.length -1] = 0x0d//结束

接收方接收到该数据后判断包头是否一致,不一致则不要,根据b[2]数据长度来去数据,第一次未接收完继续接收第二次,直到接收数据长度==b[2]为止。一条完整的数据就出来了。

写得很随意,理解理解~

try

{

InputStream is = socket.getInputStream()

while(running)

{

/*

* 读取消息长度

*/

byte[] totalLen = new byte[4]

int readLen = 0//本次读取的字节

int position = 0//已经读取数据的下一个位置

while((readLen=is.read(totalLen,position,(4-position)))>=0)

{

position = position + readLen

if(position==4)

{

break

}

}

if(readLen<0)

{//读取到EOF,socket已close或reset

throw new SocketException("读取数据流结尾.")

}

int length = SGIP.byteArrayToInt(totalLen)

ByteBuffer mesg = ByteBuffer.allocate(length)

mesg.order(SGIP.getByteOrder())

mesg.put(totalLen)

//读取所有消息

readLen = 0

position = mesg.position()

while((readLen=is.read(mesg.array(), position, mesg.remaining()))>=0)

{

position = position + readLen

mesg.position(position)

if(mesg.remaining()==0)

{

break

}

}

if(readLen<0)

{//读取到EOF,socket已close或reset

throw new SocketException("读取数据流结尾.")

}

mesg.position(0)

//解析消息

mesg.order(ByteOrder.BIG_ENDIAN)

try

{

//解析mesg

}

catch (Exception e)

{

logger.error("语法错误出错,无法解析",e)

//接收到非法命令,断开连接

socket.close()

break

}

logger.debug(this.getName()+"退出")

C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的。本人使用的是自己开发的一套组件。

Java socket通信的组件也有很多,常用的大多数都是用的mina或者netty。游戏行业使用也是居多。

关于socket的底层写法,实在太多,我就不在BB。

这里我想说,C#和C++或者叫VC++把是使用小端序作为字节序。而java使用的是大端序作为字节序。

也就是说比如一个int占用四个字节,java的字节序和c#的字节序是相反的,java的int四个字节第一个字节在数组的最后一个。C#是第一个。

也就是说如果java端正常发送一个int的字节序给C#,需要翻转一次端绪。反之也是一样的。一句话来概括的话就是高位在前还是低位在前的问题。

C#输出数字 int 4 的字节序。为了保证c#下面绝对是是int所以加入了强制int转化。默认的话可能是byte

java的默认输出,这里使用的是netty的默认框架。进行的int4的字节序输出

高位和低位表示法完全不同。

java下面如果传输字符串,那么必须要先把字符串转化成byte数组,然后获取数组长度,在字节序里面压入int表示的数组长度,然后在然如byte数组。不管你的字符串多长。

而C#也是相同做法。但是唯一不同的是数组的长度表示法不同。微软经过了字节压缩的。用字节的前7位表示长度。第8位表示下一个字节是否也是表示长度的字节,值需要与128位于。

从而减少字节的消耗。

现在一般如果我们在java和C#中无论是哪一个语言作为服务器。架设socket通信基准。其中另外一方都要妥协字节序反转问题。

大多数情况下我们也许通信的要求不高,或许把一些类或者参数通过json格式化以后传输给对方。但是在这一条消息的传输中,一般会有两个int需要字节序。最少也要一个字节序。

一个字节序int表示消息长度。另外一个字节序表示消息协议。

如果消息协议都放到json里面没有问题。但是消息长度是必不可少的。因为你需要知道在网络环境中,消息压栈,然后等待系统发出是有可能两条消息一同发送的。也或者消息发送后由于网络阻塞,前后相差好几秒的消息同一时间达到。

这就是所谓的粘包。

我这里就不表演了。

还有另外一种通信方式,就是通过protobuf进行字节序的序列化,和反序列,官方支持java,第三方支持C#。这个组件可以减少字节流。达到省流量,减少网络资源消耗的问题。

例如一个long的类型值是1常规发送需要8个字节,64位。发送。如果改用protobuf的话只需要1字节8位就能发送。

同样的问题,无论你使用哪一种序列化方式,都需要消息长度和消息协议号。