ServerSocketChannel

让我们从最简单的ServerSocketChannel来开始对socket通道类的讨论

ServerSocketChannel是一个基于通道的socket监听器。它同我们所熟悉的java.net.ServerSocket执行相同的基本任务,不过它增加了通道语义,因此能够在非阻塞模式下运行。

用静态的open( )工厂方法创建一个新的ServerSocketChannel对象,将会返回同一个未绑定的java.net.ServerSocket关联的通道。该对等ServerSocket可以通过在返回的ServerSocketChannel上调用socket( )方法来获取。作为ServerSocketChannel的对等体被创建的ServerSocket对象依赖通道实现。这些socket关联的SocketImpl能识别通道。通道不能被封装在随意的socket对象外面。

由于ServerSocketChannel没有bind( )方法,因此有必要取出对等的socket并使用它来绑定到一个端口以开始监听连接。我们也是使用对等ServerSocket的API来根据需要设置其他的socket选项

[java] view
plain
copy

  1. ServerSocketChannel ssc = ServerSocketChannel.open();
  2. ServerSocket serverSocket = ssc.socket(); // Listen on port 1234
  3. serverSocket.bind(new InetSocketAddress(1234));

同它的对等体java.net.ServerSocket一样,ServerSocketChannel也有accept( )方法。

一旦您创建了一个ServerSocketChannel并用对等socket绑定了它,然后您就可以在其中一个上调用accept( )。

如果您选择在ServerSocket上调用accept( )方法,那么它会同任何其他的ServerSocket表现一样的行为:总是阻塞并返回一个java.net.Socket对象(阻塞的!!!!)

。如果您选择在ServerSocketChannel上调用accept( )方法则会返回SocketChannel类型的对象,返回的对象能够在非阻塞模式下运行。假设系统已经有一个安全管理器(security manager),两种形式的方法调用都执行相同的安全检查。

如果以非阻塞模式被调用,当没有传入连接在等待时,ServerSocketChannel.accept( )会立即返回null (因为他是非阻塞的所以要有返回,)。

正是这种检查连接而不阻塞的能力实现了可伸缩性并降低了复杂性。可选择性也因此得到实现。我们可以使用一个选择器实例来注册一个ServerSocketChannel对象以实现新连接到达时自动通知的功能。

演示如何使用一个非阻塞的accept( )方法:

[java] view
plain
copy

  1. package com.anders.selector2;
  2. import java.net.InetSocketAddress;
  3. import java.net.ServerSocket;
  4. import java.nio.ByteBuffer;
  5. import java.nio.channels.ServerSocketChannel;
  6. import java.nio.channels.SocketChannel;
  7. public class ServerSocketChannelApp {
  8. private static final String MSG = "hello, I must be going \n";
  9. public static void main(String[] args) throws Exception {
  10. int port = 8989;
  11. ServerSocketChannel ssc = ServerSocketChannel.open();
  12. ServerSocket ss = ssc.socket();
  13. ss.bind(new InetSocketAddress(port));
  14. // set no blocking
  15. ssc.configureBlocking(false);
  16. ByteBuffer buffer = ByteBuffer.wrap(MSG.getBytes());
  17. while (true) {
  18. System.out.println("wait for connection ……");
  19. SocketChannel sc = ssc.accept();
  20. if (sc == null) {
  21. // no connections, snooze a while ...
  22. Thread.sleep(1000);
  23. } else {
  24. System.out.println("Incoming connection from " + sc.socket().getRemoteSocketAddress());
  25. buffer.rewind();
  26. //write msg to client
  27. sc.write(buffer);
  28. sc.close();
  29. }
  30. }
  31. }
  32. }

ps  阻塞和非阻塞

传统的serversocket阻塞模式

[java] view
plain
copy

  1. public class ServerSocketApp {
  2. public static void main(String[] args) throws Exception {
  3. ServerSocket ss = new ServerSocket(8989);
  4. ss.accept();
  5. System.out.println(1);
  6. }
  7. }

运行这个程序 为什么没有输出1 ??? 

因为ServerSocket  是阻塞模式的 ,什么是阻塞,就是在没有任何连接之前,accept方法一直在那里阻塞着,直到有connection来继续往下执行,所以在运行程序的时候,并没输出1,若要输出  telnet一下就可以了

nio中的 非阻塞:

[java] view
plain
copy

  1. public static void main(String[] args) throws Exception {
  2. ServerSocketChannel ssc = ServerSocketChannel.open();
  3. ServerSocket ss = ssc.socket();
  4. ss.bind(new InetSocketAddress(8989));
  5. // set no blocking
  6. ssc.configureBlocking(false);
  7. ssc.accept();
  8. System.out.println(1);
  9. }

