java TCP/IP Socket编程-----NIO--初识-----笔记8

概述:

NIO主要包括两个部分:java.nio.channels包介绍了Selector和Channel抽象,java.nio包介绍了Buffer抽象。这都是一些高级的特性,有许多微妙的使用细节,它与socket类似,但是它主要区别,socket是乡村公路,NIO就是高速公路,socket容易阻塞,而NIO可以设置不阻塞。NIO更加充分利用系统的资源。

术语:

Selector:选择渠道

Channel: (一个与设备连接的实例,与socket不同通常它调用静态的方法进行实例化)Channel直接使用的不是流,而是缓存区,Channel可以配置非阻塞

Buffer:缓存IO流 buffer是具体基本类型Buffer ,通过allocate进行分配,也可以包装现有的数组

1.一个简单例子

package com.tcp.ip.chapter5;

import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class TCPEchoClientNonblocking {
	
	public static void main(String args[]) throws Exception {
		
		if((args.length < 2) || (args.length > 3)) {
			throw new IllegalArgumentException("Parameters : <Server> <Word> [<Port>]");
		}
		String server = args[0];
		
		byte[] argument = args[1].getBytes();
		
		int servPort = (args.length == 3) ? Integer.parseInt(args[2]) : 7;
		
		//socket 变成 SocketChannel ,它静态方法实例化
		SocketChannel clntChan = SocketChannel.open();
		//配置非阻塞快
		clntChan.configureBlocking(false);
		if(!clntChan.connect(new InetSocketAddress(server, servPort))) {
			//一直在连接,知道结束
			while (!clntChan.finishConnect()) {
				System.out.println(".");
			}
		}
		//包装流
		ByteBuffer writeBuf = ByteBuffer.wrap(argument);
		ByteBuffer readBuf = ByteBuffer.allocate(argument.length);
		int totalBytesRcvd = 0;
		int bytesRcvd;
		//一直读取数据
		while (totalBytesRcvd < argument.length) {
			//如果写缓存流有数据,继续写
			if(writeBuf.hasRemaining()) {
				clntChan.write(writeBuf);
			}
			//读取到-1表示最后的字符
			if ((bytesRcvd = clntChan.read(readBuf)) == -1) {
				throw new SocketException("Connection closed prematurely");
			}
			totalBytesRcvd += bytesRcvd;
			System.out.println(".");
		}
		//打印读取数据
		System.out.println("Received:" + new String (readBuf.array(), 0, totalBytesRcvd));
		clntChan.close();
	}

}

总结:对比学习,来找茬

  • 1.Socket 变成 SocketChannel ,而且不是new,是调用静态方法 open() ,(定位是高速公路)
  • 2.SocketChannel可以设置是否阻塞 调用方法 configureBlocking(false), Socket不可以
  • 3.对于传输的数据都是缓存,写缓存通过包装byte[] ,调用方法wrap(目标byte[]) (wrap本身就是包装,打包) ,而socket是通过getOutStream() 直接操作流
  • 4.对于读缓存采用allocate(容器大小)(allocate 分配的意思) 就像你那个盒子来装东西,而socket是通过getInputStream
  • 5.对于缓存区是否还有数据通过方法是 hasRemaining()
  • 6.将缓存变成byte[] 调用 缓存对象.array()

服务端协议

package com.tcp.ip.chapter5;

import java.io.IOException;
import java.nio.channels.SelectionKey;

public interface TCPProtocol {

	/**处理连接
	 * @param key
	 * @throws IOException
	 */
	void handleAccept(SelectionKey key) throws IOException;
	
	/**
	 * 处理读
	 * @param key
	 * @throws IOException
	 */
	void handleRead(SelectionKey key) throws IOException;
	
	
	/**
	 * 处理写
	 * @param key
	 * @throws IOException
	 */
	void handleWrite(SelectionKey key) throws IOException;
}

总结:服务主要的干的活分一下类,一个请求,一个读,一个写

package com.tcp.ip.chapter5;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class EchoSelectorProtocol implements TCPProtocol {

	//缓存大小
	private int bufSize;
	
	public EchoSelectorProtocol(int bufSize) {
		this.bufSize = bufSize;
	}
	public void handleAccept(SelectionKey key) throws IOException {
		SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept();
		//非阻塞注册
		clntChan.configureBlocking(false);
		//
		clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));
		

	}

	public void handleRead(SelectionKey key) throws IOException {

		SocketChannel clntChan = (SocketChannel) key.channel();
		ByteBuffer buf = (ByteBuffer) key.attachment();
		
		long bytesRead = clntChan.read(buf);
		if(bytesRead == -1) {
			clntChan.close();
		} else if (bytesRead >0) {
			key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
		}
	} 

	public void handleWrite(SelectionKey key) throws IOException {

		//取回数据
		ByteBuffer buf = (ByteBuffer)key.attachment();
		//准备缓存区写
		buf.flip();
		SocketChannel clntChan = (SocketChannel)key.channel();
		
		clntChan.write(buf);
		//完全写完啦?
		if(!buf.hasRemaining()) {
			key.interestOps(SelectionKey.OP_READ);
		}
		buf.compact();
	}

}
总结:具体实现 ,是不是类似医院操作

1.handleAccept() 处理请求,也就相当于医院挂号,将你的信息录入到系统的中,完成注册功能

2.handleRead() 读取数据 ,这时候你到对于科室去看医院,医生根据你描述的症状进行判断

3.handleWrite() 写数据, 这时候医生根据你的描述,确诊什么?开什么?注意什么?,写病历本上,你就下去抓药

4.key.interestOps对什么感兴趣,SelectionKey.OP_READ 读 SelectKey.OP_WRITE。

5.key.attachment() 获取缓存数据

package com.tcp.ip.chapter5;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

public class TCPServerSelector {

	private static final int BUFSIZE = 256;
	
	private static final int TIMEOUT = 3000;
	
	public static void main(String[] args) throws IOException{
		if(args.length < 1){
			throw  new IllegalArgumentException("Parameter(s): <Port>...");
		}
		
		Selector selector = Selector.open();
		
		for (String arg : args){
			ServerSocketChannel listnChannel = ServerSocketChannel.open();
			//好像可以绑定多个端口
			listnChannel.socket().bind(new InetSocketAddress(Integer.parseInt(arg)));
			//非阻塞注册
			listnChannel.configureBlocking(false);
			//返回的key是忽略的
			listnChannel.register(selector, SelectionKey.OP_ACCEPT);
			TCPProtocol protocol = new EchoSelectorProtocol(BUFSIZE);
			//一直运行
			while(true) {
				//等待一些渠道
				if(selector.select(TIMEOUT) == 0) {
					System.out.println(".");
					continue;
				}
				//得到迭代IO 就是所有请求的该端口度应该经过selector管理,类似存在map中k-v键值对
				Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
				while (keyIter.hasNext()) {
					SelectionKey key = keyIter.next();
					if(key.isAcceptable()) {
						protocol.handleAccept(key);
					}
					if(key.isReadable()){
						protocol.handleRead(key);
					}
					if(key.isValid() && key.isWritable()){
						protocol.handleWrite(key);
					}
					keyIter.remove();
				}
			}
			
		}
	}
}

总结:

1.一个channel绑定一个端口

2.一个channel注册到selector上,用selector进行管理

3.selector根据当前状态对通道进行相应的处理

自己理解不够,如果什么误导的地方,深感歉意,也欢迎指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值