1、介绍
Channel
是nio包的一个抽象类,作为java nio的三大组件(Buffer、Channel,Selector)之一,在java nio网络编程中尤为重要。
Channel
是读写数据的双向通道,可以从Channel将数据读取Buffer,也可将Buffer的数据写入Channel。我们这里主要讨论用于网络编程的ServerSocketChannel
,SocketChannel
,DatagramChannel
。
本文档需要使用Buffer操作,Buffer的使用可参考:http://t.csdn.cn/f1ZB9
。
2、代码演示(阻塞)
服务端
public class Server {
public static void main(String[] args) throws IOException {
//创建服务端Channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//服务端channel监听8080端口
serverSocketChannel.bind(new InetSocketAddress(8080));
while (true){
//阻塞等待tcp客户端连接
SocketChannel channel = serverSocketChannel.accept();
InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress();
System.out.println("客户端信息:"+remoteAddress.getAddress().getHostAddress()+" "+remoteAddress.getPort());
//处理客户端channel消息读写操作
handleMsg(channel);
}
}
/**
* 处理客户端channel消息读写操作
*/
public static void handleMsg(SocketChannel channel){
new Thread(()->{
//创建大小为100的字节缓冲区,用于配合Channel读写操作
ByteBuffer buffer = ByteBuffer.allocate(100);
try {
while (true){
//阻塞等待接收已连接的tcp客户端数据
int readCount = channel.read(buffer);
if(readCount<=0){
channel.close();
break;
}
//limit = position;position = 0;
buffer.flip();
//mark = position;(记录position位置)
buffer.mark();
System.out.println("接收到字节数据:");
while (buffer.position()<buffer.limit()){
System.out.print(buffer.get()+" ");
}
System.out.println();
//position = mark;(将记录的position位置恢复)
buffer.reset();
//向tcp客户端写数据
channel.write(buffer);
buffer.clear();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
closeIO(channel);
}
}).start();
}
//关闭流方法
private static void closeIO(Closeable io){
if(io!=null){
try{
io.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
客户端
public class Client {
public static void main(String[] args) throws IOException {
//创建客户端Channel
SocketChannel channel = SocketChannel.open();
//指定服务端ip、port进行连接
channel.connect(new InetSocketAddress("127.0.0.1",8080));
//接收服务端写入数据
handleMsg(channel);
Scanner scanner = new Scanner(System.in);
while (true){
String str = scanner.next();
if("exit".equals(str)){
break;
}
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
channel.write(buffer);
}
closeIO(channel);
}
/**
* 接收服务端写入数据
*/
public static void handleMsg(SocketChannel channel) {
new Thread(()->{
//创建大小为100的字节缓冲区,用于配合Channel读写操作
ByteBuffer buffer = ByteBuffer.allocate(100);
try {
while (true){
//阻塞等待接收tcp服务端数据
int readCount = channel.read(buffer);
if(readCount<=0){
channel.close();
break;
}
//limit = position;position = 0;
buffer.flip();
System.out.println("接收到数据:");
while (buffer.position()<buffer.limit()){
System.out.print(buffer.get()+" ");
}
System.out.println();
//position = 0;limit = capacity;mark = -1;
buffer.clear();
}
}catch (Exception e){
e.printStackTrace();
}finally {
closeIO(channel);
}
}).start();
}
//关闭流方法
private static void closeIO(Closeable io){
if(io!=null){
try{
io.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
3、Channel非阻塞操作
ServerSocketChannel(部分代码)
//创建服务端Channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//服务端channel监听8080端口
serverSocketChannel.bind(new InetSocketAddress(8080));
//将ServerSocketChannel设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
/**
* 在非阻塞模式下,accept()方法将会直接返回结果,不会阻塞等待tcp客户端连接。
* 在没有tcp客户端连接时,返回的channel为null,不能进行下一步操作。
* 非阻塞模式需要Selector配合使用,Selector对象能够检测ServerSocketChannel的连接事件。
*/
SocketChannel channel = serverSocketChannel.accept();
SocketChannel(部分代码)
//创建客户端Channel
SocketChannel channel = SocketChannel.open();
//指定服务端ip、port进行连接
channel.connect(new InetSocketAddress("127.0.0.1",8080));
//创建大小为100的字节缓冲区,用于配合Channel读写操作
ByteBuffer buffer = ByteBuffer.allocate(100);
/**
* 在非阻塞模式下,read()方法将会直接返回结果,不会阻塞等待有数据写入再返回。
* 在没有数据写入时,返回的readCount为0。
* 非阻塞模式需要Selector配合使用,Selector对象能够检测SocketChannel的数据读写事件。
*/
channel.configureBlocking(false);
int readCount = channel.read(buffer);