NIO技术-4-Socket通道

一、Socket通道介绍

Socket通道即套接字通道,是一种基于TCP连接协议传输数据的通道。Socket通道可以以分阻塞模式运行,并且可以被Reactor设计模式使用,因为Socket通道是可供选择的,其继承SelectableChannel类。

非阻塞I/O是复杂的、高性能的程序构建的基础。

使用NIO的Socket通道就没有必须像旧的Socket一样为每个Socket创建一个线程,避免了N个线程之间切换上下文的性能开销,使用NIO的Socket通道可以一个或几个线程就可以管理成百上千个已连接的Socket,并且性能消耗非常小。

1. ServerSocketChannel:负责监听某个端口,等待客户端Socket连接进来

2. SocketChannel:可以理解为一对一对使用,客户端使用SocketChannel连接服务器,服务器也会有个对等的SocketChannel

3. DatagramChannel:基于UDP发送数据报

所有的Socket通道类被创建后都有一个关联的Socket;但是使用旧的方法直接创建Socket并不会关联一个通道,虽然它们有getChannel()方法,但是这个方法会返回null。

二、ServerSocketChannel

ServerSocketChannel有如下方法可供使用:

1. open():打开一个ServerSocketChannel,用于创建ServerSocketChannel

2. validOps():返回SelectionKey.OP_ACCEPT,一般用不到,可用于验证注册到选择器时指定的兴趣是否位OP_ACCEPT

3. socket():返回与通道关联的ServerSocket

4. accept():等待接受一个Socket连接,非阻塞模式下可能返回null

//打开一个ServerSocketChannel  
ServerSocketChannel ssc = ServerSocketChannel.open();  
//将通道关联的ServerSocket绑定65535端口  
//jdk1.7之后可以ssc.bind(new InetSocketAddress(65535))  
ssc.socket().bind(new InetSocketAddress(65535));  
//设置非阻塞模式  
ssc.configureBlocking(false);  

三、SocketChannel

SocketChannel有如下方法可供使用:

open():打开一个SocketChannel
open(SocketAddress remote):同open(),并指定连接服务器地址
validOps():返回SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT,作用同ServerSocketChannel的validOps()
socket():返回通道关联的Socket
isConnected():检查是否已连接,返回true or false
isConnectionPending():检查是否处于请求连接状态
connect(SocketAddress remote):连接指定地址服务器
finishConnect():强制完成连接,完成连接则返回true,返回false
read(ByteBuffer dst):将通道的数据读到ByteBuffer
read(ByteBuffer[] dsts):将通道数据分散读到ByteBuffer数组
read(ByteBuffer[] dsts, int offset, int length):将通道数据分散读到ByteBuffer数组,并指定读取数据的开始位置和读取的长度
write(ByteBuffer src):将字节缓冲区的数据写入通道
write(ByteBuffer[] srcs):将分散的字节缓冲区的数据聚集写入通道
write(ByteBuffer[] srcs, int offset, int length):同上一个方法,并指定写入数据的开始位置和长度
SocketChannel sc = SocketChannel.open();  
sc.configureBlocking(false);  
sc.connect(new InetSocketAddress("127.0.0.1", 65535));  
//轮询直到成功连接为止  
while(!sc.isConnected()){  
      
}  

四、DatagramChannel

open():打开一个DatagramChannel
validOps():返回SelectionKey.OP_READ | SelectionKey.OP_WRITE
socket():返回关联的DatagramSocket
isConnected():是否已连接
connect(SocketAddress remote)》:连接某个地址
disconnect():断开连接
receive(ByteBuffer dst):接收数据,从通道读入缓冲区
send(ByteBuffer src, SocketAddress target):将缓冲区的数据发送到指定网络地址
read(ByteBuffer dst)
read(ByteBuffer[] dsts)
read(ByteBuffer[] dsts, int offset, int length)
write(ByteBuffer src)
write(ByteBuffer[] srcs)
write(ByteBuffer[] srcs, int offset, int length)

这么需要说明下,DatagramChannel提供了connect方法,别被这样的方法套住了,DatagramSocket是基于UDP的,UDP是无连接的,connection方法只是绑定某个地址,只能从这个地址接收数据或发送数据到这个地址。

DEMO:

Java简单版飞鸽传书:http://download.csdn.net/detail/abc_key/7497543

Java简单聊天室:http://download.csdn.net/detail/abc_key/7459723

Java NIO聊天室 中:若客户端强制关闭,服务器会报“java.io.IOException: 远程主机强迫关闭了一个现有的连接。并且服务器会在报错后停止运行,错误的意思就是客户端关闭了,但是服务器还在从这个套接字通道读取数据,便抛出IOException,导致这种情况出现的原因就是,客户端异常关闭后,服务器的选择器会获取到与客户端套接字对应的套接字通道SelectionKey,并且这个key的兴趣是OP_READ,执行从这个通道读取数据时,客户端已套接字已关闭,所以会出现“java.io.IOException: 远程主机强迫关闭了一个现有的连接”的错误。解决这种问题也很简单,就是服务器在读取数据时,若发生异常,则取消当前key并关闭通道,如下代码:

//获取此key对应的套接字通道  
SocketChannel channel = (SocketChannel) key.channel();  
//创建一个大小为1024k的缓存区  
ByteBuffer buffer = ByteBuffer.allocate(1024);  
StringBuffer sb = new StringBuffer();  
//将通道的数据读到缓存区  
int count = 0;  
try{  
    count = channel.read(buffer);  
}catch(IOException e){  
    key.cancel();  
    channel.socket().close();  
    channel.close();  
    return;  
}  

因在catch中取消了key,readMsg返回后,run方法继续往下走,之前的代码会报“java.nio.channels.CancelledKeyException”错误,所以需要判断当前key是否有效,

之前的代码:

//若此key的通道的行为是"读"  
if (key.isReadable()) {  
    readMsg(key);  
}  
//若次key的通道的行为是"写"  
if (key.isWritable()) {  
    writeMsg(key);  
}  

修复后的代码:

//若此key的通道的行为是"读"  
if (key.isValid() && key.isReadable()) {  
    readMsg(key);  
}  
//若次key的通道的行为是"写"  
if (key.isValid() && key.isWritable()) {  
    writeMsg(key);  
}  

这样改良之后的聊天室服务端,客户端异常强制关闭后,服务器便会妥善处理了,也不会报错,更不会停止运行。改良之后还算比较稳定运行的。

 

 

转载于:https://my.oschina.net/u/1024107/blog/746046

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值