`
QING____
  • 浏览: 2249810 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JAVA Socket与Bufferd IO

    博客分类:
  • JAVA
 
阅读更多

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

try {
		InputStreamReader reader = new InputStreamReader(innerSocket.getInputStream());
		StringBuffer sb = new StringBuffer();
		while (true) {
			int _c = reader.read();
			if (_c == -1) {
				break;
			}
			sb.append((char) _c);
		}
		String data = StringUtils.trim(sb.toString());
		//如果数据异常
		if (StringUtils.isBlank(data)) {
			return;
		}
		BufferedWriter out = new BufferedWriter(new OutputStreamWriter(innerSocket.getOutputStream()));
		out.write(">>>" + data);
		out.newLine();
		//out.flush();//++++important
		}
	} catch (Exception e) {
		log.error(e);
	}finally {
		try{
			innerSocket.close();
		}catch (Exception e){
			//
		}
	}
}

   shell调用方式:

>echo ping | nc 127.0.0.1 10101

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

 

OutputStream out = innerSocket.getOutputStream();
String back = ">>>" + data;
out.write(back.getBytes());
...

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

 

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(innerSocket.getOutputStream()));
out.write(">>>" + data);
out.newLine();
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数据后,却始终收不到远端发来的数据,还可能有下面的一种情况:

 

BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("success 1123");
writer.flush();
//socket.shutdownOutput();
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
StringBuffer sb = new StringBuffer();
while(true){
	int _c = reader.read();
	if(_c == -1){
		break;
	}
	sb.append((char)_c);
}
System.out.println(">>>>read:" + sb.toString());
socket.close();

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

 

BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("success 1123");
writer.flush();
socket.shutdownOutput();
...

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

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics