Netty原来就是这样啊(一)
前言:
当前对于整体而言来说就是说分为客户端以及服务端然后服务端接收到客户端的请求之后将客户端想要的结果返回给它,但是这个就需要建立一个连接进行数据的传输除此以外我们是通过网络进行传输的那么就需要网络协议 (TCP UDP),建立的连接就是叫Socket连接
Socket的具体介绍以及网络协议之间的区别:
Socket是一种基于基于TCP/IP协议的网络编程接口又叫做套接字,这个简单的理解就是进行数据传输进行连接的这个一般的话就是如客户端和服务端建立不同的连接,或者说在分布式的环境钟不同的服务之间的进行数据的同步的时候等等
这是客户端和服务端之间建立Socket连接,如我们要访问一个浏览器的时候那么这时候我们的请求就会发送个服务端之后服务端就会将网页的一些数据传输那么就会呈现出来了
TCP和UDP之间的区别: 1.从可靠性的角度来看的话,因为TCP建立连接的时候是需要进行三次握手断开连接的时候需要进行四次挥手但是UDP不需要这样做所以TCP更加可靠2.从传输的速度来看的话那么由于TCP更加可靠那么TCP的速率没有UDP的速率快
补充说明:对于网络编程而言是通过IP+端口+协议来进行唯一标识双方除此以外双方一定是相同的协议,端口号位于传输层,有两种协议,一种是TCP协议,一种是UDP协议
Netty:
一.什么是Netty:
Netty 是一个 NIO 客户端服务器框架,支持快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和精简了 TCP 和 UDP 套接字服务器等网络编程。
二.其他的网络编程的方式:
1.多线程的网络编程方式:
这个不难看出当我们客户端个服务端每次建立一次连接的时候都需要创建一个线程来处理一个请求,但是每次创建一个线程里面的栈的固定的大小为1M并且线程还会存在上下文切换
缺点:
如果在高并发的场景之下那么客户端发送多个请求的话那么就创建很多的线程这个是会导致内存不足的以及消耗大量CPU资源如果在极端的情况下就有可能会使得CPU的利用率到达百分之200
2.线程池的网络编程方式
使用线程池的方式的话那么就很好解决了多线程方式的缺点
线程池可以看我的文章线程池和多线程就是这样啊-CSDN博客
缺点:
由于阻塞队列的存在那么就在高并发的时候客户端和服务端建立连接之后一个线程池中的线程复制监视之后,但是由于一直没有数据的话那么这个线程就会一直没有数据那么就会导致这个线程处于阻塞的状态,这个在该并发的场景之下的话会使得更多的其他的请求没有来得及被处理就会进入到阻塞队列中进行排队等待
3.NIO的网络编程方式
三大组件:
Buffer(缓冲区):每个客户端连接都会对应一个Buffer,读写数据通过缓冲区读写。
Channel(通道):每个channel用于连接Buffer和Selector,通道可以进行双向读写。
Selector(选择器):一个选择器对应多个通道,用于监听多个通道的事件。Selector可以监听所有的channel是否有数据要读取,当某个channel有数据时,就去处理,所有channel都没有数据时,线程可以去执行其他任务。
相对于传统的网络编程而言不是需要一直进行等待,因为这样就是会导致这个Socket端没有数据的话那么线程处于阻塞这个性能就是比较低,所以NIO就是如果线程处理的这个Socket没有请求的话不会阻塞一直等待,对于Socket端的话那么服务端就是会专门创建一个线程负责管理Selector(多路复用器不断的轮询注册其上的Channel(通道)中的 I/O 事件,如果IO有数据传输的话那么就会让其他后台的线程进行一个处理(这个就是IO多路复用就是基于NIO)
三.为什么要有Netty
经过上面的内容我们发现已经存在了NIO这个网络编程的方式为什么还要出现Netty这个框架这个难道不是一种浪费嘛,在这里我想要告诉你不是的,下面以一个例子来进行说明
// 创建一个Selector实例Selector selector = Selector.open();// 创建一个ServerSocketChannel实例并绑定到指定端口ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.configureBlocking(false);serverSocket.bind(new InetSocketAddress(8080));// 将ServerSocketChannel注册到Selector上,监听Accept事件serverSocket.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 选择一组已经准备就绪的键// 返回值 0 超时// -1 错误//如果使用的是select的话如果没有任何的请求的话那么就会一直等待//select(long timeout)如果到了设置的时间还是没有请求的话那么就会返回int readyChannels = selector.select();if (readyChannels == 0) continue;// 获取已准备就绪的键的迭代器Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();// 如果是Accept事件,处理新连接if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);System.out.println("客户端进行连接了" );}// 如果是读取数据的请求的话if (key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer) key.attachment();buffer.clear();int read = socketChannel.read(buffer);if(read>0){String string = buffer.toString();System.out.println("有数据进行读取");}}// 移除已处理的键iterator.remove();}}}
原因如下:
1.这个代码总体来说就是比较复杂而且很多东西需要进行手动进行实现那么使得这个代码的可维护性就是会比较差
2.调用Selector.select()方法会获得的已经有事件发生的IO端但是如果处理完成之后不删除的话那么就会使得下次调用的时候还是会重新进行处理,这个对性能的影响是很大的