JAVA Socket与Bufferd IO

本文详细探讨了Java程序中使用Socket通讯时遇到的问题,包括数据读取和写入的异常现象,以及如何通过修改代码实现数据正确传输。特别强调了BufferedWriter与OutputStream在数据写入时的不同表现,并解释了为何BufferedWriter需要配合flush()方法以确保数据完全发送。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    今天开发一个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字节成帧手段来解决上述问题.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值