`
kree
  • 浏览: 128832 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

非阻塞通信,《Java网络编程精解》指误。

阅读更多

     对于用ServerSocket和Socket写的服务器程序或着客户端程序,在运行的时候常常会阻塞,如当一个线程执行ServerSocket的accept()方法,如果没有客户机连接,该线程就会一直阻塞直到有了客户机连接才从accept()方法返回,再如,当线程执行Socket的read()方法,如果输入流中没有数据,该线程就会一直等到有数据可读时才从read()方法返回。

    如果服务器要与多个客户机通信,通常做法为每个客户机连接开启一个服务线程,每个工作线程都有可能经常处于长时间的阻塞状态。

    从JDK1.4版本开始,引入了非阻塞的通信机制。服务器程序接收客户连接、客户程序建立与服务器的连接,以及服务器程序和客户程序收发数据的操作都可以按非阻塞的方式进行。服务器程序只需要创建一个线程,就能完成同时与多个客户通信的任务。

    非阻塞通信要比传统的阻塞方式效率要高,Apache的MIMA框架就是以java.nio包中的类编写的。

    不知道是否有朋友看过 孙卫琴写的《Java网络编程精解》,在提到线程阻塞的时

Java网络编程精解 第84页 写道
线程从Socket的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读到了足够的数据,或者到达输入流的未尾,或者出现了异常,才从输入流的read()方法返回或异常中断。输入流中有多少数据才算足够呢,这要看线程执行read方法的类型。 
    int read():只要输入有一个字节,就算足够。 
    int read(byte[] buff):只要输入流中的字节数目与参数buff数组的长度相同,就算足够。

 我对描红的描述持不同的意见

  byte[] msgBytes = new byte[512];
  inputStream.read(msgBytes); 

如果按书中描述,这行代码必须读到512个字节后才从阻塞状态中返回,如果没有读到足够的512个字节,则一直阻塞。但实际情况却不是这样的,只要流里哪怕只有一个字节 ,inputStream.read(msgBytes)也会立即返回,返回值为读到的字节数。

下面是简单的NIO示例

1、服务端程序,非阻塞方式

package com.bill99.nioserver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.Iterator;

public class NIOServer {
	private Selector socketSelector = null;
	private ServerSocketChannel ssChannel = null;
	private SocketChannel socketChannel =null;
	private static SelectionKey key = null;
	private int port =5512;
	private int backlog = 100;
	private Charset charset = Charset.defaultCharset();
	private ByteBuffer shareBuffer = ByteBuffer.allocate(1024);//生成1kb的缓冲区,可以根据实际情况调的更大些
	
	public NIOServer()  {
		try {
			socketSelector = SelectorProvider.provider().openSelector();
			ssChannel =ServerSocketChannel.open() ;
			ssChannel.socket().bind(new InetSocketAddress(port),100);
			System.out.println(String.format("NIO服务器启动,监听端口%1$s,最大连接数%2$s", port,backlog));
		} catch(IOException e){
			throw new ExceptionInInitializerError(e);
		}
	}
	/**
	 * 接收客户端连接
	 */
	public void acceptConnect() {
		while(true) {
			try {
				SocketChannel socketChannel = ssChannel.accept();//阻塞模式,直到有连接进入
				System.out.println("收到客户机连接,来自:"+ssChannel.socket().getInetAddress());
				socketChannel.configureBlocking(false);//设置非阻塞模式
				synchronized(this){
					socketSelector.wakeup();
					socketChannel.register(socketSelector,SelectionKey.OP_READ|
														  SelectionKey.OP_WRITE);
				}
			} catch(IOException e){e.printStackTrace();}
		}
	}
	/**
	 * 读写服务
	 * @throws IOException
	 */
	public void service() throws IOException{
		while (true) {
			synchronized (this) {//空的同步块,目的是为了避免死锁
			}
			if (!(socketSelector.select() > 0)) {
				continue;
			}
			Iterator<SelectionKey> it = socketSelector.selectedKeys().iterator();
			while (it.hasNext()) {
				key = it.next();
				it.remove();
				if(key.isReadable()) {// 读就绪
					this.readDataFromSocket(key);
				}
				if(key.isWritable()){//写就绪
					this.sayWelcome(key);
				}
			}
		}
	}
	//读取客户机发来的数据
	private void readDataFromSocket(SelectionKey key) throws IOException {
		shareBuffer.clear();//清空buffer
		socketChannel=(SocketChannel) key.channel();
		int num=0;
		while((num = socketChannel.read(shareBuffer))>0){
			shareBuffer.flip();//将当前极限设置为位置,并把设置后的位置改为0
		}
		if(num ==-1){//读取流的未尾,对方已关闭流
			socketChannel.close();
			return;
		}
		System.out.println("client request:"+charset.decode(shareBuffer).toString());
	} 
	//向客户机发响应信息
	private void sayWelcome(SelectionKey key) throws IOException {
		shareBuffer.clear();//清空buffer
		socketChannel=(SocketChannel) key.channel();
		shareBuffer.put("Welcome to china!this is a greate and very beautifual country!\n".getBytes());
		shareBuffer.flip();//将当前极限设置为位置,并把设置后的位置改为0
		socketChannel.write(shareBuffer);
	}
	//Main方法
	public static void main(String[] args) {
		final NIOServer server = new NIOServer();
		Runnable task = new  Runnable() {
			public void run() {
				server.acceptConnect();
			}
		};
		new Thread(task).start();//启动处理客户机连接的线程
		try {
			server.service();
		} catch (IOException e) {//发生IO流异常时,关闭对应的socket
			try {
				key.channel().close();
				key.cancel();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
}

 2、客户端程序,阻塞方式

package com.bill99.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.CharBuffer;

import javax.net.SocketFactory;
//测试类
public class BlockingClient {
	private Socket socket = null;
	private OutputStream out = null;
	private InputStream in = null;
	
	public BlockingClient() {
		try {
			socket= SocketFactory.getDefault().createSocket("127.0.0.1", 5512);
			out = socket.getOutputStream();
			in = socket.getInputStream();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//发送请求并接收应答
	public String receiveRespMsg(String reqMsg) throws IOException{
		out.write(reqMsg.getBytes());
		out.flush();
		in = socket.getInputStream();
		int c =0;
		CharBuffer buffer = CharBuffer.allocate(1024);
		while((c=in.read())!=-1 && c!=10){
			buffer.put((char)c);
		}
		return new String(buffer.array()).trim();
	}
	
	public static void main(String[] args) throws Exception{
		BlockingClient client = new BlockingClient();
		System.out.println("服务器响应:"+client.receiveRespMsg("hello\n"));
	}
}

    总体而信,阻塞模式和非阻塞模式都可以同时处理多个客户机的连接,但阻塞模式需要较多的线程许多时间都浪费在阻塞I/O操作上,Java虚拟机需要频繁地转让CPU的使用权,而非阻塞模式只需要少量线程即可完成所有任务,非阻塞模式能更有效的利用CPU,系统开销小,能够提高程序的并发性能。

 

分享到:
评论
5 楼 szhnet 2014-01-18  
kree 写道
szhnet 写道
其实我在1楼说的不对,那个不能用到达输入流的未尾来理解。

那应该如何理解?

书中说的确实不对
4 楼 kree 2013-12-31  
szhnet 写道
其实我在1楼说的不对,那个不能用到达输入流的未尾来理解。

那应该如何理解?
3 楼 szhnet 2011-08-21  
其实我在1楼说的不对,那个不能用到达输入流的未尾来理解。
2 楼 leaow567 2011-07-25  
同意楼上的观点
1 楼 szhnet 2010-10-11  
    我觉得书上说的没有错。
引用
byte[] msgBytes = new byte[512];
inputStream.read(msgBytes);

    如果按书中描述,这行代码必须读到512个字节后才从阻塞状态中返回,如果没有读到足够的512个字节,则一直阻塞。但实际情况却不是这样的,只要流里哪怕只有一个字节 ,inputStream.read(msgBytes)也会立即返回,返回值为读到的字节数。

    你举的这个例子的情况应该是说流中只有1个字节,读完这1个字节后就到输入流的未尾了。这是符合书中所说的。
引用
线程从Socket的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读到了足够的数据,或者到达输入流的未尾,或者出现了异常,才从输入流的read()方法返回或异常中断。输入流中有多少数据才算足够呢,这要看线程执行read方法的类型。


byte[] msgBytes = new byte[512];
inputStream.read(msgBytes);
应该是这样的。通道的缓冲区中有>=512个字节,这时在读取到512个字节之前,read将被阻塞。因为缓冲区中的字节并不是立即可用的,所以如果不是nio的话,是会被阻塞的。

相关推荐

    Java网络编程精解(孙卫琴)电子教案

    - **NIO(Non-blocking I/O)**:Java的新一代I/O API,提供了非阻塞式的网络通信,提高了性能和效率。 - **SOA(Service-Oriented Architecture)**:面向服务的架构,Java通过JAX-WS和JAX-RS等技术实现了Web服务...

    java网络编程(非阻塞与阻塞编程)

    在深入探讨Java网络编程中的非阻塞与阻塞编程之前,我们先来了解这两个概念的基本含义。阻塞编程,通常指的是在程序执行过程中,当某一部分代码遇到I/O操作时,如读写文件或网络通信,整个程序会暂停运行,等待I/O...

    读书笔记:JAVA网络编程精解.zip

    读书笔记:JAVA网络编程精解

    Java网络编程精解PPT课件.ppt

    Java网络编程精解PPT课件.ppt 本资源摘要信息是关于Java网络编程的PPT课件,主要介绍了基于UDP的数据报和套接字的相关知识点。 UDP协议简介 UDP(User Datagram Protocol,用户数据报协议)是一种传输层协议,...

    Java编程案例精解源代码

    8. **网络编程**:Java提供了Socket和ServerSocket类,可以用于客户端-服务器模型的网络通信。 9. **数据库连接**:通过JDBC(Java Database Connectivity)接口,Java可以与各种类型的数据库进行交互,进行CRUD...

    读书笔记:孙卫琴《Java网络编程精解》源码.zip

    读书笔记:孙卫琴《Java网络编程精解》源码

    java编程案例精解

    总之,“Java编程案例精解”涵盖了Java编程的诸多关键点,包括但不限于基本语法、异常处理、文件操作、集合框架、多线程、网络编程以及标准库的使用。通过实例学习,学习者不仅可以巩固理论知识,还能提升解决实际...

    Java网络编程精解之ServerSocket用法详解

    Java网络编程的核心在于客户端与服务器端的交互,而ServerSocket是Java中用于服务器端的类,它使得服务器能够监听特定端口,接收客户端的连接请求。本文将详细讲解ServerSocket的使用方法及其在多线程环境下的应用。...

    Java编程案例精解素材.rar

    本资源“Java编程案例精解素材.rar”包含了一系列实用的Java编程示例,旨在帮助学习者深入理解和掌握Java的核心概念与技术。 1. **使用邮件客户端工具**:JavaMail API是Java中用于处理电子邮件的库,它允许开发者...

    Java编程案例精解

    在《Java编程案例精解》的光盘资料中,可能包含了大量的实战项目,比如简单的命令行应用、图形用户界面(GUI)程序、网络编程案例、多线程编程、数据库连接(JDBC)操作以及I/O流的使用。这些案例能帮助读者在实践中...

    JavaScript 编程精解 中文第三版

    JavaScript 编程精解 中文第三版 JavaScript 编程精解 中文第三版

    java网络精解源码

    5. **IO流与NIO**: Java标准IO流用于读写网络数据,而NIO(Non-blocking I/O)提供了一种非阻塞的I/O模型,能更高效地处理大量并发连接。NIO引入了选择器(Selector)和通道(Channel)的概念,允许单个线程处理多个...

    java网络编程

    这本书可能详细解析了Java网络编程的各个方面,包括URL类的使用、HTTP协议的实现、NIO(非阻塞I/O)以及高级网络编程技巧。通过阅读此书,读者可以深入了解Java在网络编程领域的应用。 最后,"SSR40F20070511.zip...

    java网络编程_孙卫琴_有书签

    通过学习《Java网络编程精解》,开发者不仅能掌握Java网络编程的基本原理,还能具备开发实际网络应用的能力,例如构建Web服务、设计网络通信系统等。书中的书签功能则为读者提供了方便的查阅路径,帮助快速定位到...

    Visual C++通信编程工程实例精解 光盘

    书中精选了大量来自工程实践的应用实例,涵盖了串口通信、Socket网络通信、远程数据库访问、应用于工业上的OPC通信、Modem通信以及SMS和GPRS移动通信编程。 书中的每个应用实例都是在简单介绍必备的背景知识后,重点...

    Java编程精解(孙卫琴)

    Java编程精解(孙卫琴) 经典Java书籍,你值得拥有

    《JavaScript编程精解》.pdf

    文件标题是“《JavaScript编程精解》.pdf”,描述中提到了“JavaScript 编程精解 中文第一版”,而标签同样为“JavaScript 编程精解”。部分内容重复提及了访问“稀酷客”网站的链接,这可能是出版商提供的额外资源...

    javascript编程精解第三版中文版

    javascript编程精解第三版中文版,来自github的翻译,epub电子书可在手机上看。

    Java数据库编程技术精解

    Java数据库编程技术精解,是Java开发者必备的技能之一,主要围绕JDBC(Java Database Connectivity)接口进行深入探讨。JDBC是Java平台中用于与各种数据库进行交互的标准API,无论你是初学者还是经验丰富的开发人员...

Global site tag (gtag.js) - Google Analytics