一、背景说明最近有开发同学说Php在Java中调用接口报错。表现为如果参数比较大,比如56K,会报错,其他情况不会报错。要求它提供相应的参数,发现一个参数的长度是81360,对应十六进制的13DD0。通过抓包发现实际上传到Java的长度只有3DD0,也就是上图中的第7行。现在是什么状况?好吧,让我们从Hessian协议开始吧。Hessian中的字符长度只能是2个字节,即单个数据包最多只能传输65535个字节。如果长度超过65535,需要发送多个包。官方描述如下:string::=x52b1b0string::=Sb1b0::=[x00-x1f]::=[x30-x33]b0以UTF-8编码的16位unicode字符串。字符串以块的形式编码。x53('S')表示最终块,x52('R')表示任何非最终块。每个块都有一个16位无符号整数长度值。长度是16位字符的个数,可能与字节数不同。字符串块不能拆分代理项对。有几种情况:如果长度在0到1f(31)之间,直接附加字符串;如果是32-1023之间的其他编码,具体编码方式后面会跟码;1024-65535之间是另一种编码;如果长度大于65535,则前面的数据包都是R的第一个数据包,最后一个数据包是S的数据包,表示结束。比如字符串的长度是81360,应该这样包装:R,FFFF,<前66535个字符>S,3DD1,<后15825个字符>其中,不存在,它只是一个方便阅读作为间隔。详情请参考官方文档:http://hessian.caucho.com/doc...2.代码分析&解决方案先看Php代码实现:functionwriteString($value){$len=HessianUtils::stringLength($值);如果($len<32){返回包('C',$len)。$this->writeStringData($value);}elseif($len<1024){$b0=0x30+($len>>8);$stream=pack('C',$b0);$stream.=pack('C',$len);返回$stream。$this->writeStringData($value);}else{$total=$len;$流='';$tag='S';$流.=$标签.包装('n',$len);$stream.=$this->writeStringData($value);返回$流;可以看到在最后的else判断中,并没有判断剩余长度是否大于65535,从而导致了上面的问题。修改后的代码如下:functionwriteString($value){$len=HessianUtils::stringLength($value);如果($len<32){返回包('C',$len)。$this->writeStringData($value);}elseif($len<1024){$b0=0x30+($len>>8);$stream=pack('C',$b0);$stream.=pack('C',$len);返回$stream。$this->writeStringData($值);}elseif($len<65536){$total=$len;$流='';$tag='S';$流.=$标签.包装('n',$len);$stream.=$this->writeStringData($value);返回$流;}else{$left=$len;$偏移量=0;//数据包分为R包和S包$stream='';while($left>0){if($left>65535){$tag='R';$流.=$标签.包('n',65535);$stream.=$this->writeStringData(substr($value,$offset,65535));$抵消+=65535;$左-=65535;}else{$tag='S';$流.=$标签.包装('n',$左);$stream.=$this->writeStringData(substr($value,$offset,$left));$左=0;}}返回$stream;其实也可以参考Java代码的实现,com.caucho.hessian.io.Hessian2Output是用于Hessian包的,我们看一下它的字符串封装:while(length>0x8000){intsublen=0x8000;偏移量=_偏移量;if(SIZE<=offset+16){flushBuffer();偏移量=_offset;}//chunk不能以高代理项chartai结尾l=value.charAt(strOffset+sublen-1);if(0xd800<=tail&&tail<=0xdbff)sublen--;缓冲区[偏移量+0]=(字节)BC_STRING_CHUNK;缓冲区[偏移量+1]=(字节)(sublen>>8);缓冲区[偏移量+2]=(字节)(sublen);_偏移量=偏移量+3;printString(值,strOffset,sublen);长度-=sublen;strOffset+=sublen;}偏移量=_偏移量;if(SIZE<=offset+16){flushBuffer();偏移量=_偏移量;}if(length<=STRING_DIRECT_MAX){buffer[offset++]=(byte)(BC_STRING_DIRECT+length);}elseif(length<=STRING_SHORT_MAX){buffer[offset++]=(byte)(BC_STRING_SHORT+(length>>8));缓冲区[偏移量++]=(字节)(长度);}else{buffer[offset++]=(byte)('S');buffer[offset++]=(byte)(length>>8);缓冲区[偏移量++]=(字节)(长度);}_offset=偏移量;printString(value,strOffset,length);可以看到这里是以0x8000即32768是单个数据包中的最大字节数。如果大于32768,则连续阻塞S个包,其余按协议处理。当然实现比较复杂,引入了一个buffer,这里就不详细讨论了360Atlas生产环境使用心得Dubbo2.7试用FastDFS不同步怎么破