NIO基础

      tomcat集群的时候,在心跳通讯的时候,默认的接收器是NioReceiver,对NIO的使用是个比较经典的例子,在分析NIO之前,先看自己的一个NIO的小例子,代码:

 

public class HelloServer {
	private Selector selector;
	private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
	private String name;

	public HelloServer() throws IOException {
		selector = Selector.open();
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.socket().bind(new InetSocketAddress(8888));
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

	}

	public Selector getSelector() {
		return selector;
	}

	public void setSelector(Selector selector) {
		this.selector = selector;
	}

	// 开始监听
	public void listen() {
		try {
			for (;;) {
				// 选择已经准备好的通道,返回通道数,这个值是上次调用select()后到现在这个阶段
                               //已经准备好的通道
				int i = selector.select();
				Iterator iter = selector.selectedKeys().iterator();
				if (iter.hasNext()) {
					SelectionKey selectionKey = (SelectionKey) iter.next();
					// 处理完一个就需要删除一个SelectionKey 不然的话,一直堆积,cpu100%
					iter.remove();
					process(selectionKey);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void process(SelectionKey selectionKey) throws IOException {
		// 准备好接受新的套接字连接 这个只有ServerSocketChannel绑定的SelectionKey才有效
		if (selectionKey.isAcceptable()) {
			ServerSocketChannel server = (ServerSocketChannel) selectionKey
					.channel();
			SocketChannel channel = server.accept();
			// 设置非阻塞模式
			channel.configureBlocking(false);
			// 得到client的socket后可以使其在OP_READ状态,下个时段的selector.select()中就是selectionKey.isReadable()的了,可以执行下面这个分支了
			channel.register(selector, SelectionKey.OP_READ);
			System.out.println("accept:"+selectionKey.interestOps()+" "+selectionKey.readyOps());
		} else if (selectionKey.isReadable()) {
			// 可读,下面就是client读数据
			SocketChannel channel = (SocketChannel) selectionKey.channel();
			// int count = 0;
			int count = channel.read(byteBuffer);
			if (count > 0) {
				byte[] bbb = new byte[1024];
				byteBuffer.flip();
				bbb = byteBuffer.array();
				name = new String(bbb);
				System.out.println(name + "aaa");
				byteBuffer.clear();
			}
			// channel.close();这个可以关闭,可以不关闭,一般需要关闭(count==0),不然client的socket堆积的太多
			// channel.register(selector, SelectionKey.OP_WRITE);
			// 可以在注册感兴趣的时间,这个是往客户端写,下个select()
			// 时段就可以往client里面写了
			System.out.println("isReadable:"+selectionKey.interestOps()+" "+selectionKey.readyOps());
		} else if (selectionKey.isWritable()) {
			System.out.println("isWritable:"+selectionKey.interestOps());
			SocketChannel channel = (SocketChannel) selectionKey.channel();
			byteBuffer.reset();
			byteBuffer.put(name == null ? "acb".getBytes() : name.getBytes());
		}
	}
	public static void main(String[] args) {
		int port = 8888;
		try {
			HelloServer server = new HelloServer();
			System.out.println("listening on " + port);
			server.listen();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

 

public class HelloClient {
	static InetSocketAddress ip = new InetSocketAddress("localhost", 8888);
	static ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
	static class Message implements Runnable {
		protected String name;
		String msg = "";
		public Message(String index) {
			this.name = index;
		}
		public void run() {
			try {
				long start = System.currentTimeMillis();
				// 打开Socket通道
				SocketChannel client = SocketChannel.open();
				// 设置为非阻塞模式
				client.configureBlocking(false);
				// 打开选择器
				Selector selector = Selector.open();
				// 注册连接服务端socket动作
				client.register(selector, SelectionKey.OP_CONNECT);
				// 连接
				client.connect(ip);
				// 分配内存
				ByteBuffer buffer = ByteBuffer.allocate(1);
				int total = 0;

				_FOR: for (;;) {
					selector.select();
					Iterator iter = selector.selectedKeys().iterator();
					while (iter.hasNext()) {
						SelectionKey key = (SelectionKey) iter.next();
						iter.remove();
						if (key.isConnectable()) {
							SocketChannel channel = (SocketChannel) key
									.channel();
							if (channel.isConnectionPending())
								channel.finishConnect();
							
							byte [] aaa = name.getBytes();
							channel.write(buffer.wrap(aaa));
							
							//写完可以监听读,server的响应就可以在下个select()时段检测到了
							channel.register(selector, SelectionKey.OP_READ);
						} else if (key.isReadable()) {
							SocketChannel channel = (SocketChannel) key
									.channel();
							int count = channel.read(buffer);
							if (count > 0) {
								total += count;
								buffer.flip();

								while (buffer.remaining() > 0) {
									byte b = buffer.get();
									msg += (char) b;

								}
								buffer.clear();
							} else {
								client.close();
								break _FOR;
							}
						}
					}
				}
				double last = (System.currentTimeMillis() - start) * 1.0 / 1000;
				System.out.println(msg + "used time :" + last + "s.");
				msg = "";
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		String names[] = new String[10];
		for (int index = 0; index < 10; index++) {
			names[index] = "jeff[" + index + "]";
			new Thread(new Message(names[index])).start();
		}
	}
}

   基本代码解释都有,需要注意的是:

  1  selector的selectedKeys()方法返回的是上次select()方法调用后到现在为止的放入到就绪集里面的SelectionKey

      ,这个跟SelectionKey的interest和ready集合是没有关系的

  2.用完的SelectionKey必须从就绪集里面删除,不然的话会有堆积的

  3.在调用SelectableChannel.register方法时,就是channel和selector进行关联的时候,此时SelectionKey的interest和ready集合是一致的

 

当调用selector的select()方法,查看api知道需要做的事情如下:

 

   1. 将已取消键集中的每个键从所有键集中移除(如果该键是键集的成员),并注销其通道。此步骤使已取消键集成为空集。 

   2. 在开始进行选择操作时,应查询基础操作系统来更新每个剩余通道的准备就绪信息,
以执行由其键的相关集合所标识的任意操作。对于已为至少一个这样的操作准备就绪的通道,
执行以下两种操作之一: 

如果该通道的键尚未在已选择键集中,则将其添加到该集合中,并修改其准备就绪操作集,
以准确地标识那些通道现在已报告为之准备就绪的操作。丢弃准备就绪操作集中以前记录的所有准备就绪信息。 

如果该通道的键已经在已选择键集中,则修改其准备就绪操作集,
以准确地标识所有通道已报告为之准备就绪的新操作。保留准备就绪操作集以前记录的所有准备就绪信息;
换句话说,基础系统所返回的准备就绪操作集是和该键的当前准备就绪操作集按位分开 (bitwise-disjoined) 的。 

 3.如果在此步骤开始时键集中的所有键都有空的相关集合,则不会更新已选择键集和任意键的准备就绪操作集。 
如果在步骤 (2) 的执行过程中要将任意键添加到已取消键集中,则处理过程如步骤 (1)。 

  对应步骤2我的理解是:对应一个selectionKey,如果就绪集合里面有的话,只是把这个key的状态和集合里面这个key已有的状态或操作,集合没有的话就直接set进去.

 下面看NioReceiver的代码:

 NioReceiver实现了Runnable接口,肯定会在线程中被执行,我们看他的run()方法:

    public void run() {
        try {
            listen();
        } catch (Exception x) {
            log.error("Unable to run replication listener.", x);
        }
    }
 

   调用了listen()方法,看代码:

 

 

    protected void listen() throws Exception {
        if (doListen()) {
            log.warn("ServerSocketChannel already started");
            return;
        }
        
        setListen(true);

        while (doListen() && selector != null) {
            // this may block for a long time, upon return the
            // selected set contains keys of the ready channels
            try {
                events();
                socketTimeouts();
                int n = selector.select(getTcpSelectorTimeout());
                if (n == 0) {

                    continue; // nothing to do
                }
                // get an iterator over the set of selected keys
                Iterator it = selector.selectedKeys().iterator();
                // look at each key in the selected set
                while (it.hasNext()) {
                    SelectionKey key = (SelectionKey) it.next();
                    // Is a new connection coming in?
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        SocketChannel channel = server.accept();
                        channel.socket().setReceiveBufferSize(getRxBufSize());
                        channel.socket().setSendBufferSize(getTxBufSize());
                        channel.socket().setTcpNoDelay(getTcpNoDelay());
                        channel.socket().setKeepAlive(getSoKeepAlive());
                        channel.socket().setOOBInline(getOoBInline());
                        channel.socket().setReuseAddress(getSoReuseAddress());
                        channel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());
                        channel.socket().setTrafficClass(getSoTrafficClass());
                        channel.socket().setSoTimeout(getTimeout());
                        Object attach = new ObjectReader(channel);
                        registerChannel(selector,
                                        channel,
                                        SelectionKey.OP_READ,
                                        attach);
                    }
                    // is there data to read on this channel?
                    if (key.isReadable()) {
                        readDataFromSocket(key);
                    } else {
                        key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
                    }

                    // remove key from selected set, it's been handled
                    it.remove();
                }
            } catch (java.nio.channels.ClosedSelectorException cse) {
                // ignore is normal at shutdown or stop listen socket
            } catch (java.nio.channels.CancelledKeyException nx) {
                log.warn("Replication client disconnected, error when polling key. Ignoring client.");
            } catch (Throwable x) {
                try {
                    log.error("Unable to process request in NioReceiver", x);
                }catch ( Throwable tx ) {
                    //in case an out of memory error, will affect the logging framework as well
                    tx.printStackTrace();
                }
            }

        }
        serverChannel.close();
        if (selector != null)
            selector.close();
    }

   前面是设置了一些标志位,接下来调用了events()方法,执行NioReplicationTask的里面的一些任务的一个列表。

  每一个event都是Runnable,比较怪的是在events()方法中,执行的是runnable的run()的方法,也不是线程的执行方 法.

  接着是socketTimeouts()方法调用,关闭一些已经超时的channel,接着就是根据SelectionKey的ready集合进行操作,包括注册channel,读channel的数据,之后如果NioReceiver不需要监听,进行一些资源的关闭和回收

 看下readDataFromSocket()方法的定义:

 

    protected void readDataFromSocket(SelectionKey key) throws Exception {
        NioReplicationTask task = (NioReplicationTask) getTaskPool().getRxTask();
        if (task == null) {
            // No threads/tasks available, do nothing, the selection
            // loop will keep calling this method until a
            // thread becomes available, the thread pool itself has a waiting mechanism
            // so we will not wait here.
            if (log.isDebugEnabled()) log.debug("No TcpReplicationThread available");
        } else {
            // invoking this wakes up the worker thread then returns
            //add task to thread pool
            task.serviceChannel(key);
            getExecutor().execute(task);
        }
    }
   

   首先从一个资源池中拿到NioReplicationTask,有可能资源池没有空闲资源,此时返回null,那么这次读就丢失了,不过没有关系,readDataFromSocket()方法是在一个循环中,会一直调用这个方法,只是这次的数据丢失了,如果返回不为空,则执行这个Task.

 

    // loop forever waiting for work to do
    public synchronized void run() { 
        if ( buffer == null ) {
            if ( (getOptions() & OPTION_DIRECT_BUFFER) == OPTION_DIRECT_BUFFER) {
                buffer = ByteBuffer.allocateDirect(getRxBufSize());
            } else {
                buffer = ByteBuffer.allocate(getRxBufSize());
            }
        } else {
            buffer.clear();
        }
        if (key == null) {
            return;	// just in case
        }
        if ( log.isTraceEnabled() ) 
            log.trace("Servicing key:"+key);

        try {
            ObjectReader reader = (ObjectReader)key.attachment();
            
            
            //如果SelectionKey上面没有reader的话,这个SelectionKey可以取消此键的通道到其选择器的注册
            //以及关闭SelectionKey的channel和socket
            if ( reader == null ) {
                if ( log.isTraceEnabled() ) 
                    log.trace("No object reader, cancelling:"+key);
                cancelKey(key);
            } else {
                if ( log.isTraceEnabled() ) 
                    log.trace("Draining channel:"+key);

                drainChannel(key, reader);
            }
        } catch (Exception e) {
            //this is common, since the sockets on the other
            //end expire after a certain time.
            if ( e instanceof CancelledKeyException ) {
                //do nothing
            } else if ( e instanceof IOException ) {
                //dont spew out stack traces for IO exceptions unless debug is enabled.
                if (log.isDebugEnabled()) log.debug ("IOException in replication worker, unable to drain channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].", e);
                else log.warn ("IOException in replication worker, unable to drain channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].");
            } else if ( log.isErrorEnabled() ) {
                //this is a real error, log it.
                log.error("Exception caught in TcpReplicationThread.drainChannel.",e);
            } 
            cancelKey(key);
        } finally {

        }
        key = null;
        // done, ready for more, return to pool
        getTaskPool().returnWorker (this);
    }
 

   这个Task里面,最主要的是调用drainChannel()方法,

    protected void drainChannel (final SelectionKey key, ObjectReader reader) throws Exception {
    	
    	//从SelectionKey中得到数据
        reader.setLastAccess(System.currentTimeMillis());
        reader.access();
        SocketChannel channel = (SocketChannel) key.channel();
        int count;
        buffer.clear();			// make buffer empty

        // loop while data available, channel is non-blocking
        while ((count = channel.read (buffer)) > 0) {
            buffer.flip();		// make buffer readable
            if ( buffer.hasArray() ) 
                reader.append(buffer.array(),0,count,false);
            else 
                reader.append(buffer,count,false);
            buffer.clear();		// make buffer empty
            //do we have at least one package?
            if ( reader.hasPackage() ) break;
        }

        int pkgcnt = reader.count();
        
        if (count < 0 && pkgcnt == 0 ) {
            //end of stream, and no more packages to process
            remoteEof(key);
            return;
        }
        
        ChannelMessage[] msgs = pkgcnt == 0? ChannelData.EMPTY_DATA_ARRAY : reader.execute();
        
        registerForRead(key,reader);//register to read new data, before we send it off to avoid dead locks
        
        for ( int i=0; i<msgs.length; i++ ) {
            /**
             * Use send ack here if you want to ack the request to the remote 
             * server before completing the request
             * This is considered an asynchronized request
             */
            if (ChannelData.sendAckAsync(msgs[i].getOptions())) sendAck(key,channel,Constants.ACK_COMMAND);
            try {
                if ( Logs.MESSAGES.isTraceEnabled() ) {
                    try {
                        Logs.MESSAGES.trace("NioReplicationThread - Received msg:" + new UniqueId(msgs[i].getUniqueId()) + " at " + new java.sql.Timestamp(System.currentTimeMillis()));
                    }catch ( Throwable t ) {}
                }
                //process the message  ReceiverBase.messageDataReceived()
                getCallback().messageDataReceived(msgs[i]);
                /**
                 * Use send ack here if you want the request to complete on this 
                 * server before sending the ack to the remote server
                 * This is considered a synchronized request
                 */
                if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.ACK_COMMAND);
            }catch ( RemoteProcessException e ) {
                if ( log.isDebugEnabled() ) log.error("Processing of cluster message failed.",e);
                if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.FAIL_ACK_COMMAND);
            }catch ( Exception e ) {
                log.error("Processing of cluster message failed.",e);
                if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.FAIL_ACK_COMMAND);
            }
            if ( getUseBufferPool() ) {
                BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());
                msgs[i].setMessage(null);
            }
        }                        
        
        if (count < 0) {
            remoteEof(key);
            return;
        }
    }
 

    比较经典的是:在这个Task执行前,传入的SelectionKey的OP_READ事件被屏蔽,在这个方法中数据读完后,重新开时这个事件,这里不是很明白,暂且认为,这样子SelectionKey带的数据不会被下一次的数据所污染,因为下一次通道发的数据在下一次select的时候是不会在SelectionKey的interest集合里面的,也就不可能在ready集合里面了。

最后面只是发送一个ACK的应答数据.

 

 

相比自己写的例子和tomcat的例子,差距在细节,包括异常的处理,各种临界状态的处理,线程的同步和阻塞唤醒。写一个主体常常很容易,但是各个细节的考虑却是功力的体现

 

 

 

 

 

kemeas算法继续优化——elkan kmeas算法 题记:又是凌晨3点的夜 elkan kmeans算法 在我的上一篇博客《kmeans算法性能改进_kmeans++算法+kmeans++优化算法+距离计算优化》中,在一般kmeans算法的基础上,添加了对于初始条件和距离计算更上的一些优化技巧。这篇博客聊一聊elkan算法,其实也是对于kmeans运行速度的优化算法。主要作用与kmeans的距离计算过程。 原理 假如我们已经知道任意两个样本之间的距离,再假设我们已经知道了样本1与聚类中心1之间的距离,根据三角性两边之和大于第三边,c+d>a,所以如果 阅读详情

相关推荐

【IIC通信】Chap.1(I2C)IIC通信原理、IIC读写时序详解

IIC通信以两根连线SDA、SCL实现了完善的全双工同步数据传送, 可以方便地构成多机系统和外围器件扩展系统。

Jie_MSD的博客 2万+

解决Java中的IOException异常

IOException是Java中的一个受检查异常(Checked Exception)。它是java.io包中定义的异常类之一,用于表示输入输出操作期间可能发生的错误或异常情况。

weixin_45934981的博客 3万+

Verilog使用inout信号的方法

在FPGA的设计过程中,有时候会遇到双向信号(既能作为输出,也能作为输入的信号叫双向信号)。比如,IIC总线中的SDA信号就是一个双向信号,QSPI Flash的四线操作的时候四根信号线均为双向信号。在Verilog中用关键字inout定义双向信号,这里总结一下双向信号的处理方法。  实际上,双向信号的本质是由一个三态门组成的,三态门...

qq_41687938的博客 1万+

Tomcat NIO-NioReceiver

org.apache.catalina.tribes.transport.nio.NioReceiver package org.apache.catalina.tribes.transport.nio; import java.io.IOException; import java.net.ServerSocket; import java.nio.channels.CancelledK

guolong1983811的专栏 528

FPGA新手必看:Quartus生成jic文件全流程详解(附AS/JTAG模式选择指南)

本文详细解析了在Quartus中生成JIC文件以固化FPGA程序到Flash的全流程。核心在于理解JIC文件作为连接JTAG调试与AS固化的桥梁作用,并重点阐述了Convert Programming Files工具的关键配置步骤、AS与JTAG模式的选择逻辑,以及烧录过程中的常见问题排查指南,帮助开发者实现程序断电不丢失。

weixin_29271053的博客 275

【Linux】实际项目应该如何完成?--安防监控系统项目的总结

到目前为止我也做个5、6个嵌入式项目,但以往都是从零开始自己做,也就是毫无计划,想到那里做那里,有想法就会去实现,就这样导致我碰到了很多坑,也消耗了我许多时间,当然,不是说这样不好,因为在其中我也得到了一个很好的锻炼与练习,但是这不适用于在工作中去这样做。...

修成真的博客 6726

tomcat集群

对于WEB应用集群的技术实现而言,最大的难点就是如何能在集群中的多个节点之间保持数据的一致性,会话(Session)信息是这些数据中最重要的一块。要实现这一点,大体上有两种方式,一种是把所有Session数据放到一台服务器上或者数据库中,集群中的所有节点通过访问这台Session服务器来获取数据;另一种就是在集群中的所有节点间进行Session数据的同步拷贝,任何一个节点均保存了所有的Sessi...

tomhibolu 451

IP路由原理、静态路由及动态路由区分

什么是路由 路由是指导IP报文发送的路径信息。 直连路由:目的地所在网络与路由器直接相连。 间接路由:目的地所在网络与路由器非直接相连。 静态路由是由管理员手工配置而生成的路由。 动态路由路由器使用路由协议从其他路由器那里获悉的路由。

weixin_42227328的博客 5469

thift-transport层

TTransport=&gt;TIOStreamTransport=&gt;TSocket 重要参数设置:   socket_.setSoLinger(false, 0); socket_.setTcpNoDelay(true); socket_.setSoTimeout(timeout_);//客户端读取超时时间 socket_.connect(new InetSocketAdd...

wuhuajun 223

10、犹豫双模糊TOPSIS算法:原理与实例解析

本文介绍了犹豫双模糊TOPSIS算法的基本原理与应用步骤,结合中国四家纺织公司的案例,详细解析了从构建犹豫双模糊决策矩阵、归一化处理、专家信息聚合,到确定正负理想解、计算距离与相对贴近度系数的完整决策流程。该方法有效应对决策者在评价过程中的犹豫性,适用于财务评估、项目选择等复杂多属性决策场景,具有较强的实用性和推广价值。

yhn45678901的博客 122

WPF在特定领域的应用:打造一款专业的图像编辑工具

WPF在特定领域的应用:打造一款专业的图像编辑工具,在软件开发的广阔领域中,Windows Presentation Foundation(WPF)以其强大的图形渲染能力和丰富的用户界面控件,为开发者提供了构建高性能、美观应用程序的平台。图像编辑工具作为一种对图形处理要求极高的应用类型,WPF 的特性使其成为理想的选择。本文将深入探讨如何使用 WPF 打造一款专业的图像编辑工具,涵盖从基础概念到实际代码实现的各个方面。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 6万+

网络编程之SocketChannel & ServerSocketChannel & Selector

在前面的文章中我们讲过Socket的读是阻塞模式,并且读写都是一个字节或者几个字节,为了提高响应性能往往需要在服务端为每一个请求分配一个线程。后来为了解决传统Socket的这种阻塞低效方式,在jdk1.4之后引入了New I/o(NIO)。

远方和诗 的博客 2241

OpenClaw人人养虾:配置通义千问(Qwen)

通义千问(Qwen)是阿里云推出的大语言模型系列。OpenClaw 支持通过 Qwen Portal 插件实现 OAuth 免费额度认证,每日可免费使用 2000 次请求,无需绑定支付方式。

王国平的博客 1257

NIO基础知识

在学习Netty之前先要学习一下NIO相关的知识,因为Netty是基于NIO搭建的一套网络编程框架。

m0_74229735的博客 1504

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

NIO 基础知识学习

NIO基础知识学习,以及NIO服务端、客户端编程过程。

cuiyaoqiang的博客 553

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Netty学习之NIO基础

Channel与BufferJava NIO系统的核心通道(Channel)和缓冲区(Buffer)。通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理简而言之,通道负责传输,缓冲区负责存储常见的Channel有以下四种,其中FileChannel主要用于文件传输,其余三种用于网络通信Buffer有以下几种,其中使用较多的是ByteBufferByteBuffer。

qq_29012499的博客 1453

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

NIO 基础总结

文章目录一. NIO 基础1. 三大组件1.1 Channel & Buffer1.2 Selector多线程版设计⚠️ 多线程版缺点线程池版设计⚠️ 线程池版缺点selector 版设计2. ByteBuffer2.1 ByteBuffer 正确使用姿势2.2 ByteBuffer 结构💡 调试工具类2.3 ByteBuffer 常见方法分配空间向 buffer 写入数据从 buffer 读取数据mark 和 reset字符串与 ByteBuffer 互转⚠️ Buffer 的线程安全2.4 S

batman 1615

NIO网络通信基础

本文摘要: 文章详细介绍了NIO(非阻塞IO)的核心机制与实现原理。首先对比了BIO(阻塞IO)的局限性,指出NIO通过Reactor模式和多路复用机制实现单线程处理多连接。重点解析了NIO三大组件:Selector(事件调度器)、Channel(双向数据通道)和Buffer(数据缓冲区),以及连接、读写等核心事件类型。通过Socket通信模型图解,说明了服务端与客户端的事件处理流程。文章深入剖析了Reactor模式的三种实现方案,并通过服务端代码示例演示了NIO的实际应用,包括事件注册、轮询处理等关键步骤

西岭千秋雪的博客 1012

【Netty之NIO基础篇】

一. NIO 基础 non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel,而之前的 stream 要么是输入,要么是输出,channel 比 stream 更为底层 #mermaid-svg-Yr9Ez8EmQvBnHoZA {font-family:"trebuchet ms",

诗和远方,代码和你 318

10. Java基础之NIO

Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。 NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

weixin_45267102的博客 385

Netty学习——NIO基础与IO模型

导学 Socket和NIO的区别 Socket和NIO是Java中用于网络编程的两个不同的API,具有不同的设计理念和用途。以下是它们的主要区别: 1. 定义 Socket: Socket是Java中用于实现网络通信的传统API,通常被称为Java I/O(输入/输出)。 它采用阻塞式I/O模型,适合于简单的网络通信。 NIO (New Input/Output): NIO是Java 1.4引入的一种新的I/O库,提供了非阻塞I/O操作。 它采用事件驱动和选择器(Selector)模式,适合于高并

maruofan666的博客 1560
上一篇: tomcat的Http11Protocol,Http11Processor
下一篇: tomcat NioSender
make0620
博客等级 码龄19年 0粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值