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);
}
相關文章
相關標籤/搜索