netty学习

java 网络编程

2017-06-18  本文已影响110人  二月_春风

简单的demo

客户端:

public static void main(String[] args) {

        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;

        try {
            socket = new Socket("localhost", 8899);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);

            //向服务器端发送数据
            out.println("hi server!");
            out.println("我是客户端");
            String response = in.readLine();
            System.out.println("Client: " + response);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
        }
    }
}

服务器端:

public class Server {
    public static void main(String[] args) {

        ServerSocket server = null;
        try {
            server = new ServerSocket(8899);
            System.out.println("server start .. ");
            //进行阻塞
            Socket socket = server.accept();
            //新建一个线程执行客户端的任务
            new Thread(new ServerHandler(socket)).start();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(server != null){
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            server = null;
        }
    }
}

服务端handler:

public class ServerHandler implements Runnable{

    private Socket socket ;

    public ServerHandler(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String body;
            while(true){
                body = in.readLine();
                if(body == null) break;
                System.out.println("Server :" + body);
                out.println("服务器端回送响的应数据.");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
        }
    }
}
BIO

弊端

1.1.1 Java NIO

NIO架构图

NIO三个最重要的组件
Buffer(缓冲区),Channer(管道,通道) Selector(选择器,多路复用器)

Demo1

了解一下Buffer概念

import java.nio.IntBuffer;
import java.security.SecureRandom;

public class NioTest1 {
    public static void main(String[] args) {
        //IntBuffer缓冲区分配10个长度
        IntBuffer buffer = IntBuffer.allocate(10);

        //将数据写到buffer中
        for(int i=0;i<buffer.capacity();i++){
            int randomNumber = new SecureRandom().nextInt(20);
            buffer.put(randomNumber);
        }

        //使用flip实现读写的切换
        buffer.flip();

        //将buffer数据读出来
        while (buffer.hasRemaining()){
            System.out.println(buffer.get());
        }
    }
}

Demo2

管道与Buffer结合使用,我们都是将数据从Channel读到Buffer中(涉及到Buffer就是写动作),然后Buffer读到程序中,绝对不可能数据直接从Channel直接读到程序中。

import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioTest2 {
    public static void main(String[] args) throws Exception{
        FileInputStream fileInputStream = new FileInputStream("NioTest2.txt");
        FileChannel fileChannel = fileInputStream.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        fileChannel.read(byteBuffer);

        //定义程序的反转,由读到写
        byteBuffer.flip();

        while(byteBuffer.remaining() > 0){
            byte b = byteBuffer.get();
            System.out.println("Character: "+(char)b);
        }

        fileInputStream.close();
    }
}

Demo3

import java.nio.IntBuffer;
import java.security.SecureRandom;

public class NioTest1 {
    public static void main(String[] args) {
        //IntBuffer缓冲区分配10个长度
        IntBuffer buffer = IntBuffer.allocate(10);

        System.out.println("capacity:"+buffer.capacity());

        //将数据写到buffer中
        for(int i=0;i<5;i++){
            int randomNumber = new SecureRandom().nextInt(20);
            buffer.put(randomNumber);
        }

        System.out.println("before flip limit: "+buffer.limit());  //10

        //使用flip实现读写的切换
        buffer.flip();

        System.out.println("after flip limit: "+buffer.limit()); //5

        System.out.println("enter while loop");

        //将buffer数据读出来
        while (buffer.hasRemaining()){
            System.out.println("position: "+buffer.position());
            System.out.println("limit: "+buffer.limit()); //5
            System.out.println("capacity: "+buffer.capacity()); //10
            System.out.println(buffer.get());
        }
    }
}

关于NIO Buffer中的3个重要状态属性的含义:position,limit与capacity。flip方法为什么在读写转换的时候调用。

java的api对其的描述:
A buffer is a linear, finite sequence of elements of a specific primitive type. Aside from its content, the essential properties of a buffer are its capacity, limit, and position:
一个buffer是线性,有限的特性原生类型的序列。除了其内容外,一个buffer最基础的属性是capacity,limit,和position:

A buffer's capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.
一个buffer的capacity就是其包含的元素的数量。一个buffer的capacity不可能是负数并且不会被改变。

A buffer's limit is the index of the first element that should not be read or written. A buffer's limit is never negative and is never greater than its capacity.
一个buffer的limit指的是无法再去读或者写的下一个元素的索引。一个buffer的limit不可能是负数并且不可能大于它的capacity。

A buffer's position is the index of the next element to be read or written. A buffer's position is never negative and is never greater than its limit.
一个buffer的position是下个能被读或者写的元素的索引。一个buffer的position不可能是负数并且不可能大于它的limit。

定义一个容量是6的buffer 读写过程中capacity,limit,position位置的变化

Demo4

写一个关于三个组件都使用的程序

public class NioTest4 {
    public static void main(String[] args) throws Exception{
        int[] ports = new int[5];

        ports[0]= 5000;
        ports[1]= 5001;
        ports[2]= 5002;
        ports[3]= 5003;
        ports[4]= 5004;

        //Selector对象最常见的构建方式
        Selector selector = Selector.open();

        for (int i = 0; i <ports.length ; i++) {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置channel为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //得到与channel相关联的Socket
            ServerSocket serverSocket = serverSocketChannel.socket();
            InetSocketAddress inetSocketAddress = new InetSocketAddress(ports[i]);
            serverSocket.bind(inetSocketAddress);

            //将channel注册到selector中,并且监听的是接收连接事件,这边只能监听客户端连接事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("监听端口:" + ports[i]);
        }


        while(true){
            int number = selector.select();
            System.out.println("number: "+number);

            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("selectedKeys: "+selectionKeys);
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();

                //接收到连接,我们将selector注册到一个个的ServerChannel当中,我们可以拿到每一个与之连接的Channel
                if(selectionKey.isAcceptable()){
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
                    //得到连接这个channel的SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);

                    //向selector注册可读事件
                    socketChannel.register(selector,SelectionKey.OP_READ);

                    //处理完selectkey之后一定要remove掉
                    iterator.remove();

                    System.out.println("获得客户端的连接: "+socketChannel);
                }else if(selectionKey.isReadable()){
                    SocketChannel socketChannel= (SocketChannel)selectionKey.channel();

                    int bytesRead = 0;

                    while(true){
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

                        byteBuffer.clear();

                        int read = socketChannel.read(byteBuffer);

                        if(read <= 0){
                            break;
                        }

                        byteBuffer.flip();

                        socketChannel.write(byteBuffer);

                        bytesRead +=read;
                    }

                    System.out.println("读取: "+ bytesRead+",来自于: "+socketChannel);

                    iterator.remove();
                }
            }

        }
    }
}

通过nc localhost 5050nc localhost 5051nc localhost 5052等可以去测试.

Selector维护着三个set集合:
第一个就是所有当前注册到selector上的key的集合。这个set可以通过调用keys()方法来获得。
第二个就是确定的一种感兴趣的集合比如连接的动作,可读可写的动作,这个set是通过selectedKeys方法返回的,这个已经被选择的key集合总是上面的keys()方法返回的子集。
第三个就是取消的key的集合是已经被取消的集合但是其channels还没有被取消注册。这个set集合是不能直接访问的。这个已经取消的key的集合也是上面的key set集合的一个子集。一般取消的集合都要在当前的遍历中删除掉。

上一篇下一篇

猜你喜欢

热点阅读