运行这个程序  有1  输出!!

这就是因为  它是非阻塞模式的。

SocketChannel

[java] view
plain
copy

  1. public abstract class SocketChannel extends AbstractSelectableChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel {
  2. // This is a partial API listing
  3. public static SocketChannel open() throws IOException;
  4. public static SocketChannel open(InetSocketAddress remote) throws IOException;
  5. public abstract Socket socket();
  6. public abstract boolean connect(SocketAddress remote) throws IOException;
  7. public abstract boolean isConnectionPending();
  8. public abstract boolean finishConnect() throws IOException;
  9. public abstract boolean isConnected();
  10. public final int validOps();
  11. }

Socket和SocketChannel类封装点对点、有序的网络连接,类似于我们所熟知并喜爱的TCP/IP网络连接。SocketChannel扮演客户端发起同一个监听服务器的连接。直到连接成功,它才能收到数据并且只会从连接到的地址接收

每个SocketChannel对象创建时都是同一个对等的java.net.Socket对象串联的。静态的open( )方法可以创建一个新的SocketChannel对象,而在新创建的SocketChannel上调用socket( )方法能返回它对等的Socket对象;在该Socket上调用getChannel( )方法则能返回最初的那个SocketChannel。

新创建的SocketChannel虽已打开却是未连接的。在一个未连接的SocketChannel对象上尝试一个I/O操作会导致NotYetConnectedException异常。

我们可以通过在通道上直接调用connect( )方法或在通道关联的Socket对象上调用connect( )来将该socket通道连接。

一旦一个socket通道被连接,它将保持连接状态直到被关闭。

您可以通过调用布尔型的isConnected( )方法来测试某个SocketChannel当前是否已连接。

[java] view
plain
copy

  1. 第二种带InetSocketAddress参数形式的open( )是在返回之前进行连接的便捷方法。这段代码:
  2. SocketChannel socketChannel = SocketChannel.open (new InetSocketAddress ("somehost", somePort));
  3. 等价于下面这段代码:
  4. SocketChannel socketChannel = SocketChannel.open( );
  5. socketChannel.connect (new InetSocketAddress ("somehost", somePort));

1 如果您选择使用传统方式进行连接——通过在对等Socket对象上调用connect( )方法,那么传统的连接语义将适用于此。线程在连接建立好或超时过期之前都将保持阻塞。

2 如果您选择通过在通道上直接调用connect( )方法来建立连接并且通道处于阻塞模式(默认模式),那么连接过程实际上是一样的。在SocketChannel上并没有一种connect( )方法可以让您指定超时(timeout)值,当connect( )方法在非阻塞模式下被调用时SocketChannel提供并发连接:它发起对请求地址的连接并且立即返回值

如果返回值是true,说明连接立即建立了(这可能是本地环回连接);

如果连接不能立即建立,connect( )方法会返回false且并发地继续连接建立过程。

面向流的的socket建立连接状态需要一定的时间,因为两个待连接系统之间必须进行包对话以建立维护流socket所需的状态信息。

跨越开放互联网连接到远程系统会特别耗时。假如某个SocketChannel上当前正由一个并发连接,isConnectPending( )方法就会返回true值。调用finishConnect( )方法来完成连接过程,该方法任何时候都可以安全地进行调用。假如在一个非阻塞模式的SocketChannel对象上调用finishConnect( )方法,将可能出现下列情形之一:

 connect( )方法尚未被调用。那么将产生NoConnectionPendingException异常。

 连接建立过程正在进行,尚未完成。那么什么都不会发生,finishConnect( )方法会立即返回false值。

 在非阻塞模式下调用connect( )方法之后,SocketChannel又被切换回了阻塞模式。那么如果有必要的话,调用线程会阻塞直到连接建立完成,finishConnect( )方法接着就会返回true值。

 在初次调用connect( )或最后一次调用finishConnect( )之后,连接建立过程已经完成。那么SocketChannel对象的内部状态将被更新到已连接状态,finishConnect( )方法会返回true值,然后SocketChannel对象就可以被用来传输数据了。

 连接已经建立。那么什么都不会发生,finishConnect( )方法会返回true值。

当通道处于中间的连接等待(connection-pending)状态时,您只可以调用finishConnect( )、isConnectPending( )或isConnected( )方法。

一旦连接建立过程成功完成,isConnected( )将返回true值。

