V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX  ›  程序员

对于 NIO 的阻塞式通信,这么理解的对不

  •  
  •   amiwrong123 · 2020-03-14 17:21:13 +08:00 · 1173 次点击
    这是一个创建于 1517 天前的主题,其中的信息可能已经有所发展或是发生改变。
    package TestNIO;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    
    public class BlockingClient {
        public static void main(String[] args) throws IOException {
            SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
            FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
    
            ByteBuffer buf = ByteBuffer.allocate(1024);
            while (inChannel.read(buf) != -1) {
                buf.flip();
                sChannel.write(buf);
                buf.clear();
            }
            //sChannel.shutdownOutput();
            //inChannel.close();
            sChannel.close();
            while (true) {
                System.out.println("死循环");
            }
        }
    }
    
    package TestNIO;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    
    public class BlockingServer {
        public static void main(String[] args) throws IOException {
            ServerSocketChannel ssChannel = ServerSocketChannel.open();
            ssChannel.bind(new InetSocketAddress(9898));
            FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"),
                StandardOpenOption.WRITE, StandardOpenOption.CREATE);
    
            SocketChannel sChannel = ssChannel.accept();
            System.out.println("BlockingServer accept");
            ByteBuffer buf = ByteBuffer.allocate(1024);
            System.out.println("BlockingServer accept1");
            int i = 2;
            while (sChannel.read(buf) != -1) {
                System.out.println("BlockingServer accept"+i++);
                buf.flip();
                outChannel.write(buf);
                buf.clear();
            }
            System.out.println("already get data, and output to local file");
            sChannel.close();
            outChannel.close();
            ssChannel.close();
        }
    }
    

    如上,是两个最简单的例子。经过几次 debug,有以下理解(也不知道对不对):

    • 先执行服务端,服务端会一直阻塞在SocketChannel sChannel = ssChannel.accept()这里。
    • 再执行客户端,当客户端执行了SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));以后,服务端不阻塞在SocketChannel sChannel = ssChannel.accept()这里,继续往下执行。
    • 当服务端执行到(sChannel.read(buf)会阻塞在这里,直到客户端执行了一次sChannel.write(buf)后,服务端才会继续往下执行,经过循环,在下一次的(sChannel.read(buf)阻塞。
    • 当客户端执行了sChannel.shutdownOutput()或者sChannel.close()后,服务端不会继续在(sChannel.read(buf)阻塞,而此时返回值为-1,所以退出循环
    • 有一点不好理解,就是客户端的 SocketChannel 和服务端 accept 来的 SocketChannel,是同一个东西吗,我在客户端单方面执行了sChannel.close(),服务端那边的 SocketChannel 还在正常工作吗,假设现在把服务端加点代码:读出所有数据新建那个图片后,再向客户端发送数据,是不是就不能成功了。
    • 还有一种情况,万一服务端 单方面 close 了呢( sChannel 或者 ssChannel 两种情况)。
    2 条回复    2020-03-17 18:40:05 +08:00
    tairan2006
        1
    tairan2006  
       2020-03-15 11:03:07 +08:00 via Android
    哥们你应该先了解 socket 通信的基本知识再看代码
    Chinsung
        2
    Chinsung  
       2020-03-17 18:40:05 +08:00
    nio,还是 java,首先得 select 搞搞,看看具体的 key,你才能明白大概是个什么原理。
    建议找几个有图的看,然后自己照着例子试试 api。
    nio 主要解决的是从底层开始搞定用低线程数处理大量连接的问题(相比较同步 IO ),可以了解下 linux 的 epoll,select。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1123 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 18:48 · PVG 02:48 · LAX 11:48 · JFK 14:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.