传统的 网络编程
TCP ServerSocket Socket (阻塞式)
UDP DatagramSocket DatagramPacket
1 ServerSocketChannel、SocketChannel实现阻塞式网络编程
ServerSocketChannel是一个基于通道的socket监听器,等同于ServerSocket类。SocketChannel是一个基于通道的客户端套接字,等同于Socket类。
public static void main(String[] args) throws IOException {
ServerSocketChannel listener=ServerSocketChannel.open();
//绑定地址
listener.bind(new InetSocketAddress("127.0.0.1", 8888));
System.out.println("服务器已启动");
//监听
SocketChannel channel = listener.accept();
//创建缓冲区
ByteBuffer buffer=ByteBuffer.allocate(1024);
int len = channel.read(buffer);
//切换读模式
buffer.flip();
String data=new String(buffer.array(), 0, len);
System.out.println(data);
channel.close();
}
public static void main(String[] args) throws IOException {
SocketChannel sc=SocketChannel.open(new InetSocketAddress("127.0.0.1", 8888));
ByteBuffer buffer=ByteBuffer.allocate(1024);
buffer.put("好久不见".getBytes());
buffer.flip();
sc.write(buffer);
buffer.clear();
sc.close();
}
2.selector简介
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
选择器提供选择执行已经就绪的任务的能力.从底层来看,Selector提供了询问通道是否已经准备好执行每个I/O操作的能力。Selector 允许单线程处理多个Channel。仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道,这样会大量的减少线程之间上下文切换的开销。
-
选择器(Selector) :Selector选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。
-
可选择通道(SelectableChannel):SelectableChannel这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。因为FileChannel类没有继承SelectableChannel因此是不是可选通道,而所有socket通道都是可选择的,SocketChannel和ServerSocketChannel是SelectableChannel的子类。
-
选择键(SelectionKey):**选择键封装了特定的通道与特定的选择器的注册关系。选择键对象被SelectableChannel.register()返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),选择键支持四种操作类型:
- Connect 连接
- Accept 接受请求
- Read 读
- Write 写
java中定义了四种常量来表示这四种操作类型:
- SelectionKey.OP_CONNECT
- SelectionKey.OP_ACCEPT
- SelectionKey.OP_READ
- SelectionKey.OP_WRITE
3.实现非阻塞式网络编程
服务端
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc=ServerSocketChannel.open();
//设置为非阻塞式
ssc.configureBlocking(false);
//绑定ip地址、端口号
ssc.bind(new InetSocketAddress("127.0.0.1", 9999));
//创建Selector(轮询器)
Selector selector=Selector.open();
//注册选择器
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(selector.select()>0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isAcceptable()){ //测试通道是否可以接收Socket连接
SocketChannel accept = ssc.accept();
accept.configureBlocking(false);
accept.register(selector, SelectionKey.OP_READ);
}else if(selectionKey.isReadable()){
SocketChannel socketChannel=(SocketChannel) selectionKey.channel();
ByteBuffer buffer=ByteBuffer.allocate(1024);
int len=-1;
while ((len=socketChannel.read(buffer))>0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
if(len==-1)
socketChannel.close();
}
iterator.remove();
}
}
}
客户端
public static void main(String[] args) throws IOException {
SocketChannel sc=SocketChannel.open( new InetSocketAddress(9999));
sc.configureBlocking(false);
Selector selector=Selector.open();
sc.register(selector, SelectionKey.OP_WRITE);
Scanner input=new Scanner(System.in);
ByteBuffer buffer=ByteBuffer.allocate(1024);
while (true){
System.out.println("请输入消息:");
String message = input.next();
buffer.put(message.getBytes());
buffer.flip();
sc.write(buffer);
buffer.clear();
if(message.equals("拜拜"))
break;
}
sc.close();
}