Socket通道
socket通道与文件通道有不一样的特征:
1.socket通道类可以运行与非阻塞模式并且是可选择的,这两个性能可以激活大程序(比如网络服务器和中间件组件)巨大的可伸缩和灵活性,因此,再也没有为每个socket连接使用一个线程的必要了。借助NIO类,一个或者几个线程可以管理成百上千的活动socket连接了,并且只有很少甚至没有性能的损失。
2.全部socket通道类(DatagramChannel、SocketChannel,ServerSocketChannel)被实例化时都会创建一个对应的socket对象,也就是来自java.net的类(Datagram,socket,serverSocket),这些socket可以通过调用socket()方法从通道类获取,另外,这三个类都有getChannel方法获取通道。
3.每一个socket通道都有一个关联的socket对象,反之不是。如果直接实例化的方式创建一个socket对象,它就不会有关联的socketChannel,调用它的getChannel方法将总是返回null。
非阻塞模式
传统java socket的阻塞性质曾经是java程序可伸缩性的最重要制约之一,非阻塞io是许多复杂的,高性能的程序构建的基础。
要把一个socket通道置于非阻塞模式,要依赖的是socket通道类的SelectableChannel。
public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel{ ... public abstract void configureBlocking(boolean block) throws IOException; public abstract boolean isBlocking(); public abstract Object blockngLock(); ...}
从该api中可以看出,设置或重新设置一个通道的阻塞模式是很简单的,只要调用configureBlocking方法即可,参数的true为阻塞模式,参数为false设为非阻塞模式。isBloking方式是判断某个socket通道处于哪种模式。偶尔,我们需要防止socket通道的阻塞模式被修改,所以有一个
blockingLock方法,该方法返回一个非透明对象引用,返回的对象是通道实现修改阻塞模式时内部使用的,只有拥有该对象的锁的线程才可以修改通道的阻塞模式,对于确保在执行代码关键部分时socket通道的阻塞模式不会改变,以及在不影响其他线程的前提下暂时改变阻塞模式来说,
这个方法是非常方便的。
服务端
public static void main(String[] args) throws Exception { int port = 9090; if (args != null && args.length > 0) { port = Integer.parseInt(args[0]); } ServerSocketChannel ssc = ServerSocketChannel.open();// 打开一个ServerSocketChannel对象。 ssc.configureBlocking(false);// 设置该通道是非阻塞式的 ssc.bind(new InetSocketAddress(port));// 绑定端口 System.out.println("开始等待客户端的数据!时间为" + System.currentTimeMillis()); while (true) { SocketChannel sc = ssc.accept(); if (sc == null) { // 如果当前没有数据,等待1秒钟再次轮询是否有数据,在学习了Selector之后此处可以使用Selector Thread.sleep(1000); } else { System.out.println("客户端已有数据到来,客户端ip为:" + sc.socket().getRemoteSocketAddress() + ", 时间为" + System.currentTimeMillis()); ByteBuffer bb = ByteBuffer.allocate(100);// 初始化一个缓冲区 sc.read(bb); bb.flip(); while (bb.hasRemaining()) { System.out.print((char) bb.get());// get方法一次获取一个字节,然后position往后移动一个位置。 } sc.close(); System.exit(0); } } }
客户端
服务端经常会使用非阻塞socket通达,因为它们使同时管理很多socket通道变得更容易,客户端不强求,因为客户端发起的socket操作往往比较少,且都一个个发起的。但是,在客户端使用一个或几个非阻塞模式的socket通道也是有益处的,比如借助非阻塞socket通道,gui程序可以
专注于用户请求并同时维护与一个或多个服务器的会话。在很多程序上,非阻塞模式都是有用的。
1 public class EchoClient { 2 private static final String STR = "Hello World!"; 3 private static final String REMOTE_IP = "127.0.0.1"; 4 5 public static void main(String[] args) throws Exception { 6 int port = 9090; 7 if (args != null && args.length > 0) { 8 port = Integer.parseInt(args[0]); 9 }10 SocketChannel sc = SocketChannel.open();11 sc.configureBlocking(false);12 sc.connect(new InetSocketAddress(REMOTE_IP, port));13 while (!sc.finishConnect()) {14 System.out.println("同" + REMOTE_IP + "的连接正在建立,请稍等!");15 Thread.sleep(10);16 }17 System.out.println("连接已建立,待写入内容至指定ip+端口!时间为"18 + System.currentTimeMillis());19 ByteBuffer bb = ByteBuffer.allocate(STR.length());20 bb.put(STR.getBytes());21 bb.flip(); // 写缓冲区的数据之前一定要先反转(flip)22 sc.write(bb);//会把position到limit的内容写到通道里面,如果没有调用flip,那么就是空。23 bb.clear();24 sc.close();25 }26 }