Socket处理粘包

定义一个通用的接口:
public interface IPackageContenParser {
	
	public int getProtocolHeadLength();
	
	public int getProtocolBodyLength(byte[] head);
	
	/**获取一个完整包的长度*/
	public int getCompletePackageLength(byte[] recvUnhandledPkg);
	
	/**从socket收到的数据包中截取一个完整包*/
	public byte[] getCompletePackage(byte[] recvUnhandledPkg);
	
	/**从已通过粘包处理的完整包中获取数据段*/
	public byte[] getDataSegment(byte[] handledPkg);
}

SOcket接收处:
	private final void loopAndHandlePackage(final IPackageContenParser parser) throws IOException {
		int bytesRead = -1;
		/**remains保存解析出完整包后剩余的部分*/
		byte[] remains = EmptyContainer.EMPTY_BYTE_ARRAY;
		
		while (!stopReceive) {
			
			try {
				while ((bytesRead = in.read(buffer)) != -1) {

					byte[] temp = new byte[bytesRead];
					System.arraycopy(buffer, 0, temp, 0, bytesRead);
						
					if(remains.length > 0) {
						/**剩余部分的长度大于0,代表有上一次处理后,还有剩余部分未处理,那么把收到的包和这个半包合并成一个新包(上一次包的数据在前)
						 * 第一次执行时,由于remains长度为0,因此跳过了这个判断内的语句,而后解析第一个包,若是有剩余就组合
						 * */
						temp = PackageTools.combinePackage(remains, temp);
					}
							
					remains = handlePackage(temp, parser);			
				}
			} catch (IOException e) {
				if("socket closed".equalsIgnoreCase(e.getMessage())){
					//关闭时,由于read方法还在阻塞中,因此要抛出这个异常,这是正常的,因此不处理
				}
				else {
					e.printStackTrace();
				}
			}
			
			Sleeper.sleepSomeMilliseconds(50);
		}
	}
	
	
	具体处理粘包方法:
		/***
	 * 解析从服务器收到的包,切割完整包,并返回半包
	 * @param unHandledPkg 从socket收到的数据包
	 * @param parser 不一样的协议,传递不一样的包内容解析器
	 * @return 若是收到的包是一个或者多个完整包,那么返回0长度字节数组
	 * 			若是收到的包是1个半或者N个半数据包,那么截取一个或N个完整包,并把剩余的部分返回
	 */
	private final byte[] handlePackage(byte[] unHandledPkg, IPackageContenParser parser) {
		/**调用一次read,从Server收到的数据包(多是半包、1个包、1.x、2.x....)*/
		int pkgLen = unHandledPkg.length;
		
		/**一个完整数据包的长度*/
		int completePkgLen = parser.getCompletePackageLength(unHandledPkg);
		
		if(completePkgLen > pkgLen) {
			/**当前收到的数据不到一个完整包,则直接返回,等待下一个包*/
			return unHandledPkg;
		} 
		else if (completePkgLen == pkgLen) {
			/**一个完整包,则直接丢到已处理队列*/
			handledQueue.offer(unHandledPkg);
			
			return EmptyContainer.EMPTY_BYTE_ARRAY;
		} 
		else {
			/**有多个包,那么就递归解析,*/
			byte[] onePkg = parser.getCompletePackage(unHandledPkg);
			handledQueue.offer(onePkg);
			
			/**截取除完整包后的剩余部分*/
			byte[] remain = PackageTools.getSubBytes(unHandledPkg, onePkg.length, pkgLen - onePkg.length);
			
			return handlePackage(remain, parser);
		}
	}
	
	public class EmptyContainer {
	public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
	
	@SuppressWarnings("rawtypes")
	public static final Map EMPTY_MAP = new HashMap(0);
	
	@SuppressWarnings("rawtypes")
	public static final List EMPTY_LIST = new ArrayList(0);
}
相关文章
相关标签/搜索