[java] view
plain
copy

  1. InetSocketAddress addr = new InetSocketAddress (host, port);
  2. SocketChannel sc = SocketChannel.open( );
  3. sc.configureBlocking (false);
  4. sc.connect (addr);
  5. while ( ! sc.finishConnect( )) {
  6. doSomethingElse( );
  7. }
  8. doSomethingWithChannel (sc);
  9. sc.close( );

一段用来管理异步连接的可用代码。

[java] view
plain
copy

  1. public class SocketChannelApp {
  2. public static void main(String[] args) throws Exception {
  3. InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 8989);
  4. SocketChannel sc = SocketChannel.open();
  5. sc.connect(addr);
  6. sc.configureBlocking(false);
  7. while (!sc.finishConnect()) {
  8. doSomethings();
  9. }
  10. //Do something with the connected socket
  11. ByteBuffer buffer = ByteBuffer.wrap(new String("Hello server!").getBytes());
  12. sc.write(buffer);
  13. sc.close();
  14. }
  15. private static void doSomethings() {
  16. System.out.println("do something useless!");
  17. }
  18. }

server还是采用上篇的 ,我把它简单的改了改

[java] view
plain
copy

  1. public class ServerSocketChannelApp {
  2. private static final String MSG = "hello, I must be going \n";
  3. public static void main(String[] args) throws Exception {
  4. int port = 8989;
  5. ServerSocketChannel ssc = ServerSocketChannel.open();
  6. ServerSocket ss = ssc.socket();
  7. ss.bind(new InetSocketAddress(port));
  8. // set no blocking
  9. ssc.configureBlocking(false);
  10. ByteBuffer buffer = ByteBuffer.wrap(MSG.getBytes());
  11. while (true) {
  12. //          System.out.println("wait for connection ……");
  13. SocketChannel sc = ssc.accept();
  14. if (sc == null) {
  15. // no connections, snooze a while ...
  16. Thread.sleep(1000);
  17. } else {
  18. System.out.println("Incoming connection from " + sc.socket().getRemoteSocketAddress());
  19. ByteBuffer readerBuffer = ByteBuffer.allocate(1024);
  20. sc.read(readerBuffer);
  21. readerBuffer.flip();
  22. //output get
  23. out(readerBuffer);
  24. buffer.rewind();
  25. sc.write(buffer);
  26. sc.close();
  27. }
  28. }
  29. }
  30. private static void out(ByteBuffer readerBuffer) {
  31. StringBuffer sb = new StringBuffer();
  32. for (int i = 0; i < readerBuffer.limit(); i++) {
  33. char c = (char) readerBuffer.get();
  34. sb.append(new String(new char[]{c}));
  35. }
  36. System.out.println(sb.toString());
  37. }
  38. }

ps:

如果尝试异步连接失败,那么下次调用finishConnect( )方法会产生一个适当的经检查的异常以指出问题的性质。通道然后就会被关闭并将不能被连接或再次使用。与连接相关的方法使得我们可以对一个通道进行轮询并在连接进行过程中判断通道所处的状态。第四章中,我们将了解到如何使用选择器来避免进行轮询并在异步连接建立之后收到通知。Socket通道是线程安全的。并发访问时无需特别措施来保护发起访问的多个线程,不过任何时候都只有一个读操作和一个写操作在进行中。请记住,sockets是面向流的而非包导向的。它们可以保证发送的字节会按照顺序到达但无法承诺维持字节分组。某个发送器可能给一个socket写入了20个字节而接收器调用read(
)方法时却只收到了其中的3个字节。剩下的17个字节还是传输中。由于这个原因,让多个不配合的线程共享某个流socket的同一侧绝非一个好的设计选择。

connect( )和finishConnect( )方法是互相同步的,并且只要其中一个操作正在进行,任何读或写的方法调用都会阻塞,即使是在非阻塞模式下。如果此情形下您有疑问或不能承受一个读或写操作在某个通道上阻塞,请用isConnected( )方法测试一下连接状态。

