三、深入分析javaweb技术内幕(网络IO和NIO)

前面我介绍了磁盘IO,但我们现在是互联网时代,肯定得有网络IO呀。先截段原文:

我们知道HTTP是基于TCP的,我们先来看一下TCP状态转换图:

TCP状态转换图:

了解TCP状态是有一定作用的。

影响网络传输的因素:

将一份数据 从一个地方正确地传输到另一个地方所需要的时间我们称之为 响应时间。影响这个时间的因素有很多:

Socket:

Java Socket的工作机制
Socket这个概念没有对应到一个具体的实体,它描述计算机之间相互通信的一种抽象功能。打个比方,可以把Socket比作两个城市之间的交通工具,有了它,就可以在城市之间来回穿梭了。交通工具有多种,每种交通工具也有相应的交通规则。Socket也一样,也有多种。大部分情况下我们用的都是基于TCP/IP的流套接字,它是一种稳定的通信协议。


建立通信链路
数据传输

NIO的工作方式

BIO带来的挑战
NIO的工作机制
下面放一个示例:
  1. package nio;  
  2.   
  3. import java.io.IOException;    
  4. import java.net.InetSocketAddress;    
  5. import java.nio.ByteBuffer;    
  6. import java.nio.channels.SelectionKey;    
  7. import java.nio.channels.Selector;    
  8. import java.nio.channels.ServerSocketChannel;    
  9. import java.nio.channels.SocketChannel;    
  10. import java.nio.channels.spi.SelectorProvider;    
  11. import java.util.Iterator;    
  12.     
  13. /**     
  14.  * TCP/IP的NIO非阻塞方式    
  15.  * 服务器端    
  16.  * */    
  17. public class Server implements Runnable {    
  18.     
  19.     //第一个端口       
  20.     private Integer port1 = 8099;    
  21.     //第二个端口       
  22.     private Integer port2 = 9099;    
  23.     //第一个服务器通道 服务A       
  24.     private ServerSocketChannel serversocket1;    
  25.     //第二个服务器通道 服务B       
  26.     private ServerSocketChannel serversocket2;    
  27.     //连接1       
  28.     private SocketChannel clientchannel1;    
  29.     //连接2       
  30.     private SocketChannel clientchannel2;    
  31.     
  32.     //选择器,主要用来监控各个通道的事件       
  33.     private Selector selector;    
  34.         
  35.     //缓冲区       
  36.     private ByteBuffer buf = ByteBuffer.allocate(512);    
  37.         
  38.     public Server() {    
  39.         init();    
  40.     }    
  41.     
  42.     /**    
  43.      * 这个method的作用  
  44.      * 1:是初始化选择器    
  45.      * 2:打开两个通道    
  46.      * 3:给通道上绑定一个socket    
  47.      * 4:将选择器注册到通道上    
  48.      * */    
  49.     public void init() {    
  50.         try {    
  51.             //创建选择器       
  52.             this.selector = SelectorProvider.provider().openSelector();    
  53.             //打开第一个服务器通道       
  54.             this.serversocket1 = ServerSocketChannel.open();    
  55.             //告诉程序现在不是阻塞方式的       
  56.             this.serversocket1.configureBlocking(false);    
  57.             //获取现在与该通道关联的套接字       
  58.             this.serversocket1.socket().bind(new InetSocketAddress(“localhost”this.port1));    
  59.             //将选择器注册到通道上,返回一个选择键       
  60.             //OP_ACCEPT用于套接字接受操作的操作集位       
  61.             this.serversocket1.register(this.selector, SelectionKey.OP_ACCEPT);    
  62.     
  63.             //然后初始化第二个服务端       
  64.             this.serversocket2 = ServerSocketChannel.open();    
  65.             this.serversocket2.configureBlocking(false);    
  66.             this.serversocket2.socket().bind(new InetSocketAddress(“localhost”this.port2));    
  67.             this.serversocket2.register(this.selector, SelectionKey.OP_ACCEPT);    
  68.     
  69.         } catch (Exception e) {    
  70.             e.printStackTrace();    
  71.         }    
  72.     
  73.     }    
  74.     
  75.     /**    
  76.      * 这个方法是连接    
  77.      * 客户端连接服务器    
  78.      * @throws IOException     
  79.      * */    
  80.     public void accept(SelectionKey key) throws IOException {    
  81.         ServerSocketChannel server = (ServerSocketChannel) key.channel();    
  82.         if (server.equals(serversocket1)) {    
  83.             clientchannel1 = server.accept();    
  84.             clientchannel1.configureBlocking(false);    
  85.             //OP_READ用于读取操作的操作集位       
  86.             clientchannel1.register(this.selector, SelectionKey.OP_READ);    
  87.         } else {    
  88.             clientchannel2 = server.accept();    
  89.             clientchannel2.configureBlocking(false);    
  90.             //OP_READ用于读取操作的操作集位       
  91.             clientchannel2.register(this.selector, SelectionKey.OP_READ);    
  92.         }    
  93.     }    
  94.     
  95.     /**    
  96.      * 从通道中读取数据    
  97.      * 并且判断是给那个服务通道的    
  98.      * @throws IOException     
  99.      * */    
  100.     public void read(SelectionKey key) throws IOException {    
  101.     
  102.         this.buf.clear();    
  103.         //通过选择键来找到之前注册的通道       
  104.         //但是这里注册的是ServerSocketChannel为什么会返回一个SocketChannel??       
  105.         SocketChannel channel = (SocketChannel) key.channel();    
  106.         //从通道里面读取数据到缓冲区并返回读取字节数       
  107.         int count = channel.read(this.buf);    
  108.     
  109.         if (count == -1) {    
  110.             //取消这个通道的注册       
  111.             key.channel().close();    
  112.             key.cancel();    
  113.             return;    
  114.         }    
  115.     
  116.         //将数据从缓冲区中拿出来       
  117.         String input = new String(this.buf.array()).trim();    
  118.         //那么现在判断是连接的那种服务       
  119.         if (channel.equals(this.clientchannel1)) {    
  120.             System.out.println(”欢迎您使用服务A”);    
  121.             System.out.println(”您的输入为:” + input);    
  122.         } else {    
  123.             System.out.println(”欢迎您使用服务B”);    
  124.             System.out.println(”您的输入为:” + input);    
  125.         }    
  126.     
  127.     }    
  128.     
  129.     @Override    
  130.     public void run() {    
  131.         while (true) {    
  132.             try {    
  133.                 System.out.println(”running … ”);    
  134.                 //选择一组键,其相应的通道已为 I/O 操作准备就绪。       
  135.                 this.selector.select();    
  136.     
  137.                 //返回此选择器的已选择键集       
  138.                 //public abstract Set<SelectionKey> selectedKeys()       
  139.                 Iterator selectorKeys = this.selector.selectedKeys().iterator();    
  140.                 while (selectorKeys.hasNext()) {    
  141.                     System.out.println(”running2 … ”);    
  142.                     //这里找到当前的选择键       
  143.                     SelectionKey key = (SelectionKey) selectorKeys.next();    
  144.                     //然后将它从返回键队列中删除       
  145.                     selectorKeys.remove();    
  146.                     if (!key.isValid()) { // 选择键无效    
  147.                         continue;    
  148.                     }    
  149.                     if (key.isAcceptable()) {    
  150.                         //如果遇到请求那么就响应       
  151.                         this.accept(key);    
  152.                     } else if (key.isReadable()) {    
  153.                         //读取客户端的数据       
  154.                         this.read(key);    
  155.                     }    
  156.                 }    
  157.             } catch (Exception e) {    
  158.                 e.printStackTrace();    
  159.             }    
  160.         }    
  161.     }    
  162.     
  163.     public static void main(String[] args) {    
  164.         Server server = new Server();    
  165.         Thread thread = new Thread(server);    
  166.         thread.start();    
  167.     }    
  168. }    
