socket close方法的作用(close后 远端会得到这个信息 返回-1 也就是eof)

对于InputStream的 read(b, off, len) 方法 public int read(byte[] b, int off, int len) throws IOException,Javadoc的说明为:

If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available becausethe stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b.
The default implementation of this method blocks until the requested amount of input data len has been read,end of file is detected, or an exception is thrown.

那么对于服务端Socket的输入流来说,什么是 end of file - EOF?首先说明一点,没有所谓的标识字符是EOF,对于字节流来说,从0~255的每个字节都是正常的数据,EOF只是输入流的一种状态。

当Socket客户端关闭的时候,服务端输入流在读完所有数据之后就会检测到EOF,然后服务端输入流返回-1。如果客户端Socket没有关闭,并且没有数据可读取的情况下,read方法会阻塞,等待有数据可读。如果设置了SoTimeout,那么直到超时抛出异常,如果没有设置超时,那么会一直等待数据到达。
通过测试,客户端关闭Socket之后,服务端还可以重复读取,每次都返回-1。


————————————————————————

今天开发一个Socket通讯agent,Java 程序中启动一个ServerSocket,用来与shell脚本中"nc"命令通讯并交换数据.ServerSocket可以收到"nc"发送的数据,但是"nc"却接受不到数据..代码样例:

Java代码   收藏代码
  1. try {  
  2.         InputStreamReader reader = new InputStreamReader(innerSocket.getInputStream());  
  3.         StringBuffer sb = new StringBuffer();  
  4.         while (true) {  
  5.             int _c = reader.read();  
  6.             if (_c == -1) {  
  7.                 break;  
  8.             }  
  9.             sb.append((char) _c);  
  10.         }  
  11.         String data = StringUtils.trim(sb.toString());  
  12.         //如果数据异常  
  13.         if (StringUtils.isBlank(data)) {  
  14.             return;  
  15.         }  
  16.         BufferedWriter out = new BufferedWriter(new OutputStreamWriter(innerSocket.getOutputStream()));  
  17.         out.write(">>>" + data);  
  18.         out.newLine();  
  19.         //out.flush();//++++important  
  20.         }  
  21.     } catch (Exception e) {  
  22.         log.error(e);  
  23.     }finally {  
  24.         try{  
  25.             innerSocket.close();  
  26.         }catch (Exception e){  
  27.             //  
  28.         }  
  29.     }  
  30. }  

   shell调用方式:

Java代码   收藏代码
  1. >echo ping | nc 127.0.0.1 10101  

    代码非常简单, 可是为什么不行呢?通过跟踪,Java代码中可以从socket中read到"ping"字符串,但是为什么out.write(">>>")的数据不能被shell获取呢??OK,换一下代码风格,再试一试:

 

Java代码   收藏代码
  1. OutputStream out = innerSocket.getOutputStream();  
  2. String back = ">>>" + data;  
  3. out.write(back.getBytes());  
  4. ...  

    不好意思,成功了...为什么BufferedWriter不行,反而OutStream行呢?后来简单的翻阅了一下源码,简单的修改一下即可:

 

Java代码   收藏代码
  1. BufferedWriter out = new BufferedWriter(new OutputStreamWriter(innerSocket.getOutputStream()));  
  2. out.write(">>>" + data);  
  3. out.newLine();  
  4. out.flush();  

加上"out.flush()"即可,对于BufferedWriter而言,write()方法并没有直接将数据写入到物理的IO流中,而是首先cache在了一个字节数组中,只有当cache的数据量达到buffer-size时才会触发flush,flush的作用就是将多个字节依次写入到IO中,原以为socket.close()方法会执行flush(),尽管OutputStream中有flush方法,事实上没有执行.

 

  • public BufferedWriter(Writer out, int bufferSize)

所以使用BufferedWriter的时候,一定要在IO关闭之前,调用flush()方法,否则将会丢失部分数据...真是费劲.

 

    不过顺便还要提一个问题,如果JAVA Socket向远端write数据后,却始终收不到远端发来的数据,还可能有下面的一种情况:

 

Java代码   收藏代码
  1. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));  
  2. writer.write("success 1123");  
  3. writer.flush();  
  4. //socket.shutdownOutput();  
  5. InputStreamReader reader = new InputStreamReader(socket.getInputStream());  
  6. StringBuffer sb = new StringBuffer();  
  7. while(true){  
  8.     int _c = reader.read();  
  9.     if(_c == -1){  
  10.         break;  
  11.     }  
  12.     sb.append((char)_c);  
  13. }  
  14. System.out.println(">>>>read:" + sb.toString());  
  15. socket.close();  

    上面的代码中,socket向远端write数据之后,开始read远端返回的数据,但是始终无法read到数据,是怎么回事?其实原因也很简单,这就是典型的"Socket死锁": socket write()时,同时远端也在read,因为socket通讯是基于流(frame)的,如果远端read时没有收到EOF,那么read操作将一直阻塞,直到socket关闭.上面的代码,就是出现了远端socket read一直阻塞而没有机会执行write,同时本地socket也因为read阻塞,导致了"死锁",代码调整如下:

 

Java代码   收藏代码
  1. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));  
  2. writer.write("success 1123");  
  3. writer.flush();  
  4. socket.shutdownOutput();  
  5. ...  

    在writer数据结束后,执行shutdownOutput,将output流通道关闭,此时远端socket就会read到EOF, 认为通道中不会再有数据可读.shutdownOutput()方法不会关闭整个socket,只是关闭了socket中的outputStream,不过一旦调用了此方法,此后将不能再调用writer.write()方法.

    此外还可以通过socket IO字节成帧手段来解决上述问题.

你可能感兴趣的:(socket close方法的作用(close后 远端会得到这个信息 返回-1 也就是eof))