JAVA并发处理经验(四)并行模式与算法6:NIO网络编程

简介: 一、前言 首先我们必须了解NIO的一些基本概念 channel:是NIO中的一个通道,类似我们说的流。

一、前言

首先我们必须了解NIO的一些基本概念

channel:是NIO中的一个通道,类似我们说的流。---管道

Buffer:理解为byte数组。与channel交流。----水流

Selector:有一个SelectableChancel实现,用线程管理------选择器

二、NIO编程

2.1 NIO服务端

package pattern.nio;


import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by ycy on 16/1/19.
 */
public class NioServer {
    //选择器
    private Selector selector;
    //线程池
    private ExecutorService tp = Executors.newCachedThreadPool();
    //给定大小的map
    public static Map<Socket, Long> time_stat = new HashMap<Socket, Long>(10240);

    public void startServer() throws Exception {
        //1'由selectorPrivider返回一个创建者,并打开一个选择器
        selector = SelectorProvider.provider().openSelector();
        //2'打开套接字通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //block - 如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
        ssc.configureBlocking(false);

        InetSocketAddress isa = new InetSocketAddress(65500);
        // InetSocketAddress isa=new InetSocketAddress(8000);
        //获取与此通道关联的服务器套接字。将 ServerSocket 绑定到特定地址(IP 地址和端口号)
        ssc.socket().bind(isa);
        //将通道注册到选择器,
        SelectionKey accpetKey = ssc.register(selector, SelectionKey.OP_ACCEPT);
        try {
            for (; ; ) {
                selector.select();//消费阻塞
                Set readykeys = selector.selectedKeys();//获取已经准备好的keys
                Iterator i = readykeys.iterator();//迭代
                long e = 0;
                while (i.hasNext()) {
                    SelectionKey sk = (SelectionKey) i.next();
                    i.remove();//必须消除,防止重复消费
                    if (sk.isAcceptable()) {
                        doAccept(sk);//如果为接受状态,接受
                    } else if (sk.isValid() && sk.isReadable()) {//如果是可读
                        if (!time_stat.containsKey(((SocketChannel) sk.channel()).socket())) {
                            //将socket方法如map
                            time_stat.put(((SocketChannel) sk.channel()).socket(),
                                    System.currentTimeMillis());//增加一个时间戳
                            //读取
                            doRead(sk);
                        }
                    } else if (sk.isValid() && sk.isWritable()) {
                        //写
                        doWrite(sk);
                        e = System.currentTimeMillis();
                        long b = time_stat.remove(((SocketChannel) sk.channel()).socket());
                        System.out.println("spend:" + (e - b) + "ms");//输入处理写入耗时
                    }
                }

            }
        } catch (ClosedSelectorException e) {
            System.out.println("外面捕捉不做事");
        }

    }

