小弟最近在做Socket通信程序,遇到Java中Read阻塞问题,一时想不到什么好的解决办法,恳请各位大侠指点一二,谢谢!具体问题如下:1、有什么办法能在读取Buffer时先知道Buffer里有多少字节的数据;
              2、有什么办法不但能读到Buffer里的数据,而且还不会阻塞,不像read函数那样,当没有数据时就一直等待,直到有数据读出了才跳过;据说采用NIO可以解决以上问题,小弟看了看,由于时间紧迫,所以一时半会也没有看明白太多,没能解决。恳请各位大侠指点指点,深表感谢!

解决方案 »

  1.   

    你所指的 Buffer 是什么?是 JDK 中的抽象类,还是 Socket 中的缓冲区?使用 Socket 在 read 阻塞是很正常,本身就是阻塞通信。另外,无法得知 Socket 缓冲区中有多少数据。
      

  2.   

    https://www6.software.ibm.com/developerworks/education/j-nio/j-nio-a4.pdf上面有代码片断可供参考,把它们拼接起来就可以了。
      

  3.   


    Buffer本来就是nio里的东西了
      

  4.   

    阻塞就阻塞吧,别玩NIO的非阻塞,那个东西比你现在要解决的问题复杂得多。
    别为一个小问题制造更大的麻烦。
      

  5.   

    Buffer就是缓冲区的意思了,Socket通信就是读写相应的缓冲区了
      

  6.   

    NIO的“通道”,都会有configureBlocking(boolean)这个方法,这个就是设置是否阻塞
    package com.soli.nio;import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;public class ServerNIO {
    public static void main(String[] args) throws Exception {
    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
    // ByteBuffer writeBuffer = ByteBuffer.wrap("服务器".getBytes());
    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
    writeBuffer.put("服务器".getBytes()); Selector selector = Selector.open(); InetSocketAddress address = new InetSocketAddress(9876); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(false);
    serverSocketChannel.socket().bind(address);
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("start"); while (true) {
    int temp = selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    System.out.println("keys.size()=" + temp);
    if (temp > 0) {
    for (Iterator<SelectionKey> it = keys.iterator(); it.hasNext();) {
    SelectionKey key = it.next();
    System.out.println(key);
    if (key.isAcceptable()) {
    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
    SocketChannel socketChannel = ssc.accept();
    socketChannel.configureBlocking(false);
    socketChannel.register(selector, SelectionKey.OP_READ);
    } else if (key.isReadable()) {
    SocketChannel socketChannel = (SocketChannel) key.channel();
    while (true) {
    readBuffer.clear();
    int r = socketChannel.read(readBuffer);
    System.out.println(r);
    if (r <= 0) {
    key.cancel();
    socketChannel.close();
    break;
    }
    System.out.println(new String(readBuffer.array()).trim());
    // writeBuffer.flip();
    // socketChannel.write(writeBuffer);
    }
    }
    it.remove();
    }
    }
    System.out.println("keys.size()=" + keys.size());
    }
    }
    }
    package com.soli.nio;import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;public class ClientNIO {
    public static void main(String[] args) throws Exception {
    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
    writeBuffer.put("客户端".getBytes());
    // ByteBuffer writeBuffer = ByteBuffer.wrap("客户端".getBytes());
    ByteBuffer readBuffer = ByteBuffer.allocate(1024); Selector selector = Selector.open(); SocketChannel socketChannel = SocketChannel.open();
    socketChannel.configureBlocking(false); InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 9876);
    System.out.println(socketChannel.connect(address)); while (!socketChannel.finishConnect())
    ;
    // socketChannel.register(selector, SelectionKey.OP_READ); writeBuffer.flip();
    socketChannel.write(writeBuffer);
    // while (true) {
    // int temp = selector.select();
    // System.out.println(temp);
    // if (temp > 0) {
    // Set<SelectionKey> keys = selector.selectedKeys();
    // for (Iterator<SelectionKey> it = keys.iterator(); it.hasNext();) {
    // SelectionKey key = it.next();
    // if (key.isReadable()) {
    // System.out.println("ok");
    // SocketChannel sc = (SocketChannel) key.channel();
    // int r = sc.read(readBuffer);
    // if (r <= 0) {
    // break;
    // }
    // System.out.println(new String(readBuffer.array()).trim());
    // }
    // it.remove();
    // }
    // }
    // }
    }
    }上面写得有点混乱,而且没带注析,带注析的那份在家请见量
      

  7.   

    用 SocketChannel 并设置非阻塞模式的话,比使用 Socket 复杂多了。特别是操纵 ByteBuffer 这个缓冲区。虽然 Socket 不如 SocketChannel 性能高,但是在开发上面简单很多。
      

  8.   

    7 楼的代码中,在 if (key.isReadable()) 中并不需要 while (true) 吧?
      

  9.   

    如果这样写的话,跟使用非阻塞通信没有区别,如果没有数据的话 read 会在那里阻塞。
      

  10.   

    那个应该需要吧,因为需要选择器来找对应的通道,而且那个while不是只为了一个通道服务,是为了所有连接过来的请求做服务
      

  11.   

    不会的,这样不会阻塞,那个是为了selector的循环
      

  12.   

    } else if (key.isReadable()) {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        while (true) {
            readBuffer.clear();
            int r = socketChannel.read(readBuffer);
            System.out.println(r);
            if (r <= 0) {
                key.cancel();
                socketChannel.close();
                break;
            }
            System.out.println(new String(readBuffer.array()).trim());
    //                            writeBuffer.flip();
    //                            socketChannel.write(writeBuffer);
        }
    }但是第一次在 isReadable() 为 true 的时候,就进入了 while(true) 死循环了,如果有数据读的话还好,但是如果没有数据读取的话就会在 read 那里阻塞掉。另外,当 r < 0 的时候,表示客户端已经断开了连接,这时候才会退出。难道你的客户端用的是短连接?客户端要发消息时去连接一下,连完之后就断开了?
      

  13.   


    selector.selectedKeys这句其实是阻塞语句,所以就算没请求进来,也不会一直循环至于你说r<0的情况,那个只是测试一下而已,具体怎么写,要看需求
      

  14.   

    但是你这样写的话,如果服务端要往 Socket 里写些东西就不好弄了,呵呵
      

  15.   

    服务器端如果想把数据传到客户端,其实只需要把数据写到buffer就好,并不是写到Socket里去当客户端需要查看服务端的信息的时候,可以在客户端发一个请求过去,或者定时查询,到那时候,服务端其实就是把buffer里面的东西传过去nio的Socket操作都不是对着Socket来操作的,都是对着buffer操作(应该说nio都是对buffer操作),然后当有请求过来的时候,由系统底层完成,所以说nio不阻塞,也就是因为这样
      

  16.   


    你服务端不用 socket.write(buffer) 的?
      

  17.   

        谢谢大家的答复,我已经解决了read函数阻塞问题,使用Socket.SetSoTimeout,设置读取的超时时间,如果读到数据,则继续执行,否则抛出异常。     但是,关于如何统计缓冲区中的数据字节数,还一直没有找到方法,好像没有似的,奇怪的很,还请有知道的指点一二,谢谢!
      

  18.   

    缓冲区的字节数,已经被封装起来了,程序员一般情况下看不到。
    最多调用available方法,来探测一下,下一次不受阻塞能够读取到的字节数。
    其他,未知。不知道,楼主要缓冲区的字节数干什么用 。