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

来源:互联网 发布:webservice json 编辑:程序博客网 时间:2024/04/26 20:04

概述:

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根据当前状态对通道进行相应的处理

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

原创粉丝点击