ReadableByteChannel 在读取(bytebuffer)时挂起

发布于 2024-12-02 21:19:46 字数 3082 浏览 1 评论 0原文

我正在使用 java 1.6 开发即时通讯软件。 IM 使用多线程 - 主线程、接收和 ping。对于 tcp/ip 通信,我使用了 SocketChannel。从服务器接收更大的包裹似乎存在问题。服务器而不是服务器发送了几个包,这就是问题开始的地方。前 8 个字节表示包的类型以及包的大小。这就是我管理阅读的方式:

public void run(){
    while(true){
        try{
        Headbuffer.clear();
        bytes = readChannel.read(Headbuffer); //ReadableByteChannel
        Headbuffer.flip();
        if(bytes != -1){
            int head = Headbuffer.getInt();
            int size = Headbuffer.getInt();
            System.out.println("received pkg: 0x" + Integer.toHexString(head)+" with size "+ size+" bytes);
            switch(head){
            case incoming.Pkg1: ReadWelcome(); break;
            case incoming.Pkg2: ReadLoginFail();break;
            case incoming.Pkg3: ReadLoginOk();break;
            case incoming.Pkg4: ReadUserList();break;
            case incoming.Pkg5: ReadUserData();break;
            case incoming.Pkg6: ReadMessage();break;
            case incoming.Pkg7: ReadTypingNotify();break;
            case incoming.Pkg8: ReadListStatus();break;
            case incoming.Pkg9: ChangeStatus();break;
            }
        }
        }catch(Exception e){
            e.printStackTrace();
        }
    }   
}

在测试期间一切都很好,直到我登录我的帐户并导入我的好友列表。我向服务器发送状态请求,他给我发回了 80 个联系人中的大约 10 个。所以我想出了这样的事情:

public synchronized void readInStatus(ByteBuffer headBuffer){

    byteArray.add(headBuffer); //Store every buffer in ArrayList
    int buddies = MainController.controler.getContacts().getSize();
    while(buddies>0){

          readStuff();
          readDescription();
        --buddies; 
    }       
}

每个 readStuff() 和 readDescription() 都检查每个参数大小以及缓冲区中的剩余字节:

 if(byteArray.get(current).remaining() >= 4){

      uin = byteArray.get(current).getInt();
      }else{
          byteArray.add(Receiver.receiver.read());
          current = current +1;
          uin = byteArray.get(current).getInt(); 
      }

并且 Receiver.receiver.read() 是:

public ByteBuffer read(){
    try {
        ByteBuffer bb = ByteBuffer.allocate(40000);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bytes = readChannel.read(bb);
        bb.flip();
        return bb;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

所以应用程序被启动,记录,然后发送联系人。服务器只发回我的列表的一部分。但在方法 readInStatus(ByteBuffer headBuffer) 中,我尝试强制列表的其余部分。现在有趣的部分 - 一段时间后它到达 Receiver.receiver.read() 并在 bytes = readChannel.read(bb) 上它就停止了,我没有知道为什么,没有错误,没有任何事情,即使过了一段时间,我也没有了想法。我整整一周都在奋斗,但我没有找到解决方案。我将不胜感激任何建议。谢谢。


感谢您的回复。是的,我正在使用阻塞 SocketChannel,我尝试过非阻塞,但它变得疯狂并且失控,所以我跳过了这个想法。关于我期望的字节 - 这有点奇怪,因为它只在头部给我一次大小,但它的第一部分的大小而不是整个包,其他部分根本不包含头字节。我无法预测它会有多少字节,原因是 - 具有 255 字节容量的描述。这正是我在以下位置创建变量 buddies 的原因: public synchronized void readInStatus(ByteBuffer headBuffer) 这基本上是我的好友列表的长度,在读取每个字段之前,我会检查是否还有足够的字节,如果没有,我会执行read()。但描述之前的最后一个字段是传入描述长度的整数。但在完成某些处理之前,无法确定包的长度。 @robert你认为在这种情况下我应该再次尝试切换到非阻塞SocketChannel吗?

Im working on Instant messenger using java 1.6. IM uses multithreading - main thread, receiving, and ping. For tcp/ip communication I used SocketChannel. And it seems there is a problem with receiving bigger packages from server. Server instead of one sends a couple of packages and thats where the problem begins. Every first 8 bytes is telling what is the type of package and how big it is. This is how I managed reading:

public void run(){
    while(true){
        try{
        Headbuffer.clear();
        bytes = readChannel.read(Headbuffer); //ReadableByteChannel
        Headbuffer.flip();
        if(bytes != -1){
            int head = Headbuffer.getInt();
            int size = Headbuffer.getInt();
            System.out.println("received pkg: 0x" + Integer.toHexString(head)+" with size "+ size+" bytes);
            switch(head){
            case incoming.Pkg1: ReadWelcome(); break;
            case incoming.Pkg2: ReadLoginFail();break;
            case incoming.Pkg3: ReadLoginOk();break;
            case incoming.Pkg4: ReadUserList();break;
            case incoming.Pkg5: ReadUserData();break;
            case incoming.Pkg6: ReadMessage();break;
            case incoming.Pkg7: ReadTypingNotify();break;
            case incoming.Pkg8: ReadListStatus();break;
            case incoming.Pkg9: ChangeStatus();break;
            }
        }
        }catch(Exception e){
            e.printStackTrace();
        }
    }   
}

And during the tests everything was fine until i logged on my account and import my buddylist. I send request to server for statuses and he send me back about 10 out of 80 contacts. So I came up with something like this:

public synchronized void readInStatus(ByteBuffer headBuffer){

    byteArray.add(headBuffer); //Store every buffer in ArrayList
    int buddies = MainController.controler.getContacts().getSize();
    while(buddies>0){

          readStuff();
          readDescription();
        --buddies; 
    }       
}

and each readStuff() and readDescription() are checking each parameter size with remaining bytes in the buffer:

 if(byteArray.get(current).remaining() >= 4){

      uin = byteArray.get(current).getInt();
      }else{
          byteArray.add(Receiver.receiver.read());
          current = current +1;
          uin = byteArray.get(current).getInt(); 
      }

and Receiver.receiver.read() is:

public ByteBuffer read(){
    try {
        ByteBuffer bb = ByteBuffer.allocate(40000);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bytes = readChannel.read(bb);
        bb.flip();
        return bb;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

So application is lunched, logged and then sends contacts. Server send me back just a piece of my list. But in the method readInStatus(ByteBuffer headBuffer) I try to force the rest of the list. And now the fun part - after some time it gets to the Receiver.receiver.read() and on bytes = readChannel.read(bb) it just stops and I dont know why , no errors no nothing even after some time and Im out of the ideas. Im fighting with this whole week and i dont get anywhere near the solution. I will appreciate any suggestions. Thanks.


Thanks for response. Yes, I'm using blocking SocketChannel, I tried non-blocking but it goes wild and out of control so I skipped the idea. About the bytes I expect - this is kind of weird, because its giving me size only once in head but its size of the first part not the whole package, other parts is not containing header bytes at all. I can't predict how much bytes it would be, the reason is - descriptions with 255 bytes capacity. This is exactly why I've created variable buddies in : public synchronized void readInStatus(ByteBuffer headBuffer)
wich is basically length of my buddy list and before reading each field I'm checking if there is enough bytes left if its not, I do read(). But last field before description is integer with the length of the incoming description. But its impossible to determine how long package is, until some processing is done. @robert do you think I should try again switching to non-blocking SocketChannel in that situation ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冷清清 2024-12-09 21:19:46

问题很可能是您发送的字节数少于您尝试读取的字节数。您可能错过了一些内容、写错了顺序、误读了尺寸字段或类似的内容。

我想我可以通过添加跟踪代码来计算和记录读取和写入的字节数、名义数据包大小等来解决这个问题。然后运行并比较跟踪,看看哪里开始不同步。

The problem is most likely that you are sending fewer bytes than you are trying to read. You might have missed writing something, written things in the wrong order, misread a size field or something like that.

I think I'd attack this problem by adding tracing code to count and log the number of bytes read and written, notional packect sizes and so on. Then run, and compare the traces to see where things start to get out of sync.

我也只是我 2024-12-09 21:19:46

如果您使用阻塞 SocketChannel,则读取将阻塞,直到缓冲区被填满或服务器传送流结束。对于具有连接保持活动状态的服务器,服务器不会发送流结束 - 它只会停止发送数据,并且读取将无限期挂起或直到超时。

您可以:
(i) 尝试使用非阻塞 SocketChannel,重复读取,直到读取传送 0 字节(但要注意 0 字节并不一定意味着流结束 - 它可能意味着中断)或
(ii) 如果您必须使用阻塞版本,并且您知道您期望从服务器(例如标头)获得多少字节,则当剩余读取的字节数小于 buffer.capacity() 时,移动位置和/或者对缓冲区进行限制,以便在读取之前在缓冲区中仅保留所需的空间。我现在正在研究这个解决方案。如果它对您有用,请告诉我!

据我所知,如果您必须使用阻塞 SocketChannel 并且您不知道需要多少字节,并且服务器不发送流结束,则没有解决方案。

If you are using a blocking SocketChannel, then read will block until the buffer is filled or the server delivers end of stream. For a server with connection keep-alive, the server does not send end of stream - it will simply stop sending data, and the read will hang indefinitely or until timeout.

You could either:
(i) try using a non-blocking SocketChannel, repeatedly reading until the read delivers 0 bytes (but beware 0 bytes does not necessarily mean end of stream - it could mean an interruption) or
(ii) if you have to use the blocking version, and you know how many bytes you were expecting from the server e.g. from a header, when the number of bytes left to read is less than buffer.capacity(), move position and/or limit on the buffer so as to leave only the required space in the buffer before the read. I am working this solution now. If it works for you, please let me know!

So far as I can work out, if you have to use a blocking SocketChannel and you do not know how many bytes you are expecting, and the server does not send end of stream, there is no solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文