Java Socket:Java-NIO-ServerSocketChannel的更多相关文章

  1. Java Socket(3): NIO

    NIO采取通道(Channel)和缓冲区(Buffer)来传输和保存数据,它是非阻塞式的I/O,即在等待连接.读写数据(这些都是在一线程以客户端的程序中会阻塞线程的操作)的时候,程序也可以做其他事情, ...

  2. Java Socket NIO入门

    Java Socket.SocketServer的读写.连接事件监听,都是阻塞式的.Java提供了另外一种非阻塞式读写.连接事件监听方式——NIO.本文简单的介绍一个NIO Socket入门例子,原理 ...

  3. Java NIO ServerSocketChannel

    A Java NIO ServerSocketChannel is a channel that can listen for incoming TCP connections, just like ...

  4. 【JAVA】【NIO】10、Java NIO ServerSocketChannel

    Java NIO的ServerSocketChannel是用来监听外来TCP连接的channel,就想标准Java网络中的ServerSocket.实比例如以下: ServerSocketChanne ...

  5. Java Socket, DatagramSocket, ServerSocketChannel io代码跟踪

    Java Socket, DatagramSocket, ServerSocketChannel这三个分别对应了,TCP, udp, NIO通信API封装.JDK封装了,想跟下代码,看下具体最后是怎么 ...

  6. Java Socket IO(BIO、NIO)

    总结下Java socket IO.首先是各种IO的定义,这个定义似乎也是众说纷纭.我按照stackoverflow上面的解释: IO有两种分法:按照阻塞或者按照同步.按照阻塞,有阻塞IO和非阻塞IO ...

  7. java socket nio编程

    上次写了一个socket的基本编程,但是有个问题,阻塞特别严重,于是小编便去找了nio学习了一下... public class TimeServer { public static void mai ...

  8. java socket 模拟im 即时通讯

    自己想了一下怎么实现,就写了,没有深究是否合理.更多处理没有写下去,例如收件人不在线,应该保存在数据库,等下一次连接的时候刷新map,再把数据发送过去,图片发送也没有做,也没有用json格式 sock ...

  9. java非阻塞NIO和阻塞IO

    1         非阻塞NIO和阻塞IO 1.1           定义 阻塞IO:线程被阻塞,去处理一个读取和写入,中间如果有等待时间,则线程被占用,也不能处理其他任务: 非阻塞IO(new I ...

  10. Java中的NIO基础知识

    上一篇介绍了五种NIO模型,本篇将介绍Java中的NIO类库,为学习netty做好铺垫 Java NIO 由3个核心组成,分别是Channels,Buffers,Selectors.本文主要介绍着三个 ...

随机推荐

  1. Java中for_each循环的使用

    最近在看一些和安卓相关的书籍,看到了for_each这种循环结构,这是为了简化java的for循环而改造的一种方便使用的格式. 格式如下: for(数据类型 变量:集合) 语句块 接下来看一个例程: ...

  2. Springmvc注解注入的简单demo

    今天看了注解注入觉得确实简化了xml配置,一般情况下Spring容器要成功启动的三大要件分别是:Bean定义信息,Bean实现类,以及spring本身.如果采取基于XML的配置,Bean信息和Bean ...

  3. T-SQL中的APPLY用法(半翻译)

    本文接上文:T-SQL 中的CROSS JOIN用法(半翻译) 同样可用于微软认证70-461: Querying Microsoft SQL Server 2012考试的学习中. --------- ...

  4. Servlet之Request对象

    下面的方法可用在 Servlet 程序中读取 HTTP 头.这些方法通过HttpServletRequest 对象可用. 1    Cookie[] getCookies() 返回一个数组,包含客户端 ...

  5. Android初级教程对大量数据的做分页处理理论知识

    有时候要加载的数据上千条时,页面加载数据就会很慢(数据加载也属于耗时操作).因此就要考虑分页甚至分批显示.先介绍一些分页的理论知识.对于具体用在哪里,会在后续博客中更新. 分页信息 1,一共多少条数据 ...

  6. 详解EBS接口开发之销售订单挑库发放

     1. 对销售订单的有效性验证     1)检查销售订单的行是否被完全传回客户化表     2)验证销售订单的关键字段     3)检查子库存是否启用了货位控制,如果启用了货位控制,没有生成货位, ...

  7. BCD码与16进制互转算法

    关于这类算法,以前的文章已经讲过类似的:BCD码转二进制 #include <stdio.h> // HEX转BCD //bcd_data(<0x255,>0) unsigne ...

  8. 剑指Offer——网易校招内推笔试题+模拟题知识点总结

    剑指Offer--网易校招内推笔试题+模拟题知识点总结 前言 2016.8.2 19:00网易校招内推笔试开始进行.前天晚上利用大约1小时时间完成了测评(这个必须做,关切到你能否参与面试).上午利用2 ...

  9. Transform介绍(Unity3D开发之二)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=491 可能unity中接触较早的 ...

  10. SSH深度历险(二) Jboss+EJB的第一个实例

    学习感悟:每次学习新的知识,都会通过第一个小的实例入手,获得成就感,经典的Hello Workd实例奠定了我们成功的大门哈,这些经典的实例虽小但是五脏俱全呢,很好的理解了,Ejb的核心. 今天主要以这 ...