默认
发表评论 13
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
求助关于Mina框架文件传输时服务端解析数据出错的问题
阅读(66876) | 评论(13 收藏2 淘帖
1金币
因为服务器端是已经写好的,因还关联其他的项目代码不宜改动。现要写一个客户端向服务器端发送文件,但服务器端解析的时候要检查packet长度,总是报错。现在不知道问题是出在发送文件的方法有问题,还是传输的内容有问题,求大神赐教。
求助关于Mina框架文件传输时服务端解析数据出错的问题_erro.png

已经实现的服务端编码器


CharsetProtocolEncoderAdapter() {
        this.charset = Charset.forName("UTF8");;
    }

    public void encode(IoSession session, Object message, ProtocolEncoderOutput output) {
        ByteBuffer data = this.charset.encode((String) message);
        output.write(IoBuffer.wrap(data));
    }

已经实现的服务端解码器


public boolean doDecode(IoSession session, IoBuffer buffer, ProtocolDecoderOutput output) {
        if (this.decodeReady) {
            if (buffer.remaining() < this.frameInfoData.getCapLength()) {
                return false;
            } else {
                try {
                    byte[] data = new byte[this.frameInfoData.getCapLength()];
                    buffer.get(data);
                    output.write(Pair.of(this.frameInfoData, ByteBuffer.wrap(data)));
                } finally {
                    this.decodeReady = false;
                }
                return true;
            }
        } else if (buffer.remaining() < 16) {
            return false;
        } else {
            this.decodeReady = true;
            this.frameInfoData = new FrameInfoData();
            byte byteOrder = buffer.get(buffer.position() + 11);
            buffer.order(byteOrder == 1 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
            long seconds = UnsignedValue.toUint(buffer.getInt());
            long microsnd = UnsignedValue.toUint(buffer.getInt());
            int len = buffer.getInt();
            int capLength = len & 16777215;
            int packetLength = buffer.getInt();
            this.frameInfoData.timestamp(TimeUnit.SECONDS.toNanos(seconds) + TimeUnit.MICROSECONDS.toNanos(microsnd));
            this.frameInfoData.capLength(capLength);
            this.frameInfoData.packetLength(packetLength);
            return true;
        }
    }

自己实现的客户端编解码器


Encoder() {
        this.charset = Charset.forName("UTF8");
    }
    @Override
    public void encode(IoSession session, Object msg, ProtocolEncoderOutput out) throws IOException {
        ByteBuffer msgByte = this.charset.encode((String) msg);
        out.write(IoBuffer.wrap(msgByte));
    }

也有写过在文件头加入4*4个字节:second,microsecond,caplength, packetlength。但是结果依然是同样的错误。
public void encode(IoSession session, Object msg, ProtocolEncoderOutput out) throws IOException {      
        byte[] fileBytes=(byte[])msg;
        //The cap_len of a pcap packet does not include the pcap header, big-endian
        int capLen= fileBytes.length;
        //packet_len
        int packetSize = fileBytes.length +16;
        //second && microsecond
        Timestamp timeStamp = new Timestamp(System.currentTimeMillis());         
        long seconds = TimeUnit.NANOSECONDS.toSeconds(timeStamp.getTime());
        long micros = TimeUnit.NANOSECONDS.toMicros(timeStamp.getTime() - TimeUnit.SECONDS.toNanos(seconds));
        int capacity = fileBytes.length + 16;
        IoBuffer buffer = IoBuffer.allocate(capacity, false);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        buffer.setAutoExpand(true);
        buffer.putInt((int)seconds);
        buffer.putInt((int)micros);
        buffer.putInt(capLen);
        buffer.putInt(packetSize);
        buffer.put(fileBytes);
        buffer.flip();
        out.write(buffer);
        buffer.clear();
    }

public boolean doDecode(IoSession var1, IoBuffer in, ProtocolDecoderOutput out) throws CharacterCodingException {
         CharsetDecoder cd = Charset.forName("UTF8").newDecoder();  
         IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);  
         while(in.hasRemaining()){  
             buf.put(in.get());  
         }  
         buf.flip();  
         out.write(buf.getString(cd));   
         return false;  
    }

客户端文件发送


public void sessionOpened(IoSession session) throws Exception {
           System.out.println(session.getLocalAddress() + "   " + "sessionOpened");
            //客户端发送文件   
           byte[] fileBytes =toByteArray("/path/to/file/test.pcap");
           session.write(IoBuffer.wrap(fileBytes));
}
public static byte[] toByteArray(String filename) throws IOException {  
       FileChannel fc = null;  
       try {  
           fc = new RandomAccessFile(filename, "r").getChannel();  
           MappedByteBuffer byteBuffer = fc.map(MapMode.READ_ONLY, 0, fc.size()).load();  
           System.out.println(byteBuffer.isLoaded());  
           byte[] result = new byte[(int) fc.size()];  
           if (byteBuffer.remaining() > 0) {
               byteBuffer.get(result, 0, byteBuffer.remaining());  
           }  
           return result;  
       } catch (IOException e) {  
           e.printStackTrace();  
           throw e;  
       } finally {  
           try {  
               fc.close();  
           } catch (IOException e) {  
               e.printStackTrace();  
           }  
       }  
   }  

上一篇:mina2 运行一段时间报BufferUnderflowException 异常下一篇:各位对新装IM登录时有大量联系人(比如1W以上)的情况怎么处理的
推荐方案
评论 13
新手小白,不是很懂。它为什么在这里要定义65535,IP数据包最大值限制长度
我看你的代码很诡异,因为你说的只是需要客户端向服务端传文件,所以就是客户端的编码器跟服务端的解码器对上就行了。

但能看到你客户端的编码器里为何涉及到utf-8这种跟String内容有关的代码及操作,显然你两边只应该把所有数据内容作为byte来处理才合适(即传输时是无需理会具体内容,反正对于底层而言所有数据都是byte),写入文件时同样应该写入的是底层的byte。

你仔细检查一下代码。
引用:janesjardin 发表于 2017-01-05 17:23
新手小白,不是很懂。它为什么在这里要定义65535,IP数据包最大值限制长度

你文件传输用的肯定是TCP协议,对于应用层而言数据就是个“流”的概念,而且你处理的时候一定要把它作为最底层的byte处理,建议看下MINA的tcp传输数据的DEMO就知道了,传一个文件,就相当于持续传输一个很大的byte流而已,道理是一模一样的。
引用:JackJiang 发表于 2017-01-05 17:27
我看你的代码很诡异,因为你说的只是需要客户端向服务端传文件,所以就是客户端的编码器跟服务端的解码器对 ...

已经试过在encode里只传byte流,或在encode里没有代码只在opensession里传入文件byte,报错依然一样
引用:janesjardin 发表于 2017-01-05 17:38
已经试过在encode里只传byte流,或在encode里没有代码只在opensession里传入文件byte,报错依然一样

最快的解决方法就是,找到官方最简单的TCP传输2进制数据的方法,运行看看会不会出错,不会出错就把读写byte的那段改成从你的文件里读取byte并发送到服务端由服务端保存。

你上面这一大堆代码也不知道哪复制来的,无法证明是否能用,建议从最简单的试起。
用MINA传文件?都什么时代了,还用pc端时代的im实现方法
签名: 该会员没有填写今日想说内容.
我在做任务啊啊啊啊啊 啊啊啊啊 啊
引用:一地鼻血 发表于 2017-01-08 11:49
用MINA传文件?都什么时代了,还用pc端时代的im实现方法

这个就是pc项目啊,老项目没办法,现在来不及改
引用:janesjardin 发表于 2017-01-18 17:23
这个就是pc项目啊,老项目没办法,现在来不及改

原来如此
问题已解决,因为没有对上服务端的接收数据格式。 但是现在新的问题是在sessionOpened循环调用session.write会丢失一些数据,就是没有触发messageSend发送不出去,丢失的数据是随机的。有没有遇到类似问题的朋友?
引用:janesjardin 发表于 2017-01-19 10:48
问题已解决,因为没有对上服务端的接收数据格式。 但是现在新的问题是在sessionOpened循环调用session.writ ...

你在sessionOpened里write?这很奇怪吧
引用:JackJiang 发表于 2017-01-19 11:44
你在sessionOpened里write?这很奇怪吧

嗯,思考了版主的提议并参考了这个帖子,这个问题就解决了  TCP传输下的困境—记在《Mina实现自定协议传输》之后
引用:janesjardin 发表于 2017-01-19 15:15
嗯,思考了版主的提议并参考了这个帖子,这个问题就解决了  TCP传输下的困境—记在《Mina实现自定协议传 ...

Good job!
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部