`
sunnylocus
  • 浏览: 875708 次
  • 性别: 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,系统开销小,能够提高程序的并发性能。

 

4
0
分享到:
评论
2 楼 to_zoe_yang 2011-01-14  
不错~
写的很好
1 楼 gsb 2009-12-31  

相关推荐

    阻塞通信和非阻塞通信的区别

    阻塞通信和非阻塞通信的区别 阻塞通信和非阻塞通信是两种不同的通信模式,主要应用于Socket通信中。在Java中,通过使用java.nio包中的类可以实现非阻塞通信。 阻塞通信是指在发送或接收数据时,当前线程将被阻塞,...

    阻塞通信和非阻塞通信

    ### 阻塞通信与非阻塞通信:深入解析与应用 #### 1. 概述 在并行计算领域,尤其是使用MPI(Message Passing Interface)进行编程时,通信模式的选择对于程序性能至关重要。阻塞通信与非阻塞通信是两种基本的通信方式...

    java非阻塞通信研究

    Java非阻塞通信是现代Java开发中的一个重要概念,特别是在高并发和高性能的系统设计中。非阻塞通信允许程序在等待数据就绪时不会被挂起,而是继续执行其他任务,提高了系统的整体效率。这种技术主要应用于网络I/O...

    用Java实现非阻塞通信

    用Java实现非阻塞通信 java.nio包提供了支持非阻塞通信的类,主要包括: ● ServerSocketChannel:ServerSocket的替代类,支持阻塞通信与非阻塞通信。 ● SocketChannel:Socket的替代类,支持阻塞通信与非阻塞通信...

    基于JavaNIO的非阻塞通信的研究与实现

    ### 基于Java NIO的非阻塞通信的研究与实现 #### 摘要 本文探讨了Java NIO(New I/O)框架中的非阻塞通信机制,并对其原理及应用进行了深入研究。NIO是一种现代I/O处理方法,通过引入缓冲区、通道和选择器等新概念...

    JAVA非阻塞通信技术原理研究与实现.pdf

    "JAVA非阻塞通信技术原理研究与实现" JAVA非阻塞通信技术原理研究与实现是指在JAVA平台上,服务端与客户端之间的通信过程中,不使用阻塞方式进行通信,而是使用非阻塞方式,来提高服务端的效率和灵活性。 非阻塞...

    基于WSAAsyncSelect非阻塞通信程序设计

    非阻塞通信模式下,当调用 socket 的读写操作时,如果数据未准备好,系统不会挂起线程,而是立即返回一个错误,允许线程继续执行其他任务。这种方式提高了并发性能,因为多个任务可以在单个线程中并行处理。 ### 3....

    阻塞及非阻塞通信

    阻塞与非阻塞通信是计算机网络编程中的两种基本通信方式,主要涉及到Java NIO(Non-blocking Input/Output,非阻塞输入/输出)框架。Java NIO 提供了一种新的方式来处理I/O操作,使得程序在进行读写操作时,不再必须...

    非阻塞通信例子【nonblocking】示例

    非阻塞通信是一种高效、灵活的网络编程方式,它与传统的阻塞通信模式不同,能够显著提高系统的并发处理能力。在Java中,非阻塞通信主要通过NIO(Non-blocking Input/Output,非阻塞输入/输出)实现,这是一种基于...

    java网络编程socket非阻塞通信

    通过java网络编程深入理解socket阻塞通信和非阻塞通信的在网络中的应用 源码包每一行都有注释,在代码里面每一个类都有详细的注释来解释这个类的功能这个方法的功能,调用哪一个类的哪一个功能等等。 压缩包包含实验...

    利用Python中SocketServer 实现客户端与服务器间非阻塞通信

    利用SocketServer模块来实现网络客户端与服务器并发连接非阻塞通信。 首先,先了解下SocketServer模块中可供使用的类: BaseServer:包含服务器的核心功能与混合(mix-in)类挂钩;这个类只用于派生,所以不会生成这...

    JavaSocket学习---NIO实现非阻塞的通信

    接下来是`EchoClient.java`,客户端通常采用单线程阻塞I/O模型,但在NIO模式下,也可以实现非阻塞通信。客户端创建一个`SocketChannel`连接到服务器,然后同样注册到`Selector`,设置感兴趣的读事件。客户端发送数据...

    ePump是一个基于IO事件通知、非阻塞通信、多路复用、多线.zip

    ePump是一个基于IO事件通知、非阻塞通信、多路复用、多线

    1、C++SOCKET同步阻塞、异步非阻塞通信服务端、客户端代码,支持多个客户端连接 2、断线重连(服务端或客户端没有启动顺

    1、C++SOCKET同步阻塞、异步非阻塞通信服务端、客户端代码,支持多个客户端连接。 2、断线重连(服务端或客户端没有启动顺序要求,先开启的等待另一端连接); 3、服务端支持同时连接多个客户端; 4、阅读代码就...

    send_isend_C语言_MPI阻塞非阻塞_MPI通信模式_非阻塞_nan_

    然而,非阻塞通信引入了复杂性,需要管理异步通信的状态,并确保正确完成(通过调用`MPI_Wait`或`MPI_Test`)。 3. **非阻塞通信的完成和同步** 在使用`MPI_Isend`后,发送进程需要确认消息是否已经被接收。这通常...

    非阻塞java通信代码

    在Java中实现非阻塞通信,主要依赖于Java NIO(Non-blocking Input/Output)库。 1. Java NIO简介: Java NIO是Java 1.4引入的新I/O API,是对传统BIO(Blocking I/O)模型的补充。NIO的核心概念包括通道(Channel...

    java非阻塞式的通信

    - **非阻塞模式**:通过`java.nio`提供的非阻塞通信机制,服务器可以使用少量的线程处理大量的客户端连接,极大地提高了系统吞吐量和响应速度。使用`Selector`监听多个`Channel`的事件,避免了线程阻塞,减少了线程...

    一种非阻塞式串行口通讯源代码

    本资源提供了一种经过实际验证的非阻塞式串行口通讯源代码,旨在解决传统串行通信中可能遇到的阻塞问题和数据丢失问题,提高通信效率和可靠性。 串口通信是计算机与外部设备之间通过串行接口进行的数据交换。传统的...

Global site tag (gtag.js) - Google Analytics