package nio;

import java.io.IOException;  
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.nio.channels.spi.SelectorProvider;  
import java.util.Iterator;  

/**    
 * TCP/IP的NIO非阻塞方式   
 * 服务器端   
 * */  
public class Server implements Runnable {  

    //第一个端口     
    private Integer port1 = 8099;  
    //第二个端口     
    private Integer port2 = 9099;  
    //第一个服务器通道 服务A     
    private ServerSocketChannel serversocket1;  
    //第二个服务器通道 服务B     
    private ServerSocketChannel serversocket2;  
    //连接1     
    private SocketChannel clientchannel1;  
    //连接2     
    private SocketChannel clientchannel2;  

    //选择器,主要用来监控各个通道的事件     
    private Selector selector;  

    //缓冲区     
    private ByteBuffer buf = ByteBuffer.allocate(512);  

    public Server() {  
        init();  
    }  

    /**   
     * 这个method的作用 
     * 1:是初始化选择器   
     * 2:打开两个通道   
     * 3:给通道上绑定一个socket   
     * 4:将选择器注册到通道上   
     * */  
    public void init() {  
        try {  
            //创建选择器     
            this.selector = SelectorProvider.provider().openSelector();  
            //打开第一个服务器通道     
            this.serversocket1 = ServerSocketChannel.open();  
            //告诉程序现在不是阻塞方式的     
            this.serversocket1.configureBlocking(false);  
            //获取现在与该通道关联的套接字     
            this.serversocket1.socket().bind(new InetSocketAddress("localhost", this.port1));  
            //将选择器注册到通道上,返回一个选择键     
            //OP_ACCEPT用于套接字接受操作的操作集位     
            this.serversocket1.register(this.selector, SelectionKey.OP_ACCEPT);  

            //然后初始化第二个服务端     
            this.serversocket2 = ServerSocketChannel.open();  
            this.serversocket2.configureBlocking(false);  
            this.serversocket2.socket().bind(new InetSocketAddress("localhost", this.port2));  
            this.serversocket2.register(this.selector, SelectionKey.OP_ACCEPT);  

        } catch (Exception e) {  
            e.printStackTrace();  
        }  

    }  

