zoukankan      html  css  js  c++  java
  • java NIO 学习笔记

        项目组是做IM产品的,服务端当然用的是NIO技术做通信底层。可是一直都是对NIO有些理论的了解,没有实践。近期有空了。就实践了下NIO。

        NIO,新IO,也称之为非堵塞IO。

    非堵塞是它跟传统IO的最重要的差别之中的一个。传统IO用Socket进行通信,NIO则用channel进行消息交互。channel必须注冊到selector上。把它感兴趣的事件告诉selector。这是个观察者模式的实现。能够这样描写叙述channel和selector的关系,channel是火车轨道,selector是火车调度室。

    多个channel能够注冊到同一个selector上。在一个线程了对这些channel进行调度。然而传统IO须要为每个socket创建一个线程。

    这也是NIO的优势之中的一个,不须要太多的线程。

        NIO版本号的服务端代码:

    package cn.nio;
    
    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.util.Iterator;
    
    public class NioServer {
    	private Selector	serverSelector;
    
    	public NioServer initServer(int port) throws IOException {
    		ServerSocketChannel serverChannel = ServerSocketChannel.open();
    		// 非堵塞模式
    		serverChannel.configureBlocking(false);
    		// 绑定端口号
    		serverChannel.socket().bind(new InetSocketAddress(port));
    		this.serverSelector = Selector.open();
    		serverChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
    		return this;
    	}
    
    	public void startListening() throws IOException {
    		System.out.println("服务端启动成功!");
    		while (true) {
    			// 当注冊的事件到达时。方法返回。否则,该方法会一直堵塞
    			serverSelector.select();
    			Iterator<SelectionKey> iterator = this.serverSelector.selectedKeys().iterator();
    			while (iterator.hasNext()) {
    				SelectionKey key = (SelectionKey) iterator.next();
    				// 必须删除。否则下次遍历时还会遍历旧的key
    				iterator.remove();	
    				if (key.isAcceptable()) {
    					accept(key);
    				} else if (key.isReadable()) {
    					read(key);
    				}
    			}
    		}
    	}
    	
    	// 接受client的连接请求
    	private void accept(SelectionKey key) throws IOException {
    		ServerSocketChannel server = (ServerSocketChannel) key.channel();
    		// 获得和client连接的通道
    		SocketChannel channel = server.accept();
    		// 设置成非堵塞
    		channel.configureBlocking(false);
    		channel.write(ByteBuffer.wrap(new String("client连接成功
    ").getBytes()));
    		// 注冊
    		channel.register(this.serverSelector, SelectionKey.OP_READ);
    	}
    
    	private void read(SelectionKey key) {
    		try {
    			SocketChannel channel = (SocketChannel) key.channel();
    			// 创建读取的缓冲区
    			ByteBuffer buffer = ByteBuffer.allocate(1024);
    			channel.read(buffer);
    			String msg = new String(buffer.array()).trim();
    			System.out.println("服务端:" + msg);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static void main(String[] args) throws IOException {
    		new NioServer().initServer(8080).startListening();
    	}
    }
        NIO版本号的client代码:

    package cn.nio;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.ClosedChannelException;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    
    public class NioClient {
    
    	private Selector	selector;
    
    	public NioClient initClient(String ip, int port) throws IOException {
    		SocketChannel channel = SocketChannel.open();
    		// 非堵塞
    		channel.configureBlocking(false);
    		this.selector = Selector.open();
    
    		// connect方法的凝视:此方法返回 false。而且必须在以后通过调用 finishConnect 方法来完毕该连接操作。
    		boolean result = channel.connect(new InetSocketAddress(ip, port));
    		System.out.println(result); // 返回false
    		// 注冊
    		channel.register(selector, SelectionKey.OP_CONNECT);
    		return this;
    	}
    
    	public void startListening() throws IOException {
    		while (true) {
    			selector.select();
    			Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
    			while (iterator.hasNext()) {
    				SelectionKey key = (SelectionKey) iterator.next();
    				// 必须删除,否则下次遍历时还会遍历旧的key
    				iterator.remove();
    				if (key.isConnectable()) {
    					connect(key);
    				} else if (key.isReadable()) {
    					read(key);
    				}
    			}
    		}
    	}
    
    	private void connect(SelectionKey key) throws IOException, ClosedChannelException {
    		SocketChannel channel = (SocketChannel) key.channel();
    		// 假设正在连接,则完毕连接
    		if (channel.isConnectionPending()) {
    			channel.finishConnect();
    		}
    		channel.configureBlocking(false);
    		channel.write(ByteBuffer.wrap(new String("client连接成功").getBytes()));
    		// 注冊
    		channel.register(this.selector, SelectionKey.OP_READ);
    	}
    
    	private void read(SelectionKey key) throws IOException {
    		// server可读取消息:得到事件发生的Socket通道
    		SocketChannel channel = (SocketChannel) key.channel();
    		// 创建读取的缓冲区
    		ByteBuffer buffer = ByteBuffer.allocate(1024);
    		channel.read(buffer);
    		String msg = new String(buffer.array()).trim();
    		System.out.println("client收到信息:" + msg);
    	}
    
    	/**
    	 * 启动client測试
    	 * 
    	 * @throws IOException
    	 */
    	public static void main(String[] args) throws IOException {
    		new NioClient().initClient("localhost", 8080).startListening();
    	}
    
    }


        考虑到其它语言不一定有NIO技术。我们的client都是C++的。

    那么一个NIO服务端能否够和一个非NIO的client进行通信呢?做了一个传统IO的client。做測试。可行!

    package cn.nio;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    
    public class NormalClient {
    
    	Socket	clientSocket	= null;
    
    	public NormalClient initClient(String ip, int port) {
    		try {
    			clientSocket = new Socket(ip, port);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return this;
    	}
    
    	public void read() throws IOException {
    		PrintWriter pw = new PrintWriter((clientSocket.getOutputStream()));
    		BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
    		String line = null;
    		while (true) {
    			pw.println("客户端发来的消息");
    			pw.flush();
    			line = br.readLine();
    			System.out.println(line);
    		}
    	}
    
    	public static void main(String[] args) throws IOException {
    		new NormalClient().initClient("127.0.0.1", 8000).read();
    	}
    }
    
        

        写这个client的时候。client一直无法收到服务端的消息。请教资深同事,问题在readline()方法上。假设调用的是readline(),那么服务端给client发消息时,须要传换行符。




  • 相关阅读:
    SPI总线介绍
    linuxok6410的I2C驱动分析---用户态驱动
    Centos下安装Mongodb
    python学习笔记(二):python数据类型
    python学习笔记(三):文件操作和集合
    python实现显示安装进度条
    python判断一个字符串是否是小数
    python学习笔记(四):函数
    python学习笔记(五):装饰器、生成器、内置函数、json
    python学习笔记(七):面向对象编程、类
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5068036.html
Copyright © 2011-2022 走看看