zoukankan      html  css  js  c++  java
  • java网络编程

    BIO

    java的阻塞模型,阻塞的点有两个,就是accept,和接受用户的输入的时候

    package network;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class SocketIO {
        public static void main(String[] args) throws IOException {
            ServerSocket server = new ServerSocket(9090);
    
            System.out.println("服务端启动");
    
            Socket socket = server.accept(); //阻塞
    
            System.out.println("客户端的端口号是:"+socket.getPort());
    
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
    
            System.out.println(br.readLine()); //阻塞
            while (true){
    
            }
        }
    }
    

    然后尝试连接

    但是此时服务器只能处理一个请求,如果这时候再添加一个连接,是没有效果的

    客户端连接到java 的服务端是需要先和内核进行tcp三次握手创建连接的

    java中的一些代码其实有的是调用的是内核的封装好的方法
    new ServerSocket(9090);
    相当于

    socket() = 6fd
    bind(6fd, 9090)
    listen(6fd)
    

    Socket client(7fd) = server.accept(); 相当于 accept(6fd) ==> 7fd
    对于这种情况,通常是每来一个连接,创建一个线程来专门进行IO处理accept,这是最早的BIO
    但是线程的连接由于java本身的原因是不可以很多个的,也就没法收到很多的连接

    这个时候对于内核进行了升级,出现了NIO
    也就是内核允许在BIO出现的两个阻塞 不再进行阻塞

    NIO

    package network;
    
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.LinkedList;
    
    public class SocketNIO {
        public static void main(String[] args) throws IOException, InterruptedException {
            LinkedList<SocketChannel> clients = new LinkedList<>();
            ServerSocketChannel ss = ServerSocketChannel.open();
            ss.bind(new InetSocketAddress(9090));
            ss.configureBlocking(false); //设置非阻塞
            while(true){
                Thread.sleep(1000);
                SocketChannel client = ss.accept(); //这个时候不会阻塞
                if(client == null){
                    System.out.println("null----");
                } else {
                    client.configureBlocking(false);
                    int port = client.socket().getPort();
                    System.out.println("port: "+port);
                    clients.add(client);
                }
                //buffer这里是串行化公用一个buffer了,一般每个channel都有鸽子的ByteBuffer
                ByteBuffer buffer = ByteBuffer.allocateDirect(4096); //为读写的区域指定大小,这块区域可以读写,可以在堆里,也可以在堆外
                for(SocketChannel sc: clients){
                    int num = sc.read(buffer); //先把读到的东西放进buffer里面,这里也不会阻塞
                    if(num > 0){
                        buffer.flip();
                        byte[] bytes = new byte[buffer.limit()];
                        buffer.get(bytes);
    
                        String res = new String(bytes);
                        System.out.println(sc.socket().getPort()+": "+res);
                        buffer.clear();
                    }
                }
            }
        }
    }
    

    这里面现在就可以只有一个主线程来操作这个,其实也可以分为两个线程来
    一个主线程来不停地接受accept,通常较boss,
    另一个线程来不断地遍历这些客户端,处理他们的输入输出,通常较works

    NIO的问题:不断地遍历所有的客户来看他们是否有输入输出,假设现在有10000个连接,那么每次循环你都要循环这些连接,看他们有没有输入,但是当这些连接只有少数的给你发数据,这就会造成资源浪费

    这个时候如果有东西可以告诉你有哪些连接给你发数据就好了,没有发数据的就不管
    内核提供了这样的功能,就是多路复用器,比如select,poll,epoll.kqueue
    多路复用器的作用就是可以理解为监听那些连接给你有发数据,这样程序的复杂度就会少很多,
    因为这样的事情我交给了内核来做,内核来管理,java程序这边我只要一句话就行

    多路复用

    package network;
    
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.*;
    import java.util.Iterator;
    import java.util.Set;
    
    public class Select {
    
    
        private ServerSocketChannel server = null;
        private Selector selector = null;
        private int port = 9090;
        public void initServer() throws IOException {
            server = ServerSocketChannel.open();
            server.configureBlocking(false);
            server.bind(new InetSocketAddress(port));
            //将这个server注册到selector里面,并且制定了我想得到的东西
            server.register(selector, SelectionKey.OP_ACCEPT);
        }
        public void start() throws IOException {
            initServer();
            System.out.println("服务器启动了。。。");
            while(true){
                while(selector.select(0) > 0){
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while(iterator.hasNext()){
                        SelectionKey key = iterator.next();
    
                        iterator.remove();
    
                        if(key.isAcceptable()){ //第一次进来是还不能读的
                            acceptHandler(key);
                        } else if(key.isReadable()){
                            readHandler();
                        }
                    }
                }
            }
        }
        public void acceptHandler(SelectionKey key) throws IOException {
            ServerSocketChannel channel = (ServerSocketChannel) key.channel();
            SocketChannel client = channel.accept();
    
            client.configureBlocking(false);
    
            ByteBuffer buffer = ByteBuffer.allocate(8192);
            client.register(selector, SelectionKey.OP_READ, buffer);  //设置可以读
            System.out.println("新客户端  "+ client.getRemoteAddress());
        }
        public void readHandler(SelectionKey key) throws IOException {
            //通过这个key,我可以取到曾经的buffer和曾经的clinet
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear();
            int read = 0;
            while(true) {
                read = client.read(buffer);
                if(read > 0){
                    buffer.flip(); //把数据取出来,然后再返回给客户端
                    while(buffer.hasRemaining()) {
                        client.write(buffer);
                    }
                    buffer.clear();
                } else if(read == 0){
                    break;
                } else {
                    client.close();
                    break;
                }
            }
        }
    
        public static void main(String[] args) throws IOException {
            Select s = new Select();
            s.start();
        }
    }
    
    
    个人qq:835493858 有事联系我
  • 相关阅读:
    Webpack笔记(三)——一款破产版脚手架的开发
    Google C++命名规范
    视觉词袋模型(BOVW)
    机器学习之四:决策树
    Zernike不变矩
    互联网产品各阶段的标准流程文档
    机器学习之三:logistic回归(最优化)
    Python
    机器学习之二:K-近邻(KNN)算法
    Python学习之二:Python 与 C 区别
  • 原文地址:https://www.cnblogs.com/wpbing/p/14386045.html
Copyright © 2011-2022 走看看