    /**   
     * 这个方法是连接   
     * 客户端连接服务器   
     * @throws IOException    
     * */  
    public void accept(SelectionKey key) throws IOException {  
        ServerSocketChannel server = (ServerSocketChannel) key.channel();  
        if (server.equals(serversocket1)) {  
            clientchannel1 = server.accept();  
            clientchannel1.configureBlocking(false);  
            //OP_READ用于读取操作的操作集位     
            clientchannel1.register(this.selector, SelectionKey.OP_READ);  
        } else {  
            clientchannel2 = server.accept();  
            clientchannel2.configureBlocking(false);  
            //OP_READ用于读取操作的操作集位     
            clientchannel2.register(this.selector, SelectionKey.OP_READ);  
        }  
    }  

    /**   
     * 从通道中读取数据   
     * 并且判断是给那个服务通道的   
     * @throws IOException    
     * */  
    public void read(SelectionKey key) throws IOException {  

        this.buf.clear();  
        //通过选择键来找到之前注册的通道     
        //但是这里注册的是ServerSocketChannel为什么会返回一个SocketChannel??     
        SocketChannel channel = (SocketChannel) key.channel();  
        //从通道里面读取数据到缓冲区并返回读取字节数     
        int count = channel.read(this.buf);  

        if (count == -1) {  
            //取消这个通道的注册     
            key.channel().close();  
            key.cancel();  
            return;  
        }  

        //将数据从缓冲区中拿出来     
        String input = new String(this.buf.array()).trim();  
        //那么现在判断是连接的那种服务     
        if (channel.equals(this.clientchannel1)) {  
            System.out.println("欢迎您使用服务A");  
            System.out.println("您的输入为:" + input);  
        } else {  
            System.out.println("欢迎您使用服务B");  
            System.out.println("您的输入为:" + input);  
        }  

    }  

