这篇文章只是说说简单的使用,内容不难,很简单很基础。
如果说错了希望纠正。
最近在做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();
}
}