`
suhuanzheng7784877
  • 浏览: 702636 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Ff8d036b-05a9-33b5-828a-2633bb68b7e6
读金庸故事,品程序人生
浏览量:47710
社区版块
存档分类
最新评论

3种下载文件程序的思考,为何使用NIO进行异步网络通讯

    博客分类:
  • NIO
阅读更多

1.  前言

现在很多做网络通讯中间代理层的通讯都是使用Java1.4以后推出的NIO进行编写,现在还有很多开源的框架也是封装了NIO的书写细节来帮助大家简写异步非阻塞通讯服务。像MySql的代理中间件amoeba-mysql-proxy就是采用NIO的方式处理client端过来的request,之后与Mysql-Server层的通讯也是采用NIO进行命令消息发送的。再看咱们JavaEye首页介绍的项目xmemcached,其中作者Dennis是其xmemcached的开发人,他也是通过NIO的方式与memcachedServer进行异步通讯的,Dennis的另一个项目yanf4j就是一个NIO框架,xmemcache也是借助这个NIO框架实现的异步非阻塞方式的网络通讯,ApacheMINA框架都是NIO的封装再实现。

那么我们就来回顾一下以前的处理方式,来看看为什么现在要使用NIO来进行异步非阻塞方式的通讯吧,网上很多文章都是几句话将NIO和原始的socket通讯的优劣一带而过,我们这次用一个简单的下载大文件的网络服务程序进行说明。使用3种模式来说明,分别是同步单独线程服务运行模式、传统阻塞多线程模式、使用NIO异步非阻塞模式。

我们设置服务器上有一个1.81GB的电影,格式为RMVB。使用Server进行服务监听,客户端请求到Server,建立网络通讯,进行电影下载。

2.  同步单线程阻塞

使用同步单线程下载,是最原始的socket通讯,服务端的代码如下

package server;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * liuyan
 */
public class FilmServer {

	public static void main(String[] args) {
		FilmServer ms = new FilmServer();
		try {
			ms.server();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 服务器端响应请求
	 * 
	 * @throws Exception
	 */
	public void server() throws Exception {

		// 0.建立服务器端的server的socket
		ServerSocket ss = new ServerSocket(9999);

		while (true) {

			// 1.打开socket连接
			// 等待客户端的请求
			final Socket server = ss.accept();

			System.out.println("服务-----------请求开始start");

			// 2.打开socket的流信息,准备下面的操作
			final InputStream is = server.getInputStream();
			byte b[] = new byte[1024];

			int readCount = is.read(b);

			String str = new String(b);

			str = str.trim();

			final String serverFileName = str;

			// 3.对流信息进行读写操作
			System.out.println("客户端传过来的信息是:" + str);

			System.out.println("线程" + Thread.currentThread().getName() + "启动");

			try {

				FileInputStream fileInputStream = new FileInputStream(
						serverFileName);

				// 3.1 服务器回复客户端信息(response)
				OutputStream os = server.getOutputStream();

				byte[] bfile = new byte[1024];

				// 往客户端写
				while (fileInputStream.read(bfile) > 0) {
					os.write(bfile);
				}

				fileInputStream.close();

				os.close();

				// 4.关闭socket
				// 先关闭输入流
				is.close();

				// 最后关闭socket
				server.close();

			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			System.out.println("服务-----------请求结束over");
		}

	}

}

 服务端这么写代码会有什么问题?咱们先来看客户端代码,之后运行后就知道了。

package client;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * liuyan
 * @version 1.0
 */
public class FilmClient {
	public static void main(String[] args) {
		for (int i = 1; i <= 2; i++) {
			Client client = new Client();
			client.i = i;
			client.start();
		}
	}
}

class Client extends Thread {

	int i;

	@Override
	public void run() {

		// 1.建立scoket连接
		Socket client;
		try {
			client = new Socket("127.0.0.1", 9999);

			// 2.打开socket的流信息,准备下面的操作
			OutputStream os = client.getOutputStream();

			// 3.写信息
			os.write(("d://film//2.rmvb").getBytes());
			
			String filmName = "c://io"+i+".rmvb";
			
			FileOutputStream fileOutputStream = new FileOutputStream(filmName);

			// 3.1接收服务器端的反馈
			InputStream is = client.getInputStream();
			byte b[] = new byte[1024];
			
			while(is.read(b)>0){
				fileOutputStream.write(b);
			}

			// 4.关闭socket
			// 先关闭输出流
			os.close();

			// 最后关闭socket
			client.close();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 客户端启动了2个线程进行下载电影的工作,先启动服务端,再运行客户端,会看笔者本地的硬盘C分区到有如下效果。

 可以看到线程2的下载任务一直是0字节,等第一个线程下载完成后呢,线程2的下载任务才能进行。



 

服务端的代码造成的问题就是使用传统的sokect网络通讯,那么另一个客户端的线程请求到server端的时候就发生了阻塞的情况,也就是说,服务端相当一个厕所,厕所就有只有一个坑位,来了一个人,相当于客户端请求,那这个人相当于就把坑位给占了,write操作和read操作会阻塞,这个人还没解决完问题呢,下个人就来了,没办法,哥们儿先在门外等等啊,等前一个客户爽完了再给您提供服务好吧。那么如何解决这个占着坑位不让别人用的情况呢?

3.  阻塞的多线程

为了解决以上问题,那么之后很多Server肯定不可能像以上程序那么做,不过以前很多Server都是基于单线程服务改造一下,做成多线程的Server的通讯,修改一下上面的Server代码,如下

package server;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 
 */
public class FilmServerNewThread {

	public static void main(String[] args) {
		FilmServerNewThread ms = new FilmServerNewThread();
		try {
			ms.server();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 服务器端响应请求
	 * 
	 * @throws Exception
	 */
	public void server() throws Exception {

		// 0.建立服务器端的server的socket
		ServerSocket ss = new ServerSocket(9999);

		while (true) {

			// 1.打开socket连接
			// 等待客户端的请求
			final Socket server = ss.accept();

			System.out.println("服务-----------请求开始start");

			// 2.打开socket的流信息,准备下面的操作
			final InputStream is = server.getInputStream();
			byte b[] = new byte[1024];

			int readCount = is.read(b);

			String str = new String(b);

			str = str.trim();

			final String serverFileName = str;

			// 3.对流信息进行读写操作
			System.out.println("客户端传过来的信息是:" + str);

			if (readCount > 0) {
				new Thread() {

					@Override
					public void run() {

						System.out.println("线程"
								+ Thread.currentThread().getName() + "启动");

						try {

							FileInputStream fileInputStream = new FileInputStream(
									serverFileName);

							// 3.1 服务器回复客户端信息(response)
							OutputStream os = server.getOutputStream();

							byte[] bfile = new byte[1024];

							// 往客户端写
							while (fileInputStream.read(bfile) > 0) {
								os.write(bfile);
							}

							fileInputStream.close();

							os.close();

							// 4.关闭socket
							// 先关闭输入流
							is.close();

							// 最后关闭socket
							server.close();

						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}.start();
			}

			System.out.println("服务-----------请求结束over");
		}

	}
}

 以上的Server就是在原始的socket基础上加了线程,每一个Client请求过来后,整个Server主线程不必处于阻塞状态,接收请求后直接另起一个新的线程来处理和客户端的交互,就是往客户端发送二进制包。这个在新线程中虽然阻塞,但是对于服务主线程没有阻塞的影响,主线程依然通过死循环监听着客户端的一举一动。另一个客户端的线程发起请求后就再起一个新的线程对象去为客户端服务。执行效果如下

 

2个线程互不影响,各自下载各自的。当然从非常严格的意义来讲,str变量在十分高并发的情况下有线程安全问题,这个咱暂且忽略,就着眼于低并发的情况。这个问题是什么呢,就是如果客户端请求比较多了,那么为每一个客户端开辟一个新的线程对象来处理网络传输的请求,需要创建个线程对象,而且这个线程对象从时间上来讲还是处于长连接,这个就比较消费系统资源,这个打开进程管理器就可以看到。而且每一个线程内部都是阻塞的,也没有说完全利用好这个新创建的线程。还拿刚才上厕所举例子,好比现在不止一个坑位了,来了一个用户我这边就按照工程师的厕所坑位图建立一个新的坑位,客户来了,不用等待老坑位,用新创建的坑位就行了。等那个老坑位用完了,自然有垃圾回收器去消灭那个一次性的坑位的,腾出资源位置为了建立新的坑位。长时间连接的意思,相当于这个人上厕所的时间非常长,便秘??需要拉一天才能爽完……老的坑位一时半会儿回收不了,新的坑位需要有空间为其建造茅房以便满足客户端的“急切方便”需要。久而久之,线程数目一多,系统就挂了的概率就增多了(谁也别想上,全玩完了)。

4.  异步非阻塞

使用JDK1.4NIO可以适当的解决上面的问题,异步 I/O 是一种 没有阻塞地读写数据的方法。通常,在代码进行 read() 调用时,代码会阻塞直至有可供读取的数据。同样, write() 调用将会阻塞直至数据能够写入。异步 I/O 调用不会阻塞。相反,您将注册对特定 I/O 事件的兴趣可读的数据的到达、新的套接字连接,等等,而在发生这样的事件时,系统将会告诉您。异步 I/O 的一个优势在于,它允许您同时根据大量的输入和输出执行 I/O。同步程序常常要求助于轮询,或者创建许许多多的线程以处理大量的连接。使用异步 I/O,您可以监听任何数量的通道上的事件,不用轮询,也不用额外的线程。还是举上公共厕所例子,虽然这个例子有点臭臭的。您现在有“便便”的需求了,不用那么麻烦,看看公共厕所是否有人占领,也不用给您另起个新坑位,您就拿一根我们服务端定制的容器和一个很粗管子,这个坐便器的大小因您那个地方的尺寸而定,坐便器往您的那个地方一放,再将坐便器和管子一连接,OK,您就敞开了“爽”吧。不用担心,这个管子自然会连接到相应的肥料厂家,将您的排泄物有效回收加以利用的。您完了事,擦擦屁股,关上管子该干嘛还干嘛就行了。另一个人也有这个需求,没问题,每个要我们提供服务的人都用这根管子,和自己的坐便器就行了,管子很粗,谁来连这个管子都行,有多少都行啊。下面我们来看基于NIO的网络下载程序

package server;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;

/**
 * 
 * @author liuyan
 *
 */
public class NIOServer {
	static int BLOCK = 500*1024;

	/**
	 * 处理客户端的内部类,专门负责处理与用户的交互
	 */
	public class HandleClient {
		protected FileChannel channel;
		protected ByteBuffer buffer;
		String filePath;
		
		 /**
		  * 构造函数,文件的管道初始化
		  * @param filePath
		  * @throws IOException
		  */
		public HandleClient(String filePath) throws IOException {
			
			//文件的管道
			this.channel = new FileInputStream(filePath).getChannel();
			
			//建立缓存
			this.buffer = ByteBuffer.allocate(BLOCK);
			this.filePath = filePath;
		}
		
		/**
		 * 读取文件管道中数据到缓存中
		 * @return
		 */
		public ByteBuffer readBlock() {
			try {
				
				//清除缓冲区的内容,posistion设置为0,limit设置为缓冲的容量大小capacity
				buffer.clear();
				
				//读取
				int count = channel.read(buffer);
				
				//将缓存的中的posistion设置为0,将缓存中的limit设置在原始posistion位置上
				buffer.flip();
				if (count <= 0)
					return null;
			} catch (IOException e) {
				e.printStackTrace();
			}
			return buffer;
		}
		
		/**
		 * 关闭服务端的文件管道
		 */
		public void close() {
			try {
				channel.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	protected Selector selector;
	protected String filename = "d:\\film\\60.rmvb"; // a big file
	protected ByteBuffer clientBuffer = ByteBuffer.allocate(BLOCK);
	protected CharsetDecoder decoder;
	
	//构造服务端口,服务管道等等
	public NIOServer(int port) throws IOException {
		selector = this.getSelector(port);
		Charset charset = Charset.forName("GB2312");
		decoder = charset.newDecoder();
	}

	// 获取Selector
	//构造服务端口,服务管道等等
	protected Selector getSelector(int port) throws IOException {
		ServerSocketChannel server = ServerSocketChannel.open();
		Selector sel = Selector.open();
		server.socket().bind(new InetSocketAddress(port));
		server.configureBlocking(false);
		
		//刚开始就注册链接事件
		server.register(sel, SelectionKey.OP_ACCEPT);
		return sel;
	}

	// 服务启动的开始入口
	public void listen() {
		try {
			for (;;) {
				
				//?
				selector.select();
				Iterator<SelectionKey> iter = selector.selectedKeys()
						.iterator();
				while (iter.hasNext()) {//首先是最先感兴趣的连接事件
					SelectionKey key = iter.next();
					
					//
					iter.remove();
					
					//处理事件
					handleKey(key);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 处理事件
	protected void handleKey(SelectionKey key) throws IOException {
		if (key.isAcceptable()) { // 接收请求
			
			//允许网络连接事件
			ServerSocketChannel server = (ServerSocketChannel) key.channel();
			SocketChannel channel = server.accept();
			channel.configureBlocking(false);
			
			//网络管道准备处理读事件
			channel.register(selector, SelectionKey.OP_READ);
		} else if (key.isReadable()) { // 读信息
			SocketChannel channel = (SocketChannel) key.channel();
			
			//从客户端读过来的数据块
			int count = channel.read(clientBuffer);
			if (count > 0) {
				
				//读取过来的缓存进行有效分割,posistion设置为0,保证从缓存的有效位置开始读取,limit设置为原先的posistion上
				//这样一来从posistion~limit这段缓存数据是有效,可利用的
				clientBuffer.flip();
				
				//对客户端缓存块进行编码
				CharBuffer charBuffer = decoder.decode(clientBuffer);
				System.out.println("Client >>download>>" + charBuffer.toString());
				
				//对网络管道注册写事件
				SelectionKey wKey = channel.register(selector,
						SelectionKey.OP_WRITE);
				
				//将网络管道附着上一个处理类HandleClient,用于处理客户端事件的类
				wKey.attach(new HandleClient(charBuffer.toString()));
			} else{
				//如客户端没有可读事件,关闭管道
				channel.close();
			}
				
			clientBuffer.clear();
		} else if (key.isWritable()) { // 写事件
			SocketChannel channel = (SocketChannel) key.channel();
			
			//从管道中将附着处理类对象HandleClient取出来
			HandleClient handle = (HandleClient) key.attachment();
			
			//读取文件管道,返回数据缓存
			ByteBuffer block = handle.readBlock();
			if (block != null){
				//System.out.println("---"+new String(block.array()));
				
				//写给客户端
				channel.write(block);
			}else {
				handle.close();
				channel.close();
			}
		}
	}

	public static void main(String[] args) {
		int port = 12345;
		try {
			NIOServer server = new NIOServer(port);
			System.out.println("Listernint on " + port);
			while (true) {
				server.listen();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 ServerSocketChannel相当于我们说的那个大粗管子,在它上面注册了很多这个管子感兴趣的事件,比如大便、小便、酒醉后吐的污浊都是它关心的。至于谁来控制管道应该关心的事件,是由管道通过Selector注册事件完成的,Selector相当于一个大管道的维护员了。管道必须得有服务商的厂家维护吧,不能滥用吧。Selector就是个管家,负责管道的事件监听的。XXXXBuffer相当于咱们说的坐便器,它是以块为单位进行管道疏通的,假如您的尺寸特别大,估计您排出的那个玩意也小不了,就配置一个大点的缓存传给服务那边,当然,您这边得到的服务端返回的加工后肥料,返给您的也是和您配置的尺寸有关系的。客户端的代码如下

package client;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * @author liuyan
 *
 */
public class NIOClient {
	static int SIZE = 2;
	final static int bufferSize = 500 * 1024;
	static InetSocketAddress ip = new InetSocketAddress("localhost", 12345);
	static CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();

	static class Download implements Runnable {
		protected int index;
		String outfile = null;

		public Download(int index) {
			this.index = index;
			this.outfile = "c:\\" + index + ".rmvb";
		}

		public void run() {

			FileOutputStream fout = null;
			// FileChannel fcout = null;
			try {
				fout = new FileOutputStream(outfile);
				// fcout = fout.getChannel();
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			try {
				long start = System.currentTimeMillis();

				// 打开客户端socket管道
				SocketChannel client = SocketChannel.open();

				// 客户端的管道的通讯模式
				client.configureBlocking(false);

				// 选择器
				Selector selector = Selector.open();

				// 往客户端管道上注册感兴趣的连接事件
				client.register(selector, SelectionKey.OP_CONNECT);

				// 配置IP
				client.connect(ip);

				// 配置缓存大小
				ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
				int total = 0;
				FOR: for (;;) {

					// 阻塞,返回发生感兴趣事件的数量
					selector.select();

					// 相当于获得感兴趣事件的集合迭代
					Iterator<SelectionKey> iter = selector.selectedKeys()
							.iterator();

					while (iter.hasNext()) {
						
						SelectionKey key = iter.next();
						
						System.out.println("-----Thread "
								+ index + "------------------"+key.readyOps());

						// 删除这个马上就要被处理的事件
						iter.remove();

						// 感兴趣的是可连接的事件
						if (key.isConnectable()) {

							// 获得该事件中的管道对象
							SocketChannel channel = (SocketChannel) key
									.channel();

							// 如果该管道对象已经连接好了
							if (channel.isConnectionPending())
								channel.finishConnect();

							// 往管道中写一些块信息
							channel.write(encoder.encode(CharBuffer
									.wrap("d://film//1.rmvb")));

							// 之后为该客户端管道注册新的感兴趣的事件---读操作
							channel.register(selector, SelectionKey.OP_READ);
						} else if (key.isReadable()) {

							// 由事件获得通讯管道
							SocketChannel channel = (SocketChannel) key
									.channel();

							// 从管道中读取数据放到缓存中
							int count = channel.read(buffer);
							System.out.println("count:" + count);
							if (count > 0) {

								// 统计读取的字节数目
								total += count;

								// 这样一来从posistion~limit这段缓存数据是有效,可利用的
								// buffer.flip();

								buffer.clear();

								// 往输出文件中去写了
								if (count < bufferSize) {

									byte[] overByte = new byte[count];

									for (int index = 0; index < count; index++) {
										overByte[index] = buffer.get(index);
									}

									fout.write(overByte);
								} else {
									fout.write(buffer.array());
								}

							} else {

								// 关闭客户端通道
								client.close();

								// 退出大循环
								break FOR;
							}
						}
					}
				}

				// 计算时间
				double last = (System.currentTimeMillis() - start) * 1.0 / 1000;
				System.out.println("Thread " + index + " downloaded " + total
						/ 1024 + "kbytes in " + last + "s.");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException {

		long startTime = System.currentTimeMillis();

		// 启用线程池
		ExecutorService exec = Executors.newFixedThreadPool(SIZE);
		for (int index = 1; index <= SIZE; index++) {
			exec.execute(new Download(index));
		}
		exec.shutdown();

		long endTime = System.currentTimeMillis();

		long timeLong = endTime - startTime;

		System.out.println("下载时间:" + timeLong);

	}
}

 效果和上一个程序的效果差不多,只是时间上和内存资源占有率上有所提高,当然本机仅仅启动了几个线程,如果客户端启动更多线程,NIO的方式节约资源的效果是明显的,宕机概率小于阻塞IO方式很多。

5.  总结

其实NIO想写得更多,但是看到网络上已经有很多资料了,就不再展开了,非一篇所能尽述的了的。当然了,NIO也是有场景的,比如适合与长连接的请求,以为NIO维护管道、缓存块、时间选择器等等也需要资源消耗的,而且比传统IO的对象们要重量级。所以原始IO也并不是一无是处,现在还是有很多socket中间件还是已然使用第二种方式。

代码在附件中~~~

  • 大小: 10.8 KB
  • 大小: 11.1 KB
  • 大小: 10.8 KB
89
5
分享到:
评论
43 楼 geniusxi 2015-05-18  
看完我就去厕所思考了~~写的太好了~~  
42 楼 spring-china 2011-10-10  
novoland 写道
hi NIO 并非AIO,它是同步非阻塞IO,所以文章直接将NIO称呼为异步IO有点不妥


恩,对,nio是属于同步IO的. 而非异步IO,

这一句:
异步 I/O 调用不会阻塞。相反,您将注册对特定 I/O 事件的兴趣 ― 可读的数据的到达、新的套接字连接,等等,而在发生这样的事件时,系统将会告诉您。

其实也是符合同步IO模型Reactor.
而异步IO模型,如Proactor,特征是支持异步的操作系统替事件处理器完成IO操作,事件处理器下达完IO命令后就不用管事了.

楼主的重口味比喻很生动形象,赞一个,呵呵
41 楼 zrs217 2011-10-09  
楼主说话很幽默,学习了!
40 楼 锅巴49 2011-10-09  
好文,受教了.
39 楼 endiya 2011-10-08  
讲的不错,通俗易懂;顶.
38 楼 angel243fly 2011-10-07  
很不错的文章,让初学者也能看懂。
话说我正吃饭呢。。。
37 楼 mouzhaonu1985 2011-10-07  
这位大哥的例子真的让人很倒胃口啊,不过确实挺形象的。重口味
36 楼 houfeng0923 2011-10-06  
novoland 写道
hi NIO 并非AIO,它是同步非阻塞IO,所以文章直接将NIO称呼为异步IO有点不妥

说的是呀。异步的AIO在jdk7中才刚发布。
35 楼 object_object 2011-10-06  
NIOServer 服务器类 的main 启动方法里面 加了个while (true)循环监听,调用
server.listen();就已经在循环监听了,这里是不是 重复监听了啊????
34 楼 novoland 2011-09-14  
hi NIO 并非AIO,它是同步非阻塞IO,所以文章直接将NIO称呼为异步IO有点不妥
33 楼 jingyunxia198111 2011-09-04  
      小伙子很不错啊
32 楼 firstlove8856 2011-08-21  
这个篇文章有点意思,值得回味。呵呵
31 楼 suhuanzheng7784877 2011-08-15  
tan4836128 写道
很实惠的文章,性价比高高的阅读价值,没理由不顶一顶


至于举例,通俗易懂很好,读不下去的朋友只能哪儿凉快哪儿呆着去了

兄弟你言重了,没到那种高度。而且有些人适合比较严肃的风格,有些人适合比较风趣的风格。个人品味不太一样吧。
30 楼 tan4836128 2011-08-12  
很实惠的文章,性价比高高的阅读价值,没理由不顶一顶


至于举例,通俗易懂很好,读不下去的朋友只能哪儿凉快哪儿呆着去了
29 楼 OLIVER_kahn111 2011-08-10  
不是吧,博主这都能想到。自愧不如,哈哈
28 楼 daur 2011-08-09  
呀,楼主屎例给我留下深刻印象哇,大大的up~~
27 楼 llxhna 2011-08-02  
http://suhuanzheng7784877.iteye.com/blog/1122131
好东西啊
26 楼 suhuanzheng7784877 2011-07-27  
yuediaoyuan0809 写道
上厕所这种事,怎能公开呢。

恩,只是个比喻罢了。。。。。
25 楼 yuediaoyuan0809 2011-07-27  
上厕所这种事,怎能公开呢。
24 楼 suhuanzheng7784877 2011-07-19  
自我检讨:
在代码中NIOServer.java中的main函数有一个死循环,死循环的内容是server.listen();

这个死循环应该去掉。
直接写成server.listen();


多谢Dping 兄提出的问题,感激不尽……

相关推荐

    基于nio实现的多文件上传源码

    NIO提供了一种非阻塞I/O操作的方式,特别适用于处理大量的并发连接,例如在文件传输、网络通信等场景。本主题“基于nio实现的多文件上传源码”探讨的是如何利用Java NIO来实现高效的多文件上传功能,尤其对于小文件...

    NIO网络通讯编程

    NIO(Non-blocking I/O,非阻塞I/O)是Java平台中的一种I/O模型,与传统的BIO(Blocking I/O,阻塞I/O)相比,NIO在处理高并发、大数据传输时表现出更高的效率和更好的性能。NIO的核心概念包括通道(Channel)、缓冲...

    java nio 包读取超大数据文件

    - **异步处理**:如果系统允许,可以考虑使用NIO的异步特性来进一步提高性能。 - **多线程处理**:结合多线程技术,实现文件读取和数据处理的并行执行,提高整体效率。 综上所述,使用Java NIO处理超大数据文件时...

    NIO学习系列:连网和异步IO

    在IT行业中,网络编程是构建分布式系统和网络应用的基础,而Java NIO(Non-blocking Input/Output)则是Java提供的一种高效、低延迟的I/O模型。本篇文章将深入探讨NIO在连网和异步IO方面的应用,以及如何通过源码...

    java nio 异步编程源码

    在“AsynIOModule”这个压缩包中,可能包含了关于Java NIO和AIO编程的相关示例代码和文档,这些资源可以帮助开发者深入理解和实践这两种异步I/O机制,提升他们在Java网络编程中的技能。通过研究这些代码和文档,...

    JAVA NIO 异步通信客户端

    在描述中提到的"JAVA NIO 异步通信客户端"是指使用NIO API实现的一个客户端程序,它能够在不阻塞主线程的情况下进行网络通信。这通常通过使用Selector和Channel来完成。Selector负责监控多个通道的状态变化,而...

    JAVA NIO 简单PFT 文件服务

    总的来说,JAVA NIO PFT文件服务结合了非阻塞I/O、多路复用和高效的数据处理,为文件的上传、下载和列表展示提供了一个强大而灵活的解决方案。通过深入理解和熟练运用这些概念,开发者可以构建出高性能、高并发的...

    一个简单的异步网络通讯源代码(27kb)

    标题中的“一个简单的异步网络通讯源代码”指的是一个实现异步网络通信的程序示例。在计算机编程中,异步网络通信是处理网络连接的一种常见方式,它允许程序在等待数据传输的同时执行其他任务,提高了系统资源的利用...

    java NIO异步框架

    NIO与传统的IO(-blocking I/O)相比,最大的区别在于它支持异步非阻塞的I/O操作,这意味着在进行读写操作时,程序不会被阻塞,可以继续执行其他任务,从而提高了系统的整体效率。 在Java NIO中,主要有以下关键...

    JAVA NIO 异步通信模板服务端

    例如,使用NIO进行大文件上传或下载,或者在后台服务中处理多个网络连接。 ### 注意事项 - **线程安全**:NIO操作通常是线程不安全的,所以在多线程环境下,需要对共享的Buffer和Channel进行适当的同步控制。 - **...

    用nio实现异步连接池

    ### 使用Java NIO实现异步连接池的关键知识点 #### 异步连接池的诞生背景与重要性 在现代Web应用程序开发中,为了提升系统性能和响应速度,常连接与连接池技术成为不可或缺的一部分。常连接是指一个持久存在的TCP...

    NIO处理大文件

    这段代码展示了如何使用NIO读取一个大文件并写入另一个文件,通过循环读取和清空缓冲区,实现了高效的文件复制。 8. 性能对比: 相较于传统的IO,NIO在处理大文件时通常具有更高的吞吐量和更低的CPU占用。尤其是...

    MINA NIO 高性能异步并发网络通讯框架

    使得 2.0.x 成为十分令人期待的一个版本 我们在惊叹 MINA 可以带来多么大便利的同时,还不得不为其卓越的性能而骄傲,据称使用MINA开发服务器程序的性能已经逼近使用 C/C++ 语言开发的网络服务。 不过不管怎样...

    java.nio(socket异步通讯完整版)

    重新写的Java.nio的socket异步通讯,包含客户端及服务端完整版, 注:解析信息CLASS及进制转换CLASS需要自己去写,项目直接导入,需要自己写一些解析及转换类,这样你才能读懂SOCKET的异步,否则光拿代码没用 ...

    文件异步下载

    异步下载允许应用程序在不阻塞主线程的情况下进行文件下载,这样用户可以继续其他操作而不必等待文件下载完成。这篇博客文章可能详细讲解了如何实现这一功能。 在Java或JavaScript等编程语言中,文件异步下载通常...

    java nio 写文件

    使用Java NIO进行文件写入的主要优势在于其非阻塞特性。NIO能够以更有效的方式管理系统资源,特别是在高并发环境中。此外,通过缓冲区和选择器,可以提高I/O操作的吞吐量。 总结,Java NIO提供了一套高效、灵活的...

    JAVA nio异步长连接服务端与客户端

    在标题中提到的"JAVA nio异步长连接服务端与客户端",我们可以理解为使用Java NIO实现的TCP长连接通信。TCP长连接是指在客户端和服务端之间保持一个持久的连接,可以多次收发数据,而不必每次通信都建立新的连接。这...

    nio异步长连接服务端与客户端

    Java NIO(非阻塞I/O)是一种在Java中实现高效I/O操作的方式,相比于传统的BIO(阻塞I/O),NIO提供了更强大的数据传输能力,尤其适用于高并发、低延迟的网络应用,如服务器长连接场景。在这个主题中,我们将深入...

    Java.NIO资源下载资源下载

    根据提供的文件信息,我们可以提取并总结出关于Java NIO(New Input/Output)的重要知识点。 ### Java NIO 概述 Java NIO 是 Java 平台的一个重要特性,首次出现在 Java 1.4 版本中。它为 Java 开发者提供了一套...

Global site tag (gtag.js) - Google Analytics