程式設計能力在不斷的總結中進步以及成長,最近的半年裡,對之前的開源專案程式碼進行迴歸,在重構的過程中進行了很多思考,很多次都想放棄重構,畢竟一個已經在使用的專案,重構基礎程式碼就相當於重新開發了,不過最終還是下定了決心,畢竟重構就是一個成長過程,要想進步,就要不斷的發現原有程式碼的不足,使用新的思維去優化原來的東西,在重構的過程中,針對tcp網路通訊,我有了新的思路。
無論是網上的tcp範例或者書本的範例,只要是非同步方式,讀寫基本都是分離的,也就是說如果想通過tcp去和伺服器端進行資料互動,你在A方法傳送了指令,只能在B方法接收並進行處理,這種傳送與接收資料不在同一個方法內,嚴重干擾了程式碼的順序執行邏輯,我們需要將傳送前和接收後的程式碼寫在不同的地方,而現在,我們可以寫在一起。例如:
long fileId = 1001; // 通過tcp傳送命令到伺服器端 bool result = await tcp.Delete(fileId); // 返回結果後處理相應的業務 if(result){ // 重新整理資料 } else{ // 提示錯誤 }
沒錯,這樣子看上去和呼叫http請求一模一樣,是不是邏輯變得簡單了?
畫一個時序圖吧,我們來看一下呼叫過程
從上圖我們可以看出,和其它網上範例的不同在於,我們自己加了一個task管理模組,我們通過自己對Task進行管理,實現了tcp的非同步呼叫但順序執行程式碼。
將以上範例用在實際程式碼上,效果如下:
/// <summary> /// 等待請求 /// </summary> /// <param name="token">請求的token</param> /// <param name="timeOut">超時時間</param> /// <returns></returns> public async Task<T> Wait(T1 token, TimeSpan timeOut) { TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<T>(); // 將等待結果的任務加入字典中 TaskDict.Add(token, taskCompletionSource); try { T ret = await taskCompletionSource.Task.WaitAsync(timeOut); return ret; } catch { // 如果超時,則移除字典中的任務,並丟擲超時異常 if (TaskDict.ContainsKey(token)) { TaskDict.Remove(token); } throw; } }
async...await是非同步方案的一種,配合TaskCompletionSource可以將分散於2個不同地方的程式碼合併到一起,對簡化程式碼邏輯來說還是比較靠譜的。
目前這種非同步方案已在自己的開源專案(Wireboy.Socket.P2PSocket)中使用了,並且也將此方案適配在了公司專案,主要運用於使用命名管道的程序間通訊。總的來說還是一個比較不錯的方案。