Netty——1、网络编程基本概念

Netty网络编程 【代码】Netty网络编程。 阅读详情

1.阻塞IO—Socket

Socket又称“套接字”,应用程序通常通过“套接字”向网络发出请求或应答网络请求。

Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,不会因为在服务器端或在客户端而产生不同的级别。不管是ServerSocket还是Socket,它们的工作都是通过SocketImpl类及其子类完成的。

套接字的连接过程可以分为四个步骤:服务器监听、客户端请求服务器、服务器端连接确认、客户端连接确认并进行通信。

(1)服务器监听:服务器端套接字并不定位具体的客户端套接字,而是出于等待连接的状态,实时监控网络状态。

(2)客户端请求:客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述要连接的服务器端的套接字,指出服务器端的套接字的地址和端口号,然后向服务器端套接字提出连接请求。

(3)服务器端连接确认:当服务器端的套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发送给客户端。

(4)客户端连接确认:一旦客户端确认了此描述,连接就建立好了,双方开始通信。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

借用一下网上的Socket通信模型图片:


Socket通信步骤:

①创建ServerSocket和Socket

②打开连接到Socket的输入/输出流

③按照协议对Socket进行读写操作

④关闭输入输出流、关闭Socket

服务器端:

①创建ServerSocket对象,绑定监听端口

②通过accept()方法监听客户端请求

③建立连接后,通过输入流读取客户端发送的请求信息

④通过输出流向客户端发送响应信息

⑤关闭相关资源

客户端:

①创建Socket对象,指明需要连接的服务器的地址和端口号

②连接建立后,通过输出流向服务器端发送请求信息

③通过输入流获取服务器响应信息

④关闭响应资源

下面看一个简单的小例子:

服务器端响应工具类:

public class ServerHandler implements Runnable {  
  
    private Socket socket;  
  
    public ServerHandler(Socket socket) {  
        this.socket = socket;  
    }  
  
