这篇文章只是说说简单的使用,内容不难,很简单很基础。
如果说错了希望纠正。
最近在做NIO的服务器端和客服端开发(虽然有mina 但是自己从头写感觉会好一点 当做练习的话)
SocketChannel有两种模式:blocking模式(和原来的Socket类似)和non-blocking模式(不会堵塞)
在两种模式下的读写操作是不一样的。 写比较好理解,非堵塞下可能会一个字节都不写(缓存区满了 堵塞的这种情况就是堵塞在那了)
在非堵塞模式下只要用while判断一下ByteBuffer的remaining就可以了:
public void write(ByteBuffer byteBuffer) throws IOException { byteBuffer.flip(); while(byteBuffer.hasRemaining()){ sc.write(byteBuffer); } }
接下来说说read read的返回参数有三个
正整数 0和-1
正整数在堵塞和非堵塞模式中意义是一样的,就是读入了多少个字节。
0的话涵义不同:
- 在堵塞中,如果返回0就说明服务器发送了请求但请求内容为空.
- 在非堵塞模式中,如果在read的时候流中暂时还没内容那么返回的也是0(堵塞中没有内容就堵塞在那了).
-1都表示连接已经断开(单向的连接 比如用shutdonwInput把Input流断开了 read就返回-1 但直接close的话 会返回一个java.nio.channels.ClosedChannelException的异常
有几个实验来证明以上的 实验都是连接www.baidu.com 百度现在用的最多的就是编程中测试了....
// 阻塞模式下 没有发送请求就read 会一直堵在read方法里 @Test public void testSocketChannel(){ InetSocketAddress address=new InetSocketAddress("www.baidu.com",80); try { SocketChannel sc=SocketChannel.open(address); ByteBuffer bb=ByteBuffer.allocate(1024); //sc.configureBlocking(false); System.out.println(sc.read(bb)); } catch (IOException e) { e.printStackTrace(); } }
然后把注释去掉设置成非阻塞模式,就可见读回了0.
然后是-1的返回情况:
//阻塞和非阻塞一样 这都会返回-1 因为通道已经断了(end-of-stream的意思应该理解成断开比较好吧) @Test public void testSocketChannel(){ InetSocketAddress address=new InetSocketAddress("www.baidu.com",80); try { SocketChannel sc=SocketChannel.open(address); ByteBuffer bb=ByteBuffer.allocate(1024); sc.configureBlocking(false); sc.shutdownInput(); System.out.println(sc.read(bb)); } catch (IOException e) { e.printStackTrace(); } }
好接下去的实验就让他接受百度返回的回应:
为了看得清楚就写成死循环不断读了,堵塞的情况:
@Test public void testSocketChannel2(){ InetSocketAddress address=new InetSocketAddress("www.baidu.com",80); try { SocketChannel sc=SocketChannel.open(address); Charset charset=Charset.forName("UTF-8"); ByteBuffer bb=ByteBuffer.allocate(1024); String send="GET / HTTP/1.1\r\n\r\n"; //sc.configureBlocking(false); sc.write(charset.encode(send)); while(true){ bb.clear(); System.out.println(sc.read(bb)); bb.flip(); System.out.println(charset.decode(bb).toString()); } } catch (IOException e) { e.printStackTrace(); } }
输出完后就一直卡在read里了 返回内容太长我就不打出来了
然后把注释去掉变为非阻塞的话,接下去除了输出完内容外就是不断地打0了
这里也说明不管用堵塞模式和非堵塞模式 用 read()!=-1来判断一次请求已经读完是多么不靠谱 堵塞模式会堵塞还好 非堵塞模式用0来判断还是不错的 这边实验性地写一下有关非堵塞模式完整读完一次请求的(当然只是试验性写一下 正常使用结合Selector写不用担心我这边试验代码的一些问题):
@Test public void testSocketChannel2(){ InetSocketAddress address=new InetSocketAddress("www.baidu.com",80); try { SocketChannel sc=SocketChannel.open(address); Charset charset=Charset.forName("UTF-8"); ByteBuffer bb=ByteBuffer.allocate(1024); String send="GET / HTTP/1.1\r\n\r\n"; sc.configureBlocking(false); sc.write(charset.encode(send)); try { Thread.sleep(5000); //给予足够的时间让服务器返回信息 不然下面直接返回0 } catch (InterruptedException e) { e.printStackTrace(); } //0 -->如果没有信息读入 或者已经把信息读完了 //-1-->输入的channel已经关闭了 while(sc.read(bb)>0){ if(bb.remaining()<bb.capacity()*0.02){ ByteBuffer nbb=ByteBuffer.allocate(bb.capacity()*2); bb.flip(); nbb.put(bb); bb=nbb; System.out.println(bb.capacity()); } } bb.flip(); System.out.println(charset.decode(bb).toString()); } catch (IOException e) { e.printStackTrace(); } }