    @Override  
    public void run() {  
        while (true) {  
            try {  
                System.out.println("running ... ");  
                //选择一组键,其相应的通道已为 I/O 操作准备就绪。     
                this.selector.select();  

                //返回此选择器的已选择键集     
                //public abstract Set<SelectionKey> selectedKeys()     
                Iterator selectorKeys = this.selector.selectedKeys().iterator();  
                while (selectorKeys.hasNext()) {  
                    System.out.println("running2 ... ");  
                    //这里找到当前的选择键     
                    SelectionKey key = (SelectionKey) selectorKeys.next();  
                    //然后将它从返回键队列中删除     
                    selectorKeys.remove();  
                    if (!key.isValid()) { // 选择键无效  
                        continue;  
                    }  
                    if (key.isAcceptable()) {  
                        //如果遇到请求那么就响应     
                        this.accept(key);  
                    } else if (key.isReadable()) {  
                        //读取客户端的数据     
                        this.read(key);  
                    }  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  

    public static void main(String[] args) {  
        Server server = new Server();  
        Thread thread = new Thread(server);  
        thread.start();  
    }  
}  
  1. package nio;  
  2.   
  3. import java.io.IOException;    
  4. import java.net.InetSocketAddress;    
  5. import java.nio.ByteBuffer;    
  6. import java.nio.channels.SocketChannel;    
  7. import java.net.InetAddress;    
  8.     
  9. /**    
  10.  * TCP/IP的NIO非阻塞方式    
  11.  * 客户端    
  12.  * */    
  13. public class Client {    
  14.     
  15.     //创建缓冲区       
  16.     private ByteBuffer buffer = ByteBuffer.allocate(512);   
  17.       
  18.     //访问服务器  
  19.     public void query(String host, int port) throws IOException {    
  20.         InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(host), port);    
  21.         SocketChannel socket = null;    
  22.         byte[] bytes = new byte[512];    
  23.         while (true) {    
  24.             try {    
  25.                 System.in.read(bytes);    
  26.                 socket = SocketChannel.open();    
  27.                 socket.connect(address);    
  28.                 buffer.clear();    
  29.                 buffer.put(bytes);    
  30.                 buffer.flip();    
  31.                 socket.write(buffer);    
  32.                 buffer.clear();    
  33.             } catch (Exception e) {    
  34.                 e.printStackTrace();    
  35.             } finally {    
  36.                 if (socket != null) {    
  37.                     socket.close();    
  38.                 }    
  39.             }    
  40.         }    
  41.     }    
  42.     
  43.     public static void main(String[] args) throws IOException {    
  44.         new Client().query(“localhost”8099);    
  45.     
  46.     }    
  47. }    
package nio;

import java.io.IOException;  
import java.net.InetSocketAddress;  
import java.nio.ByteBuffer;  
import java.nio.channels.SocketChannel;  
import java.net.InetAddress;  

/**   
 * TCP/IP的NIO非阻塞方式   
 * 客户端   
 * */  
public class Client {  

    //创建缓冲区     
    private ByteBuffer buffer = ByteBuffer.allocate(512); 

    //访问服务器
    public void query(String host, int port) throws IOException {  
        InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(host), port);  
        SocketChannel socket = null;  
        byte[] bytes = new byte[512];  
        while (true) {  
            try {  
                System.in.read(bytes);  
                socket = SocketChannel.open();  
                socket.connect(address);  
                buffer.clear();  
                buffer.put(bytes);  
                buffer.flip();  
                socket.write(buffer);  
                buffer.clear();  
            } catch (Exception e) {  
                e.printStackTrace();  
            } finally {  
                if (socket != null) {  
                    socket.close();  
                }  
            }  
        }  
    }  

    public static void main(String[] args) throws IOException {  
        new Client().query("localhost", 8099);  

    }  
}  

NIO主要原理与使用



Buffer的工作方式






NIO的文件访问方式




            </div>
                </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值