    @Override  
    public void run() {  
        BufferedReader bufferedReader = null;  
        PrintWriter printWriter = null;  
        try {  
            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
            printWriter = new PrintWriter(socket.getOutputStream(), true);  
  
            while (true) {  
                String info = bufferedReader.readLine();  
                if(info == null)  
                    break;  
                System.out.println("客户端发送的消息:" + info);  
                printWriter.println("服务器端响应了客户端请求....");  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if(bufferedReader != null){  
                try {  
                    bufferedReader.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            if(printWriter != null){  
                try {  
                    printWriter.close();  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
            if(socket != null){  
                try {  
                    socket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            socket = null;  
        }  
    }  
}  

服务器端:

public class Server {  
    private static int PORT = 8379;  
    public static void main(String[] args) {  
        ServerSocket serverSocket = null;  
        try {  
            serverSocket = new ServerSocket(PORT);  
            System.out.println("服务器端启动了....");  
            //进行阻塞  
            Socket socket = serverSocket.accept();  
            //启动一个线程来处理客户端请求  
            new Thread(new ServerHandler(socket)).start();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if(serverSocket != null){  
                try {  
                    serverSocket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            serverSocket = null;  
        }  
    }  
}  
客户端:
public class Client {  
    private static int PORT = 8379;  
    private static String IP = "127.0.0.1";  
  
    public static void main(String[] args) {  
        BufferedReader bufferedReader = null;  
        PrintWriter printWriter = null;  
        Socket socket = null;  
        try {  
            socket = new Socket(IP, PORT);  
            printWriter = new PrintWriter(socket.getOutputStream(), true);  
            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
  
            printWriter.println("客户端请求了服务器....");  
            String response = bufferedReader.readLine();  
            System.out.println("Client:" + response);  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if(bufferedReader != null){  
                try {  
                    bufferedReader.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            if(printWriter != null){  
                try {  
                    printWriter.close();  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
            if(socket != null){  
                try {  
                    socket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            } else {  
                socket = null;  
            }  
        }  
    }  
}  
运行结果:



以上的代码有个问题,就是每次有客户端请求服务器端都会创建一个线程,当线程过多时,服务器端可能会宕机。解决这个问题,可以使用JDK提供的线程池(伪异步)。其它地方都不变,将服务器端的代码修改成如下即可:

public class Server {  
    private static int PORT = 8379;  
    public static void main(String[] args) {  
        ServerSocket serverSocket = null;  
        try {  
            serverSocket = new ServerSocket(PORT);  
            System.out.println("服务器端启动了....");  
            //进行阻塞  
            Socket socket = null;  
            //启动一个线程来处理客户端请求  
            //new Thread(new ServerHandler(socket)).start();  
            <span style="color:#ff0000;">HandlerExecutorPool pool = new HandlerExecutorPool(50, 1000);  
            while (true) {  
                socket = serverSocket.accept();  
                pool.execute(new ServerHandler(socket));  
            }</span>  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if(serverSocket != null){  
                try {  
                    serverSocket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            serverSocket = null;  
        }  
    }  
}  
其中HandlerExecutorPool为自定义的线程池,代码如下:

public class HandlerExecutorPool {  
  
    private ExecutorService executor;  
  
    public HandlerExecutorPool(int maxSize, int queueSize) {  
        this.executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxSize, 120L, TimeUnit.SECONDS,  

<span style="white-space:pre">                      </span> new ArrayBlockingQueue<>(queueSize));  
    }  
  
    public void execute(Runnable task) {  
        executor.execute(task);  
    }  
}  

2.IO(BIO)与NIO的区别

其本质就是阻塞和非阻塞的区别。

阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,那么程序就一直等着,直到传输完毕为止。

非阻塞概念:应用程序直接可以获取已经准备就绪的数据,无需等待。

IO为同步阻塞形式,NIO为同步非阻塞形式。NIO没有实现异步,在JDK1.7之后,升级了NIO库包,支持异步非阻塞通信模型,即NIO2.0(AIO)。

同步和异步:同步和异步一般是面向操作系统与应用程序对IO操作的层面上来区别的。①同步时,应用程序会直接参与IO读写操作,并且应用程序会直接阻塞到某一个方法上,直到数据准备就绪(BIO);或者采用轮询的策略实时检查数据的就绪状态,如果就绪则获取数据(NIO)。②异步时,则所有的IO读写操作都交给操作系统处理,与应用程序没有直接关系,应用程序并不关心IO读写,当操作系统完成IO读写操作时,会向应用程序发出通知,应用程序直接获取数据即可。

同步说的是Server服务端的执行方式,阻塞说的是具体的技术,接收数据的方式、状态(io、nio)。

3.NIO编程介绍

学习NIO编程,首先需要了解几个概念:

(1)Buffer(缓冲区)

Buffer是一个对象,它包含一些需要写入或者读取的数据。在NIO类库中加入Buffer对象,体现了新类库与原IO的一个重要区别。在面向流的IO中,可以直接将数据写入或读取到Stream对象中。在NIO类库中,所有的数据都是用缓冲区处理的(读写)。 缓冲区实质上是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类型的数组。这个数组为缓冲区提供了访问数据的读写等操作属性,如位置、容量、上限等概念,具体的可以参考API文档。

Buffer类型:最常使用的是ByteBuffer,实际上每一种java基本类型都对应了一种缓存区(除了Boolean类型)。

①ByteBuffer②CharBuffer③ShortBuffer④IntBuffer⑤LongBuffer⑥FloatBuffer⑦DoubleBuffer

(2)Channel(管道、通道)

Channel就像自来水管道一样,网络数据通过Channel读取和写入,通道与流的不同之处在于通道是双向的,而流只能在一个方向上移动(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读、写或者二者同时进行,最关键的是可以和多路复用器集合起来,有多种的状态位,方便多路复用器去识别。通道分为两大类:一类是用于网络读写的SelectableChannel,另一类是用于文件操作的FileChannel,我们使用的SocketChannel和ServerSocketChannel都是SelectableChannel的子类。

(3)Selector(选择器、多路复用器)

是NIO编程的基础,非常重要。多路复用器提供选择已经就绪的任务的能力。简单说,就是Selector会不断的轮询注册在其上的通道(Channel),如果某个通道发生了读写操作,这个通道就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以取得就绪的Channel集合,从而进行后续的IO操作。一个多路复用器(Selector)可以负责成千上万的通道(Channel),没有上限。这也是JDK使用了epoll代替传统的select实现,获得连接句柄(客户端)没有限制。那也就意味着我们只要一个线程负责Selector的轮询,就可以接入成千上万个客户端,这是JDK NIO库的巨大进步。

Selector线程类似一个管理者(Master),管理了成千上万个管道,然后轮询哪个管道的数据已经准备好了,通知CPU执行IO的读取或写入操作。

Selector模式:当IO事件(管道)注册到选择器以后,Selector会分配给每个管道一个key值,相当于标签。Selector选择器是以轮询的方式进行查找注册的所有IO事件(管道),当IO事件(管道)准备就绪后,Selector就会识别,会通过key值来找到相应的管道,进行相关的数据处理操作(从管道中读取或写入数据,写到缓冲区中)。每个管道都会对选择器进行注册不同的事件状态,以便选择器查找。

事件状态:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRITE

NIO通信模型图解:

(虚线表示不直接相关联)

下面用代码来演示一下Buffer、Channel、Selector的使用。

以IntBuffer为例,讲解一下Buffer的常用API:


public class IntBufferTest {  
    public static void main(String[] args) {  
        //1、基本操作  
        //创建指定长度的缓冲区  
        /*IntBuffer buffer = IntBuffer.allocate(10); 
        buffer.put(11); //position位置:0->1 
        buffer.put(5); //position位置:1->2 
        buffer.put(32); //position位置:2->3 
        System.out.println("未调用flip复位方法前的buffer:" + buffer); 
        //把位置复位为0,也就是position位置由3->0 
        buffer.flip(); 
        //比较未调用flip方法和调用之后buffer的limit可以发现,不进行复位操作的话,position的值为3,limit的值为10 
        // 因为缓冲区中已有11、5、32三个元素,也就意味着put()方法会使position向后递增1 
        System.out.println("调用flip复位方法后的buffer:" + buffer); 
        System.out.println("buffer容量为:" + buffer.capacity()); 
        System.out.println("buffer限制为:" + buffer.limit()); 
        System.out.println("获取下标为1的元素:" + buffer.get(1)); 
        System.out.println("调用get(index)方法后的buffer:" + buffer); //调用get(index)方法,不会改变position的值 
        buffer.put(1, 4); //将buffer位置为1的值替换为4,调用put(index,value)不会改变position的值 
        System.out.println("调用put(index, value)方法后的buffer:" + buffer); 
 
        for(int i=0; i<buffer.limit(); i++) { 
            //调用get方法会使缓冲区的位置(position)向后递增一位 
            System.out.print(buffer.get() + "\t"); 
        } 
        System.out.println("\nbuffer对象遍历之后buffer为:" + buffer);*/  
  
  
        //2、wrap方法的使用  
        /*int[] arr = new int[]{1, 2, 3}; 
        IntBuffer buffer = IntBuffer.wrap(arr); 
        System.out.println("wrap(arr)方法:" + buffer); 
        //IntBuffer.wrap(array, postion, length)表示容量为array的长度,但是可操作的元素为位置postion到length的数组元素 
        buffer = IntBuffer.wrap(arr, 0, 2); 
        System.out.println("wrap(arr, 0, 2):" + buffer);*/  
  
        //3、其他方法  
        IntBuffer buffer = IntBuffer.allocate(10);  
        int[] arr = new int[]{1, 2, 3};  
        buffer.put(arr);  
        System.out.println("调用put(arr)方法后的buffer:" + buffer);  
        //一种复制方法,buffer1的pos、lim、cap与buffer的一样  
        IntBuffer buffer1 = buffer.duplicate();  
        System.out.println("buffer1:" + buffer1);  
  
        buffer.position(1); //将buffer的position设置为1,不建议使用。功能相当于flip()方法,但是从运行结果可以看出,lim依然等于10  
        System.out.println("调用position()方法后的buffer:" + buffer);  
        System.out.println("buffer的可读数据量:" + buffer.remaining()); //计算出从pos到lim的长度  
        int[] arr1 = new int[buffer.remaining()];  
        //将缓冲区的数据放入arr1中  
        buffer.get(arr1);  
        for(Integer i : arr1) {  
            System.out.print(Integer.toString(i) + ",");  
        }  
        System.out.println();  
  
        //比较flip()方法和position(index)方法的区别  
        buffer1.flip();  
        System.out.println("buffer1的可读数量:" + buffer1.remaining());  
        arr1 = new int[buffer1.remaining()];  
        buffer1.get(arr1);  
        for(Integer i : arr1) {  
            System.out.print(Integer.toString(i) + ",");  
        }  
    }  
}  
运行结果:


接下来是Buffer、Channel、Selector的一个入门的小例子:

Server端:

public class Server implements Runnable {  
  
    private Selector selector;  
    private ByteBuffer buffer = ByteBuffer.allocate(1024);  
  
    public Server(int port) {  
        try {  
            //1 打开多复用器  
            selector = Selector.open();  
            //2 打开服务器通道  
            ServerSocketChannel ssc = ServerSocketChannel.open();  
            //3 设置服务器通道为非阻塞方式  
            ssc.configureBlocking(false);  
            //4 绑定地址  
            ssc.bind(new InetSocketAddress(port));  
            //5 把服务器通道注册到多路复用选择器上,并监听阻塞状态  
            ssc.register(selector, SelectionKey.OP_ACCEPT);  
            System.out.println("Server start, port:" + port);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    @Override  
    public void run() {  
        while (true) {  
            try {  
                //1 必须让多路复用选择器开始监听  
                selector.select();  
                //2 返回所有已经注册到多路复用选择器上的通道的SelectionKey  
                Iterator<SelectionKey> keys = selector.selectedKeys().iterator();  
                //3 遍历keys  
                while (keys.hasNext()) {  
                    SelectionKey key = keys.next();  
                    keys.remove();  
                    if(key.isValid()) { //如果key的状态是有效的  
                        if(key.isAcceptable()) { //如果key是阻塞状态,则调用accept()方法  
                            accept(key);  
                        }  
                        if(key.isReadable()) { //如果key是可读状态,则调用read()方法  
                            read(key);  
                        }  
                    }  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    private void accept(SelectionKey key) {  
        try {  
            //1 获取服务器通道  
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();  
            //2 执行阻塞方法  
            SocketChannel sc = ssc.accept();  
            //3 设置阻塞模式为非阻塞  
            sc.configureBlocking(false);  
            //4 注册到多路复用选择器上,并设置读取标识  
            sc.register(selector, SelectionKey.OP_READ);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    private void read(SelectionKey key) {  
        try {  
            //1 清空缓冲区中的旧数据  
            buffer.clear();  
            //2 获取之前注册的SocketChannel通道  
            SocketChannel sc = (SocketChannel) key.channel();  
            //3 将sc中的数据放入buffer中  
            int count = sc.read(buffer);  
            if(count == -1) { // == -1表示通道中没有数据  
                key.channel().close();  
                key.cancel();  
                return;  
            }  
            //读取到了数据,将buffer的position复位到0  
            buffer.flip();  
            byte[] bytes = new byte[buffer.remaining()];  
            //将buffer中的数据写入byte[]中  
            buffer.get(bytes);  
            String body = new String(bytes).trim();  
            System.out.println("Server:" + body);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    public static void main(String[] args) {  
        new Thread(new Server(8379)).start();  
    }  
}  
客户端:
public class Client {  
    public static void main(String[] args) {  
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8379);  
        SocketChannel sc = null;  
        ByteBuffer buffer = ByteBuffer.allocate(1024);  
        try {  
            //打开通道  
            sc = SocketChannel.open();  
            //建立连接  
            sc.connect(address);  
            while (true) {  
                byte[] bytes = new byte[1024];  
                System.in.read(bytes);  
                //把输入的数据放入buffer缓冲区  
                buffer.put(bytes);  
                //复位操作  
                buffer.flip();  
                //将buffer的数据写入通道  
                sc.write(buffer);  
                //清空缓冲区中的数据  
                buffer.clear();  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if(sc != null) {  
                try {  
                    sc.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
}  
运行结果:



3、AIO

在NIO的基础上引入了异步通道的概念,并提供了异步文件和异步套接字通道的实现,从而在真正意义上实现了异步非阻塞,之前的NIO只是非阻塞而并非异步。AIO不需要通过对多路复用器对注册的通道进行轮询操作即可实现异步读写,从而简化NIO编程模型。

①AsynchronousServerSocketChannel

②AsynchronousSocketChannel

下面看代码:

Server端:

  1. public class Server {  
  2.     //线程池  
  3.     private ExecutorService executorService;  
  4.     //线程组  
  5.     private AsynchronousChannelGroup channelGroup;  
  6.     //服务器通道  
  7.     public AsynchronousServerSocketChannel channel;  
  8.   
  9.     public Server(int port) {  
  10.         try {  
  11.             //创建线程池  
  12.             executorService  = Executors.newCachedThreadPool();  
  13.             //创建线程组  
  14.             channelGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);  
  15.             //创建服务器通道  
  16.             channel = AsynchronousServerSocketChannel.open(channelGroup);  
  17.             //绑定地址  
  18.             channel.bind(new InetSocketAddress(port));  
  19.             System.out.println("server start, port:" + port);  
  20.             channel.accept(this, new ServerCompletionHandler());  
  21.             Thread.sleep(Integer.MAX_VALUE);  
  22.         } catch (Exception e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.     }  
  26.   
  27.     public static void main(String[] args) {  
  28.         Server server = new Server(8379);  
  29.     }  
  30. }  

ServerCompletionHandler类:
public class ServerCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, Server> {  
    @Override  
    public void completed(AsynchronousSocketChannel channel, Server attachment) {  
        //当有下一个客户端接入的时候,直接调用Server的accept方法,这样反复执行下去,保证多个客户端都可以阻塞  
        attachment.channel.accept(attachment, this);  
        read(channel);  
    }  
  
    private void read(AsynchronousSocketChannel channel) {  
        //读取数据  
        ByteBuffer buffer = ByteBuffer.allocate(1024);  
        channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {  
            @Override  
            public void completed(Integer resultSize, ByteBuffer attachment) {  
                attachment.flip();  
                System.out.println("Server->" + "收到客户端发送的数据长度为:" + resultSize);  
                String data = new String(buffer.array()).trim();  
                System.out.println("Server->" + "收到客户端发送的数据为:" + data);  
                String response = "服务器端响应了客户端。。。。。。";  
                write(channel, response);  
            }  
  
            @Override  
            public void failed(Throwable exc, ByteBuffer attachment) {  
                exc.printStackTrace();  
            }  
        });  
    }  
  
    private void write(AsynchronousSocketChannel channel, String response) {  
        try {  
            ByteBuffer buffer = ByteBuffer.allocate(1024);  
            buffer.put(response.getBytes());  
            buffer.flip();  
            channel.write(buffer).get();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    @Override  
    public void failed(Throwable exc, Server attachment) {  
        exc.printStackTrace();  
    }  
客户端:
public class Client implements Runnable {  
  
    private AsynchronousSocketChannel channel;  
  
    public Client() throws IOException {  
        channel = AsynchronousSocketChannel.open();  
    }  
  
    public void connect() {  
        channel.connect(new InetSocketAddress("127.0.0.1", 8379));  
    }  
  
    public void write(String data) {  
        try {  
            channel.write(ByteBuffer.wrap(data.getBytes())).get();  
            read();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    public void read() {  
        ByteBuffer buffer = ByteBuffer.allocate(1024);  
        try {  
            channel.read(buffer).get();  
            buffer.flip();  
            byte[] bytes = new byte[buffer.remaining()];  
            buffer.get(bytes);  
            String data = new String(bytes, "UTF-8").trim();  
            System.out.println(data);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    @Override  
    public void run() {  
        while (true) {  
  
        }  
    }  
  
    public static void main(String[] args) {  
        try {  
            Client c1 = new Client();  
            Client c2 = new Client();  
            Client c3 = new Client();  
  
            c1.connect();  
            c2.connect();  
            c3.connect();  
  
            new Thread(c1).start();  
            new Thread(c2).start();  
            new Thread(c3).start();  
  
            Thread.sleep(1000);  
  
            c1.write("c1 aaa");  
            c2.write("c2 bbbb");  
            c3.write("c3 ccccc");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}  

运行结果:










Netty笔记 bossGroup用于处理客户端的TCP连接请求,workerGroup负责每一条的具体读写数据的处理逻辑,真正负责I/O读写操作,交由对应的Handler处理。(3)、Bootstrap只需要配置一个线程组------EventLoopGroup,而ServerBootstrap需要配置两个线程租----EvenetLoopGroup,一个用于接收连接,一个用于具体的处理。在Reactor模式中,可以有多个Handlers,每个Handler负责处理特定的事件类型,如连接事件、读取事件、写入事件等。 阅读详情

相关推荐

Java NIO与Netty网络编程

Java NIO和Netty都是网络编程中的重要技术。Java NIO提供了非阻塞I/O操作的能力,使得开发高性能网络应用成为可能;而Netty则是一个基于Java NIO的高性能网络应用框架,它封装了Java NIO的复杂性,提供了更易于使用的API和丰富的功能。在实际应用中,应根据具体需求和项目特点选择合适的技术,并结合最佳实践进行优化和实现。

shejizuopin的博客 1142

Netty

一、概述 1、什么是Netty Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.Copy Netty 是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端 注意:netty的异步还是基于多路复用的,并没有实现真正意

Sai_BAN的博客 990

Netty网络编程实战1,搭建第一个Netty服务器

Java高并发编程实战系列,打造精品专栏。

学Java,找哪吒 6273

Netty应用(一) 之 NIO概念 & 基本编程

Netty应用(一) 之 NIO概念 & 基本编程

Eriksen的博客 2024

Netty网络编程实战2,使用Netty开发聊天室功能

Java高并发编程实战系列,打造精品专栏。

学Java,找哪吒 4718

时序数据库避坑指南:TDengine与IoTDB在车联网场景的7个关键对比

本文对比了时序数据库TDengine与IoTDB在车联网场景下的7个关键差异,包括数据模型设计、边缘-云端协同能力、车规级函数库、存储压缩算法等。通过真实案例和性能测试,帮助企业在新能源汽车大数据处理中选择合适的时序数据库,优化存储成本和实时告警性能。

rust6ferris的博客 452

Netty-1、网络编程基本概念

1.阻塞IO—Socket Socket又称“套接字”,应用程序通常通过“套接字”向网络发出请求或应答网络请求。 Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,不会因为

kingjs123456的博客 137

ss524v100 调试cvbs输出

启用视频层3 dev2 对应 layer3在vdec与vo绑定的时候(不需要经过vpss),不能绑定错误,以下是绑定的程序。

2116

网络编程进阶-Netty从入门到精通

这里串联了上面提到的还有一个常用到的叫做ChannelHandlerContext,那他和ChannelHandler有什么关系和区别呢ChannelHandlerContext 是与 ChannelHandler 关联的上下文对象,用于在处理过程中传递状态和操作。它提供了对当前 Channel 的引用,并允许访问其上下游的处理器。通过ChannelHandlerContext,可以调用方法向下一个处理器发送事件,或者从上一个处理器接收事件。

qq_46248151的博客 1814

从ADRV9002到ADRV9003:手把手教你移植FPGA驱动,避开那些官方没说的坑

本文详细介绍了从ADRV9002到ADRV9003的FPGA驱动移植实战指南,重点解析了两者在硬件架构上的关键差异,如通道配置和DPD功能的移除。通过提供完整的移植步骤、调试技巧和性能优化建议,帮助工程师避开官方文档未提及的常见陷阱,实现高效驱动开发。

weixin_28738397的博客 173

AD学习笔记(一)基础认识

文章目录AD学习笔记第一讲 AD基础认识一、两库两图(分别对应下图)1、PCB图(pcbdoc)2、原理图(schdoc)3、原理图库(schlib)4、PCB库(pcblib)二、封装1、DIP封装2、LQFP封装三、绘制整体流程1、建立工程(PrjPcb)2、建立两库两图并保存整个工程(五次)3、从库中提取绘制原理图4、原理图导入pcb图5、根据原理图布线6、pcb图交由工厂打板 AD学习笔记 第一讲 AD基础认识 一、两库两图(分别对应下图) PCB图(pcbdoc) 原理图(schdoc) PCB库

qq_52465473的博客 6686

netty网络编程1

Netty网络编程 文章目录Netty网络编程1. IO概念BIO 同步阻塞式 (Blocking I/O)NIO 同步非阻塞式(Non Blocking I/O)AIO 异步非阻塞式(Asynchronous Blocking I/O)2.BIO与NIO模型区别阻塞模型 BIO非阻塞模型 NIO3.NIO 基础组件 Channel 与Buffer缓冲区Buffer缓冲区Buffer定义Buffer 内部结构Buffer核心使用flip操作clear操作rewind操作mark操作Channel 管道Cha

haohao_ding的博客 673

彩票预测的可能性与算法分析

彩票作为一种以随机数为基础的赌博形式,吸引了大量希望一夜暴富的参与者。尽管许多人希望通过某种方式预测彩票结果,以增加中奖几率,但彩票预测的可能性依然是一个充满争议和误解的话题。本文将探讨彩票是否能够预测的理论依据,分析可能的预测算法,并解释这些算法为何在实际应用中难以奏效。

m0_38012434的博客 2万+

Netty与网络编程

Netty的特点1)高并发:基于 NIO(Nonblocking IO,非阻塞IO)开发,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高;2)传输快:传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输;3)封装好:封装了 NIO 操作的很多细节,提供了易于使用调用接口。Netty的优势1)使用简单:封装了 NIO 的很多细节,使用更简单;2)功能强大:预置了多种编解码功能,支持多种主流协议;

qq_36037795的博客 1724

WPS占用C盘空间解决方案与优化技巧

办公软件在长期使用过程中会产生大量缓存和历史文件,这些文件往往隐藏在系统目录中,导致磁盘空间被无声占用。以WPS为例,其版本历史备份、云同步缓存和模板库是主要空间占用源。通过分析文件存储机制,可以定位到具体的缓存路径,如AppData目录下的Kingsoft文件夹。针对这一问题,有效的解决方案包括手动清理、自动化脚本和注册表优化。对于需要持续监控的场景,可以结合PowerShell脚本和任务计划实现自动化管理。在实际应用中,这些技术不仅适用于WPS,也可迁移到其他办公软件的空间优化中,是每位IT运维人员和高

weixin_30788239的博客 453

YOLOv8【第九章:模型部署篇·第2节】一文搞懂,模型剪枝与压缩技术!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 1523

Java网络编程:socket与Netty

Socket 实现了TCP/IP协议,可以连接到网络上的上脑,并收发数据ServerSocket 用于服务端,可以监听服务端口,等待客户端连接,连接后可以生成用于传输数据的Socket创建服务端 创建客户端 客户端发送消息 服务端接收消息 也可以使用Netty很方便的实现socket编程 Netty介绍 简介 按照官方定义,它是一个异步的、基于事件 Client/Server 的网络框架,目标是提供一种简单、快速构建网络应用的方式,同时保证高吞吐量、低延时、高可靠性。 Netty 的

闫玉林的博客 7518

SCI论文发表很容易【5】:比较容易中的期刊

整理了一些经管方面比较容易中稿的期刊,可供大家参考

qq_25018077的博客 1675

Netty网络编程第二卷

Netty网络编程第二卷二. Netty 入门1. 概述1.1 Netty 是什么?1.2 Netty 的作者1.3 Netty 的地位1.4 Netty 的优势2. Hello World2.1 目标2.2 服务器端2.3 客户端2.4 流程梳理???? 提示3. 组件3.1 EventLoop普通任务和定时任务线程池默认大小???? 优雅关闭演示 NioEventLoop 处理 io 事件细分group组按照工作耗时再进行细分???? handler 执行中如何换人?演示 NioEventLoop 处理

m0_53157173的博客 1235

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【Java网络编程与IO流】Java之Netty网络编程

Java之Netty网络编程 为什么要学Netty? Netty基于NIO(NIO是一种同步非阻塞的I/O模型,在Java1.4中引入了NIO)。使用Netty可以极大地简化TCP和UP套接字服务器等网络编程,并且性能以及安全等很多方面非常优秀; 平常经常接触的 Dubbo、RocketMQ、Elasticsearch、gRPC、Spark、Elasticsearch 等等热门开源项目都用到了 Netty。 大部分微服务框架底层涉及到网络通信的部分都是基于 Netty 来做的,比如说 Spring Clo

Mind_programmonkey的博客 1212

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Netty:网络编程基础

Netty:网络编程基础

weixin_46619605的博客 1337

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Netty | 第1章 Java NIO 网络编程《Netty In Action》

本系列为 Netty 学习笔记,本篇介绍总结**Java NIO 网络编程**。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层 API 进行了封装,因此有必要对 Java 网络编程做个大概了解。本篇将重点放在 NIO 网络编程模型上,对 BIO 及 AIO 仅做简要说明;

多氯环己烷的博客 842
上一篇: redis实现session共享
下一篇: Netty——基本使用介绍
SOFT-WORKER
博客等级 码龄9年 440粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值