    /*
    与客户端建立连接
     */
    private void doAccept(SelectionKey sk) {

        try {
            ServerSocketChannel server = (ServerSocketChannel) sk.channel();
            SocketChannel clientChannel;
            clientChannel = server.accept();//生成一个channel表示与客户端通信
            clientChannel.configureBlocking(false);//非阻塞模式
            //Register this channel for reading
            SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ);
            //Allocate an Echoclient instance adn attach it to this selction key
            EchoClient echoClient = new EchoClient();//回复给客服端口的全部信息
            clientKey.attach(echoClient);//附加实例,整个连接共享实例


            InetAddress clientAddress = clientChannel.socket().getInetAddress();
            System.out.println("Acceprted connection form " + clientAddress.getHostAddress() + ".")
            ;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //执行读得操作
    private void doRead(SelectionKey sk) {
        SocketChannel channel = (SocketChannel) sk.channel();
        ByteBuffer bb = ByteBuffer.allocate(8192);
        int len;
        try {
            len = channel.read(bb);
            if (len < 0) {
                disconnect(sk);
                return;
            }
        } catch (Exception e) {
            System.out.println("faild to read from client");
            e.printStackTrace();
            disconnect(sk);
            return;
        }

        bb.flip();
        tp.execute(new HanldeMsg(sk, bb));
    }

    private void disconnect(SelectionKey sk) {
        try {

            SocketChannel channel = (SocketChannel) sk.channel();
            channel.close();
            //sk.cancel();
            sk.selector().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //执行写操作
    private void doWrite(SelectionKey sk) {
        SocketChannel channel = (SocketChannel) sk.channel();
        EchoClient echoClient = (EchoClient) sk.attachment();
        LinkedList<ByteBuffer> outq = echoClient.getOutputQuquq();

        ByteBuffer bb = outq.getLast();

        try {
            int len = channel.write(bb);
            if (len == -1) {
                disconnect(sk);
                return;
            }
            if (bb.remaining() == 0) {
                //已经完成
                outq.removeLast();
            }

        } catch (Exception e) {
            System.out.println("Faild to write to client");
            e.printStackTrace();
            disconnect(sk);
        }
        if (outq.size() == 0) {//很重要
            sk.interestOps(SelectionKey.OP_READ);
        }
    }

    /////////////////内部匿名类/////////////////////
    class EchoClient {
        private LinkedList<ByteBuffer> outq;

        EchoClient() {
            outq = new LinkedList<ByteBuffer>();
        }

        public LinkedList<ByteBuffer> getOutputQuquq() {
            return outq;
        }

        public void enqueue(ByteBuffer bb) {
            outq.addFirst(bb);
        }
    }

    //将接受的数据压入EchClient,需要处理业务在这u处理,处理完成之后重新注册事件op_write
    class HanldeMsg implements Runnable {
        SelectionKey sk;
        ByteBuffer bb;

        public HanldeMsg(SelectionKey sk, ByteBuffer bb) {
            this.sk = sk;
            this.bb = bb;
        }

        public void run() {
            EchoClient echoClient = (EchoClient) sk.attachment();
            echoClient.enqueue(bb);
            sk.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
            //强迫selector立即返回
            selector.wakeup();
        }
    }

}

2.2NIO main方法

package pattern.nio;

/**
 * Created by ycy on 16/1/20.
 */
public class NioMain {
    public static void main(String[] args) throws Exception {
        NioServer nioServer=new NioServer();
        nioServer.startServer();
    }
}

2.3 NIO客户端

package pattern.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.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;

/**
 * Created by ycy on 16/1/20.
 */
public class NioClient {
    private Selector selector;
    public void init(String ip,int port) throws IOException {
        SocketChannel channel=SocketChannel.open();
        channel.configureBlocking(false);

        this.selector= SelectorProvider.provider().openSelector();
        channel.connect(new InetSocketAddress(ip,port));
        channel.register(selector,SelectionKey.OP_CONNECT);

    }
    public void working() throws IOException {
        while (true){
            if (!selector.isOpen()){
                break;
            }
                selector.select();
                Iterator<SelectionKey> ite=this.selector.selectedKeys().iterator();
                while (ite.hasNext()){
                    SelectionKey key=ite.next();
                    ite.remove();
                    //连接事件
                    if (key.isConnectable()){
                        connect(key);
                    }else if(key.isReadable()){
                        read(key);
                    }
                }

        }
    }
    /*
    连接
     */
    public void connect(SelectionKey key) throws IOException {
       SocketChannel channel=(SocketChannel)key.channel();
        //如果正在连接,则连接完成
        if(channel.isConnectionPending()){
            channel.finishConnect();

        }
        channel.configureBlocking(false);
        channel.write(ByteBuffer.wrap(new String("HELLO" ).getBytes()));
        channel.register(this.selector,SelectionKey.OP_READ);
    }

    public void read(SelectionKey key) throws IOException {
        SocketChannel channel=(SocketChannel)key.channel();
        //读取缓冲区
        ByteBuffer bb=ByteBuffer.allocate(1000);
        channel.read(bb);
        byte[] data=bb.array();
        String msg=new String(data).trim();
        System.out.println("客户端收到信息:"+msg);
        channel.close();
        key.selector().close();
    }

    public static void main(String[] args) throws IOException {
        NioClient nioClient=new NioClient();
        nioClient.init("127.0.0.1",65500);
        nioClient.working();
    }
}



目录
相关文章
|
11天前
|
负载均衡 算法 应用服务中间件
面试题:Nginx有哪些负载均衡算法?Nginx位于七层网络结构中的哪一层?
字节跳动面试题:Nginx有哪些负载均衡算法?Nginx位于七层网络结构中的哪一层?
28 0
|
23天前
|
存储 算法 Java
Java数据结构与算法-java数据结构与算法(二)
Java数据结构与算法-java数据结构与算法
67 1
|
26天前
|
机器学习/深度学习 算法 计算机视觉
|
3天前
|
网络协议 Java API
深度剖析:Java网络编程中的TCP/IP与HTTP协议实践
【4月更文挑战第17天】Java网络编程重在TCP/IP和HTTP协议的应用。TCP提供可靠数据传输,通过Socket和ServerSocket实现;HTTP用于Web服务,常借助HttpURLConnection或Apache HttpClient。两者结合,构成网络服务基础。Java有多种高级API和框架(如Netty、Spring Boot)简化开发,助力高效、高并发的网络通信。
|
3天前
|
算法 定位技术 Windows
R语言最大流最小割定理和最短路径算法分析交通网络流量拥堵问题
R语言最大流最小割定理和最短路径算法分析交通网络流量拥堵问题
10 4
|
9天前
|
机器学习/深度学习 自然语言处理 算法
|
11天前
|
算法 安全 Java
java代码 实现AES_CMAC 算法测试
该代码实现了一个AES-CMAC算法的简单测试,使用Bouncy Castle作为安全提供者。静态变量K定义了固定密钥。`Aes_Cmac`函数接受密钥和消息,返回AES-CMAC生成的MAC值。在`main`方法中,程序对给定的消息进行AES-CMAC加密,然后模拟接收ECU的加密结果并进行比较。如果两者匹配,输出&quot;验证成功&quot;,否则输出&quot;验证失败&quot;。辅助方法包括将字节转为16进制字符串和将16进制字符串转为字节。
|
17天前
|
搜索推荐 Java
Java排序算法
Java排序算法
18 0
|
17天前
|
搜索推荐 Java
Java基础(快速排序算法)
Java基础(快速排序算法)
23 4
|
20天前
|
存储 算法 JavaScript
Java入门高频考查算法逻辑基础知识3-编程篇(超详细18题1.8万字参考编程实现)
解决这类问题时,建议采取下面的步骤: 理解数学原理:确保你懂得基本的数学公式和法则,这对于制定解决方案至关重要。 优化算法:了解时间复杂度和空间复杂度,并寻找优化的机会。特别注意避免不必要的重复计算。 代码实践:多编写实践代码,并确保你的代码是高效、清晰且稳健的。 错误检查和测试:要为你的代码编写测试案例,测试标准的、边缘情况以及异常输入。 进行复杂问题简化:面对复杂的问题时,先尝试简化问题,然后逐步分析和解决。 沟通和解释:在编写代码的时候清晰地沟通你的思路,不仅要写出正确的代码,还要能向面试官解释你的
32 0