阻塞IO模型经典例子:data = socket.read()

进入11月份以来,忙得焦头烂额,临近年底就开始进入对账节奏,各种查错纠错,加之又负责一个离职同事的项目,一直没时间更新博客。今天遇到了一个很有意思的问题,一个实习生用socket通讯的时候遇到了java.net.SocketTimeoutException:Read timed out异常,指点他以后,顺便讲讲这个东西。我们都知道,利用Socket读取数据一般有两种方法:
1)按照字节流读取(BufferedInputStream.read!=-1)
2)按照字符流读取(BufferedReader.readLine!=null)

不加while循环时是不会存在异常阻塞情况的,但使用while后,如果无数据可读,就会阻塞到有数据可读,或者到达流的末尾,即字节流返回-1,字符流返回null。那我们为什么写程序的时候一般都要用while呢?(当时讲解的时候这个实习生提出的问题,我觉得这个问题很有意思)为什么要使用while?显然易见呀,我们数据又不仅仅是一两行,大多时候数据都比较多,直接readLine而不加while的话,默认只能取得最后一行的数据。但是使用while也有一个比较显而易见的缺点----阻塞等待。

public String callUnify(String importMsg, String svcCode) {
		Socket socket = null;
		BufferedWriter pw = null;
		BufferedReader br = null;
		String resultStr = null;

		String svcCodeFormat = HostFmtTool.formatString(svcCode + "", 8, HostFmtTool.FULL_ADDR_RIGHT, ' ');

		StringBuffer outStr = new StringBuffer();
		// 四个参数代表的意思分别是:原始字符串[获取字符串的字节长度]、需求字符串长度、填充方向、填充空格或0
		outStr.append(HostFmtTool.formatString(StringUtils.charLength(importMsg, "UTF-8") + "", 8,
				HostFmtTool.FULL_ADDR_RIGHT, ' '));
		outStr.append(svcCodeFormat);
		outStr.append(importMsg);

		StringBuffer imStr = new StringBuffer();
		try {
			socket = new Socket();
			socket.connect(new InetSocketAddress("ip", 8080), 3000);
			socket.setSoTimeout(3000); // 设置超时
			pw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
			br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));

			pw.write(outStr.toString());
			pw.flush();

			int rd = 0;
			while ((rd = br.read()) != -1) {
				imStr.append((char) rd);
			}
			/**
			 * 去除报文格式
			 */
			resultStr = StringUtils.subString(imStr.toString(), 16, StringUtils.charLength(imStr.toString()));

		} catch (UnknownHostException e) {
			e.printStackTrace();
			throw new RuntimeException("核心请求失败[" + e.getMessage() + "]");
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException("核心连接异常[" + e.getMessage() + "]");
		} finally {
			try {
				if (pw != null) {
					pw.close();
				}
				if (br != null) {
					br.close();
				}
				if (socket != null) {
					socket.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return resultStr;
	}

那么,如何处理这个问题呢?
1)最常用的方法,设置时长。
2)双方约定长度,比如8位交易码+8位长度(长度不足8位补0)

附录工具类:

import java.io.UnsupportedEncodingException;

/**
 * 字符串按字节截取GBK标准
 * 
 * @author Admin
 */
public class StringUtils {

	private static final String CHARSET = "GBK";

	/**
	 * 获取字符串字节长度
	 * 
	 * @param str
	 *            字符串
	 * @return 字符串长度(使用GBK,汉字算长度为2)
	 * @throws UnsupportedEncodingException
	 */
	public static Integer charLength(String str) {
		if (str == null) {
			return null;
		}
		int length = 0;
		try {
			length = str.getBytes(CHARSET).length;
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e.getMessage());
		}
		return length;
	}

	/** 指定编码集计算中文长度 **/
	public static Integer charLength(String str, String charset) {
		if (str == null) {
			return null;
		}
		int length = 0;
		try {
			length = str.getBytes(charset).length;
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e.getMessage());
		}
		return length;
	}

	/**
	 * 对字符串srcStr截取从 begin到end段字符串
	 * 
	 * @param srcStr
	 *            源字符串
	 * @param begin
	 *            起始位置
	 * @param end
	 *            结束位置
	 * @return 截取位置字符串
	 * @throws UnsupportedEncodingException
	 */
	private static String subStringInner(String sourceString, int begin, int end) throws UnsupportedEncodingException {
		if (sourceString == null) {
			return null;
		}
		if (charLength(sourceString) < begin || charLength(sourceString) < end) {
			throw new RuntimeException("字符串截取位置越界");
		}
		if (begin >= end) {
			throw new RuntimeException("传入起始位置错误");
		}
		byte[] bt = sourceString.getBytes(CHARSET);
		byte[] tt = new byte[end - begin];
		int i = 0;
		int j = 0;
		while (i < bt.length) {
			if (i >= begin && i < end) {
				tt[j] = bt[i];
				j++;
			}
			i++;
		}
		return new String(tt, CHARSET);
	}

	/**
	 * 对字符串srcStr截取从 begin到end段字符串
	 * 
	 * @param srcStr
	 *            源字符串
	 * @param begin
	 *            起始位置
	 * @param end
	 *            结束位置
	 * @return 截取位置字符串
	 */
	public static String subString(String sourceString, int begin, int end) {
		String str;
		try {
			str = subStringInner(sourceString, begin, end);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}
		return str;
	}
}
/**
 * 字符串填充类 保证字符串定长 不定长则填充
 * 
 * @author guxf
 */
public class HostFmtTool {

	public final static int FULL_ADDR_LEFT = 0;
	public final static int FULL_ADDR_RIGHT = 1;

	/**
	 * 字符串填充
	 * 
	 * @param str
	 *            原始字符串
	 * @param length
	 *            需求字符串长度
	 * @param addtr
	 *            填充位置 FULL_ADDR_LEFT 0-左 FULL_ADDR_RIGHT 1-右
	 * @param fullStr
	 *            填充字符串
	 * @return 处理后字符串
	 */
	public static String formatString(String str, int length, int addtr, char fullStr) {
		if (str == null) {
			str = "";
		}
		int strLen = StringUtils.charLength(str);
		if (strLen >= length) {
			return StringUtils.subString(str, 0, length);
		}
		StringBuffer sb = new StringBuffer();
		int l;

		switch (addtr) {
		case 0:
			l = length - strLen;
			for (int i = 0; i < l; i++) {
				sb.append(fullStr);
			}
			sb.append(str);
			break;
		case 1:
			l = length - strLen;
			sb.append(str);
			for (int i = 0; i < l; i++) {
				sb.append(fullStr);
			}
			break;
		default:
			throw new RuntimeException("传入参数错误!");
		}
		return sb.toString();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值