前言
Internal Process Communication(IPC), 是現代計算機電腦中的重要主題,它關係到不同進程之間的數據和信息交換。這種通信對於多進程環境中的協調和數據共享至關重要。同時,TCP/IP協議套件是全球互聯網的基礎,它用於在不同主機之間實現數據傳輸。理解IPC和TCP/IP之間的關聯對於網絡和系統工程師以及軟件開發人員至關重要。
在這篇文章中,我們將介紹C#中利用TCP進行IPC的基本概念、實現方法以及最佳實現方式。我們將從基礎開始,介紹TCP的工作原理,然後逐步深入到如何在C#中實現TCP通訊。透過實際的代碼示例和詳細的解釋,我們將展示如何在您的C#應用程序中有效地利用TCP進行進程(Process)間通訊。
IPC Note
Internal Process Communication (IPC) 是指在同一系統中的不同進程(Process)之間進行通信的過程。Process可以是在同一台機器上運行的不同應用程序,也可以是在不同機器上運行的應用程序。IPC 的主要目的是允許進程之間交換數據、共享資源、同步操作等。
IPC 種類
- Pipes: 一種基於文件描述符的通信機制,用於兩個進程之間的通信。它是一種半雙工通信方式,即同一時間只能有一個方向上的通信。
- Message Queues: 一種基於消息的通信機制,用於多個進程之間的通信。消息隊列可以緩解通信速度不一致的問題,因為消息可以在隊列中等待,直到接收者準備好接收為止。
- Shared Memory: 一種允許多個進程訪問同一塊物理內存的機制。這種通信方式可以實現高效的數據交換,但需要注意同步和互斥的問題。
- Semaphores: 一種用於同步進程之間的通信機制。它可以用於控制共享內存的訪問權限、進程的執行順序等。
- Sockets: 是一種基於網絡的通信機制,可以用於不同機器之間的進程通信。
使用IPC優點
- 增強系統的功能:IPC 可以讓不同進程之間協調工作,共享資源,從而實現更複雜的任務。
- 提高系統效率:IPC 可以讓進程之間快速高效地進行通信,從而減少進程間的等待時間和佔用系統資源的情況。
- 提高系統可靠性:IPC 可以用於實現容錯機制,增加系統的可靠性和穩定性。
實現中需要注意的問題 – C# Internal Process Communication
- 選擇正確的TCP類: C#提供了多種用於TCP通訊的類,如TcpClient和TcpListener。根據您的具體需求選擇適合的類是關鍵,例如,TcpListener適用於建立伺服器,而TcpClient適合用於建立客戶端連接。
- 異步操作: 異步編程模型在處理網路操作時非常重要,因為它可以防止應用程序在等待網路響應時被阻塞。利用C#的異步編程功能(如async和await)能夠提高應用程序的響應性和性能。
- 錯誤處理和異常管理: 網路通訊中可能會遇到各種異常情況,如連接中斷、數據傳輸錯誤等。適當的錯誤處理機制(如try-catch塊)對於確保應用程序的穩定性和可靠性至關重要。
- 安全性考慮: 在進程間通訊中保證數據的安全性是非常重要的。這包括實施加密(如使用SSL/TLS)和驗證機制來保護數據傳輸免受未授權存取和攻擊。
- 資源管理: 確保適當管理TCP連接和其他資源是很重要的。這包括適時關閉連接和釋放不再需要的資源,以避免資源泄漏和其他問題。
- 性能優化: 根據應用程序的需求,可能需要對TCP通訊進行性能優化。這可能包括調整緩衝區大小、選擇合適的發送和接收策略等。
- 跨進程同步: 當多個進程需要通過TCP進行通訊時,確保數據的一致性和同步是重要的。這可能需要實現額外的同步機制,如使用鎖或信號量。
- 測試和調試: 網路編程常常涉及複雜的錯誤情況和邊界條件,因此徹底的測試和調試是至關重要的。這可能包括單元測試、集成測試以及使用適當的調試工具。
- 兼容性和擴展性: 在設計和實現TCP通訊解決方案時,考慮到未來的擴展性和不同環境下的兼容性是重要的。
以我目前所使用的經驗,IPC 在多進程系統中扮演著重要的角色,可以實現進程之間的通信和協作,但需要注意同步、互斥和安全性等問題。選擇適當的 IPC 方式和實現方式可以提高系統的效率和可靠性 ( 依不同情況來採用實現的方式 ) 。
Server端規劃
在規劃Server端時需要注意的部分:
- 監聽的Port:需要選擇一個沒有被其他程序佔用的Port。
- Exception Handle:在 TCP 通訊中,可能會發生各種異常情況,例如客戶端斷開連接、網絡中斷、消息解析錯誤等等。為了保證Server的穩定運行,需要對這些異常情況進行處理。
- Multi-Thread Support:如果有多個Client同時連接到Server,需要支持多線程處理,以避免阻塞其他客戶端的連接。
- Protocol:在 TCP 通訊中,需要定義一個合適的Protocal,包括Data Format、SOF、EOF等,以便Server和Client進行溝通和解析。
- 資源的釋放:在Server停止運行時,需要釋放相關資源,例如關閉 TcpListener對象、關閉網絡流、釋放緩存等。
Planning: 在Server端,需要控制使否開啟Server,以及監聽Client端使否有用戶端進。
Client端規劃
在規劃Client端時需要注意的部分:
- Connect Server:需要使用正確的 IP Address和Port連接到Server。如果Server在局域網中,需要確定本機和服務器在同一網段,或者設置相應的路由器轉發。
- Exception Handle:和 Server 端一樣,Client 端也可能會發生各種異常情況,例如網絡中斷、消息解析錯誤等等。為了保證 Client 的穩定運行,需要對這些異常情況進行處理。
- Protocol:在 TCP 通訊中,需要定義一個合適的Protocol,包括Data Format、SOF、EOF等,以便Server和Client進行溝通和解析。
- 資源釋放:在 Client 端停止運行時,需要釋放相關資源,例如關閉 TcpClient 對象、關閉網絡流、釋放緩存等。
總體來說,實現 TCP 通訊的 Client 端也需要考慮多方面的因素,需要仔細設計和測試,以確保 Client 的穩定運行和高效性能。另外,由於 TCP 通訊是一種同步阻塞的模型,因此在設計 Client 端時,還需要考慮如何處理阻塞和超時等問題,以避免對用戶體驗造成影響。
Planning:
Server Object Implement
以下是我實現IPCService類別的所有程式碼,有付註解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
namespace IPCService { // 定義代理:當 IPC 服務器讀取數據時觸發 public delegate void OnIPCServerReadHandler(byte[] data, IClientPackage clientPackage); // 定義代理:當 IPC 服務器的套接字出現錯誤時觸發 public delegate void OnIPCServerSocketError(SocketError socketError); // 定義 IPC 服務器類,實現 IDisposable 接口 public class IPCServer : IDisposable { // TCP 監聽器 private TcpListener tcpListener; // 處理接收套接字的任務 private Task AcceptSocketHandlerTask; // 標記服務器是否開啟 public bool IsOpen { get; private set; } // 用於存儲客戶端連接的列表 private List<ClientPackage> ClientPackages; // IPC 讀取事件 public event OnIPCServerReadHandler OnIPCRead = null; // IPC 套接字錯誤事件 public event OnIPCServerSocketError OnIPCReceiveSocketError = null; // IPCServer 構造函數 public IPCServer() { // 初始設置為未開啟 IsOpen = false; // 初始化客戶端連接列表 ClientPackages = new List<ClientPackage>(8); // 創建 TCP 監聽器 tcpListener = new TcpListener(TCPSpec.Instance.IPCIPAddress, TCPSpec.Instance.Port); // 初始化接收套接字的任務 AcceptSocketHandlerTask = new Task(AcceptSocketHandler); } // 開啟服務器 public void Open() { IsOpen = true; tcpListener.Start(); AcceptSocketHandlerTask.Start(); } // 向客戶端寫入數據 public bool Write(byte[] data, IClientPackage package) { TCPSpec.Instance.TransferSpecConvert(data, out byte[] buffer); return package.Write(buffer); } // 處理接收套接字的方法 private void AcceptSocketHandler() { do { Socket accpectSocket = null; try { // 接受來自監聽器的套接字連接 accpectSocket = tcpListener.AcceptSocket(); } catch (InvalidOperationException ioe) { // 調試信息 #if DEBUG Debug.WriteLine(ioe.Message); #endif return; } // 為每個接受的套接字創建一個新的客戶端封裝並開始監聽 ClientPackage clientPackage = new ClientPackage(accpectSocket, TCPSpec.Instance.BufferTotalBytes, ReadListenerHandler); clientPackage.Start(); } while (IsOpen); // 持續接受新連接,直到服務器關閉 } // 讀取監聽器處理器 private void ReadListenerHandler(ClientPackage clientPackage) { // 初始化變數用於處理讀取操作 Stopwatch timeoutWatch = new Stopwatch(); bool isReading = false; Socket socket = clientPackage.Socket; int totalReceiveCnt = 0; // 接收完成的處理函數 Action ReceiveDoneHandler = () => { timeoutWatch.Stop(); totalReceiveCnt = 0; clientPackage.Buffer.Clear(); isReading = false; }; // 接收到數據的處理函數 Action ReceivedHandler = () => { TCPSpec.Instance.ReceiveSpecConvert(clientPackage.Buffer, out byte[] data); OnIPCRead?.Invoke(data, clientPackage); }; // 接收超時的處理函數 Action ReceiveTimeoutHandler = () => { OnIPCReceiveSocketError?.Invoke(SocketError.TimedOut); }; try { do { // 檢查是否有可讀數據 if (socket.Poll(100, SelectMode.SelectRead)) { if(!isReading) { isReading = true; timeoutWatch.Restart(); System.Threading.Thread.Sleep(100); } // 接收數據 int receiveCnt = socket.Receive(clientPackage.Buffer, totalReceiveCnt, TCPSpec.Instance.BufferTotalBytes, SocketFlags.None); totalReceiveCnt += receiveCnt; // 檢查是否接收完畢 if(totalReceiveCnt == TCPSpec.Instance.BufferTotalBytes) { ReceivedHandler(); ReceiveDoneHandler(); } // 檢查是否超時 else if(TCPSpec.Instance.IsTimeout(timeoutWatch.ElapsedMilliseconds)) { // 調試信息 #if DEBUG Debug.WriteLine($"IPC Server Socket Receive Timeout"); #endif ReceiveTimeoutHandler(); ReceiveDoneHandler(); } } // 檢查是否需要取消讀取 else if (clientPackage.ReadListenerHandleCancelToken.IsCancellationRequested) { clientPackage.ReadListenerHandleCancelToken.ThrowIfCancellationRequested(); break; } } while (IsOpen); // 持續讀取,直到服務器關閉 } catch(SocketException se) { // 套接字錯誤處理 OnIPCReceiveSocketError?.Invoke(se.SocketErrorCode); } catch (Exception e) { // 一般異常處理 #if DEBUG Debug.WriteLine(e.Message); #endif clientPackage.Close(); } } // 關閉服務器 public void Close() { IsOpen = false; ClientPackages.Close(); } // 處置方法,用於資源回收 public void Dispose() { Close(); tcpListener.Stop(); } } } |
Client Object Implement
Client端內部實現代碼和註解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
namespace IPCService { // 定義代理:當 IPC 客戶端讀取數據時觸發 public delegate void OnIPCClientReadHandler(byte[] data); // 定義 IPC 客戶端類 public class Client { // 定義接收緩衝區 private byte[] rxbuffer = new byte[TCPSpec.Instance.BufferTotalBytes]; // 套接字用於與服務器通信 private Socket clientSocket; // TCP 客戶端 private TcpClient tcpClient; // 處理讀取的任務 private Task ReadListenerHandlerTask; // 取消讀取操作的取消令牌來源 private System.Threading.CancellationTokenSource ReadListenerHandleCancelTokenSource; // 取消讀取操作的取消令牌 private System.Threading.CancellationToken ReadListenerHandleCancelToken; // 標記客戶端是否開啟 public bool IsOpen { get; private set; } // IPC 讀取事件 public event OnIPCClientReadHandler OnIPCRead = null; // IPC 套接字錯誤事件 public event OnIPCServerSocketError OnIPCReceiveSocketError = null; // 客戶端構造函數 public Client() { // 初始設置為未開啟 IsOpen = false; // 初始化取消令牌來源和取消令牌 ReadListenerHandleCancelTokenSource = new System.Threading.CancellationTokenSource(); ReadListenerHandleCancelToken = ReadListenerHandleCancelTokenSource.Token; // 初始化讀取處理任務 ReadListenerHandlerTask = new Task(ReadListenerHandler, ReadListenerHandleCancelToken); // 初始化 TCP 客戶端並設置接收緩衝區大小 tcpClient = new TcpClient(); tcpClient.ReceiveBufferSize = TCPSpec.Instance.BufferBytes; } // 開啟客戶端的同步方法 public void Open() { try { // 連接到服務器 tcpClient.Connect(TCPSpec.Instance.IPCIPAddress, TCPSpec.Instance.Port); // 處理連接成功後的操作 ConnectedHandler(); } catch (OperationCanceledException oce) { // 調試信息 #if DEBUG Debug.WriteLine($"Cancel Read Listener Handler.{Environment.NewLine}[{oce.Message}]"); #endif } finally { // 可以進行的最終清理操作 } } // 開啟客戶端的異步方法 public async Task OpenAsync() { try { // 異步連接到服務器 await tcpClient.ConnectAsync(TCPSpec.Instance.IPCIPAddress, TCPSpec.Instance.Port); // 處理連接成功後的操作 ConnectedHandler(); } catch (OperationCanceledException oce) { // 調試信息 #if DEBUG Debug.WriteLine($"Cancel Read Listener Handler.{Environment.NewLine}[{oce.Message}]"); #endif } finally { // 可以進行的最終清理操作 } } // 處理連接成功後的方法 private void ConnectedHandler() { // 獲取 TCP 客戶端的套接字 clientSocket = tcpClient.Client; // 標記為開啟 IsOpen = true; // 開始讀取處理任務 ReadListenerHandlerTask.Start(); } // 向服務器寫入數據 public bool Write(byte[] data) { // 轉換數據格式 TCPSpec.Instance.TransferSpecConvert(data, out byte[] buffer); try { // 發送數據 clientSocket.Send(buffer, 0, buffer.Length, SocketFlags.None); } catch (ArgumentNullException ane) { // 調試信息 #if DEBUG Debug.WriteLine(ane.Message); #endif return false; } catch (ArgumentOutOfRangeException aore) { // 調試信息 #if DEBUG Debug.WriteLine(aore.Message); #endif Close(); return false; } catch (System.IO.IOException ioe) { // 調試信息 #if DEBUG Debug.WriteLine(ioe.Message); #endif Close(); return false; } catch (ObjectDisposedException ode) { // 調試信息 #if DEBUG Debug.WriteLine(ode.Message); #endif Close(); return false; } return true; } // 讀取監聽器處理器 private void ReadListenerHandler() { // 初始化變數用於處理讀取操作 Stopwatch timeoutWatch = new Stopwatch(); bool isReading = false; Socket socket = this.clientSocket; int totalReceiveCnt = 0; // 接收完成的處理函數 Action ReceiveDoneHandler = () => { timeoutWatch.Stop(); totalReceiveCnt = 0; rxbuffer.Clear(); isReading = false; }; // 接收到數據的處理函數 Action ReceivedHandler = () => { TCPSpec.Instance.ReceiveSpecConvert(rxbuffer, out byte[] data); OnIPCRead?.Invoke(data); }; // 接收超時的處理函數 Action ReceiveTimeoutHandler = () => { OnIPCReceiveSocketError?.Invoke(SocketError.TimedOut); }; try { do { // 檢查是否有可讀數據 if (socket.Poll(100, SelectMode.SelectRead)) { if (!isReading) { isReading = true; timeoutWatch.Restart(); System.Threading.Thread.Sleep(100); } // 接收數據 int receiveCnt = socket.Receive(rxbuffer, totalReceiveCnt, TCPSpec.Instance.BufferTotalBytes, SocketFlags.None); totalReceiveCnt += receiveCnt; // 檢查是否接收完畢 if (totalReceiveCnt == TCPSpec.Instance.BufferTotalBytes) { ReceivedHandler(); ReceiveDoneHandler(); } // 檢查是否超時 else if (TCPSpec.Instance.IsTimeout(timeoutWatch.ElapsedMilliseconds)) { // 調試信息 #if DEBUG Debug.WriteLine($"IPC Server Socket Receive Timeout"); #endif ReceiveTimeoutHandler(); ReceiveDoneHandler(); } } // 檢查是否需要取消讀取 else if (ReadListenerHandleCancelToken.IsCancellationRequested) { ReadListenerHandleCancelToken.ThrowIfCancellationRequested(); } } while (IsOpen); // 持續讀取,直到客戶端關閉 } catch (SocketException se) { // 套接字錯誤處理 OnIPCReceiveSocketError?.Invoke(se.SocketErrorCode); } catch (Exception e) { // 一般異常處理 #if DEBUG Debug.WriteLine(e.Message); #endif Close(); } } // 關閉客戶端 public void Close() { // 標記為關閉 IsOpen = false; // 取消讀取操作 ReadListenerHandleCancelTokenSource.Cancel(); } // 處置方法,用於資源回收 public void Dispose() { ReadListenerHandlerTask.Dispose(); ReadListenerHandleCancelTokenSource.Dispose(); } } } |
下面這段代碼定義了一個名為 IClientPackage 的接口,用於表示 IPC 客戶端的功能。它包含了一個用於檢查連接狀態的屬性和一個用於寫入數據的方法。這樣的接口設計有助於實現不同類型的客戶端,並確保它們具有一致的行為。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 使用 IPCService 命名空間 using IPCService { // 定義一個公共接口 IClientPackage public interface IClientPackage { // 一個只讀屬性,用來表示客戶端是否已連接 bool IsConnect { get; } // 定義一個方法,用於將數據寫入客戶端 // 此方法接受一個 byte 類型的數組作為參數,返回一個布爾值以表示寫入操作是否成功 bool Write(byte[] data); } } |
下方代碼定義了一個名為 ClientPackage 的內部類,它實現了 IClientPackage 接口。這個類包含了與 Socket 通信相關的功能,如開始和停止監聽,以及向 Socket 寫入數據。這樣的實現有助於在 IPC 通信中處理客戶端的連接和數據傳輸。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
// 使用 IPCService 命名空間 using IPCService { // 定義一個內部類 ClientPackage,實現 IClientPackage 接口 internal class ClientPackage : IClientPackage { // 定義一個取消讀取操作的取消令牌來源 private System.Threading.CancellationTokenSource ReadListenerHandleCancelTokenSource; // 定義一個取消讀取操作的取消令牌 internal System.Threading.CancellationToken ReadListenerHandleCancelToken; // 定義一個用於處理監聽的任務 private Task ListenerHandlerTask; // 內部屬性,用於獲取 Socket 實例 internal Socket Socket { get; } // 內部屬性,用於獲取或設置數據緩衝區 internal byte[] Buffer { get; } // 公共屬性,用於表示客戶端是否已連接 public bool IsConnect { get; internal set; } // 客戶端包裝類的構造函數 internal ClientPackage(Socket socket, int BufferSize, Action<ClientPackage> listenerHandler) { // 初始化 Socket 和緩衝區 Socket = socket; Buffer = new byte[BufferSize]; // 初始化取消令牌來源和取消令牌 ReadListenerHandleCancelTokenSource = new System.Threading.CancellationTokenSource(); ReadListenerHandleCancelToken = ReadListenerHandleCancelTokenSource.Token; // 初始化監聽處理任務 ListenerHandlerTask = new Task(() => listenerHandler(this), ReadListenerHandleCancelToken); // 標記為已連接 IsConnect = true; } // 開始監聽的內部方法 internal void Start() { // 開始執行監聽處理任務 ListenerHandlerTask.Start(); } // 關閉客戶端的內部方法 internal void Close() { // 取消並處置取消令牌來源和任務 ReadListenerHandleCancelTokenSource?.Cancel(); ReadListenerHandleCancelTokenSource?.Dispose(); ListenerHandlerTask?.Dispose(); // 關閉並處置 Socket Socket?.Close(); Socket?.Dispose(); // 標記為已斷開連接 IsConnect = false; } // 實現接口中的寫入方法 public bool Write(byte[] data) { // 轉換數據格式 TCPSpec.Instance.TransferSpecConvert(data, out byte[] buffer); Socket socket = this.Socket; try { // 發送數據 socket.Send(buffer); } catch (ArgumentNullException ane) { // 調試信息 #if DEBUG Debug.WriteLine(ane.Message); #endif return false; } catch (SocketException se) { // 調試信息 #if DEBUG Debug.WriteLine(se.Message); #endif Close(); return false; } catch (ObjectDisposedException ode) { // 調試信息 #if DEBUG Debug.WriteLine(ode.Message); #endif Close(); return false; } return true; } // 重寫 ToString 方法,用於返回關於此實例的信息 public override string ToString() { StringBuilder sb = new StringBuilder(128 * Buffer.Length); sb.Append($"Buffer Len [{BitConverter.ToInt32(Buffer, 0)}]"); sb.Append("Buffer Detail Datas:"); for (int idx = 0; idx < Buffer.Length; idx++) { sb.AppendLine($"Buff[{idx}] = {Buffer[idx]}"); } return sb.ToString(); } } } |
下方代碼提供了一個擴展方法 Close 給 IEnumerable<ClientPackage>。這使得可以對 ClientPackage 的集合直接調用 Close 方法,從而閉關集合中所有的 ClientPackage 實例。這是一種常見的做法,用於提高代碼的可讀性和簡潔性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 定義一個內部靜態類 ClientPackagesExtension internal static class ClientPackagesExtension { // 擴展方法:為 IEnumerable<ClientPackage> 集合提供 Close 方法 public static void Close(this IEnumerable<ClientPackage> clientPakgs) { // 遍歷集合中的每個 ClientPackage 實例 foreach (var clientPackage in clientPakgs) { // 調用每個 ClientPackage 的 Close 方法 clientPackage.Close(); } } } |
Array Extensions
這段代碼定義了一個名為 ArrayExtensions 的內部靜態類,其中包含了幾個擴展方法,用於增強數組的功能。這些擴展方法包括清空數組、填充數組、從一個數組複製元素到另一個數組。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// 定義一個內部靜態類 ArrayExtensions internal static class ArrayExtensions { // 擴展方法:為所有數組類型提供 Clear 方法 public static void Clear<T>(this T[] array) { // 使用 Array.Clear 方法清空整個數組 Array.Clear(array, 0, array.Length); } // 擴展方法:為所有數組類型提供 Fill 方法 public static void Fill<T>(this T[] array, T value) { // 從數組的末尾開始,填充數組中的每個元素 int i = array.Length - 1; do { array[i] = value; --i; } while (i != 0); // 重複此操作直到達到數組的開頭 } // 擴展方法:將來源數組的內容複製到目標數組 public static void CopyTo<T>(this T[] sourceBuffer, T[] targetBuffer, int targetIndex, int copySize) { // 從來源數組中複製指定長度的元素到目標數組的指定位置 int buffIdx = 0; for (int idx = targetIndex; idx < targetIndex + copySize; idx++) { targetBuffer[idx] = sourceBuffer[buffIdx]; ++buffIdx; } } // 擴展方法:從來源數組的指定索引處開始,將元素複製到目標數組的指定位置 public static void CopyTo<T>(this T[] sourceArray, int sourceIndex, T[] targetBuffer, int targetIndex, int copySize) { // 從來源數組的指定索引處開始,將指定數量的元素複製到目標數組 int cnt = 0; while (cnt < copySize) { targetBuffer[targetIndex] = sourceArray[sourceIndex]; ++targetIndex; ++sourceIndex; ++cnt; } } } |
完整程式碼:
在我Github內部,包含使用方式,如下:
首先定義了一個使用 IPC (Inter-Process Communication) 通訊的應用程式。它包含一個伺服器 (Server) 和一個客戶端 (Client),以及它們之間的數據交換和錯誤處理。以下是執行步驟的詳細說明:
- 應用啟動 – Main 方法是應用的入口點。
- 初始化 IPC 伺服器:
- 建立 IPCServer 物件。
- 註冊 OnIPCRead 和 OnIPCReceiveSocketError 事件處理方法。
- 調用 Open 方法以開啟伺服器。
- 初始化 IPC 客戶端:
- 建立 Client 物件。
- 註冊 OnIPCRead 和 OnIPCReceiveSocketError 事件處理方法。
- 調用 Open 方法以開啟客戶端。
- 等待 1 秒,以確保伺服器已開始監聽。
- 發送初始數據 (0xFF) 至伺服器。
- 主循環處理使用者輸入:
- 從控制台讀取輸入。
- 解析輸入並將其轉換為 byte 陣列。
- 檢查是否有輸入錯誤或是否輸入了 “exit”。
- 如果輸入正確,則將解析後的數據發送給伺服器。
- 如果輸入 “exit”,則跳出循環。
- 關閉客戶端和伺服器:
- 調用 Close 方法來關閉客戶端和伺服器。
- 解析命令行輸入 – ParseArgument 方法將字串輸入轉換為 byte 陣列。
- 事件處理:
- IpcServer_OnIPCReceiveSocketError 和 Client_OnIPCReceiveSocketError 處理伺服器和客戶端的 socket 錯誤。
- IpcServer_OnIPCRead 處理伺服器接收到數據的事件。
- Client_OnIPCRead 處理客戶端接收到數據的事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
// Importing required namespaces using IPCService; // Custom IPC service implementation using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace TestIPC { // Internal class Program within namespace Test IPC internal class Program { // Entry point of the application static void Main(string[] args) { // Creating and initializing an IPC server IPCServer ipcServer = new IPCServer(); ipcServer.OnIPCRead += IpcServer_OnIPCRead; ipcServer.OnIPCReceiveSocketError += IpcServer_OnIPCReceiveSocketError; ipcServer.Open(); // Creating and initializing an IPC client Client client = new Client(); client.OnIPCRead += Client_OnIPCRead; client.OnIPCReceiveSocketError += Client_OnIPCReceiveSocketError; client.Open(); Thread.Sleep(1000); // Pause for 1 second to allow server to start listening client.Write(new byte[1] { 0xFF }); // Sending initial data to server // Main loop for processing user input string arg = string.Empty; do { arg = Console.ReadLine(); var values = ParseArgument(arg); if (values is null && !arg.ToLower().Contains("exit")) { Console.WriteLine("Input Error"); } else { client.Write(values.ToArray()); } } while (!arg.ToLower().Contains("exit")); // Closing client and server when 'exit' is typed client.Close(); ipcServer.Close(); } // Parses arguments from the command line input static IReadOnlyList<byte> ParseArgument(string arg) { var buffer = new List<byte>(); var args = arg.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); try { foreach (var para in args) { var value = para.Trim(); if (value.StartsWith("0x")) { var byteValue = Convert.ToByte(value.Remove(0, 2), 16); buffer.Add(byteValue); } else { continue; } } } catch(Exception ex) { Console.WriteLine(ex.ToString()); return null; } return buffer.Count == 0 ? null : buffer; } // Event handler for server's socket error private static void IpcServer_OnIPCReceiveSocketError(System.Net.Sockets.SocketError socketError) { Console.WriteLine($"Server Error:{socketError.ToString()}"); } // Event handler for server's IPC read private static void IpcServer_OnIPCRead(byte[] data, IClientPackage clientPackage) { Console.WriteLine($"Server Receive: {string.Join(",", data.Select(val => val.ToString()))}"); var result = clientPackage.Write(new byte[1] { 0x11 }); if (!result) Console.WriteLine($"Client Write Fail!"); } // Event handler for client's socket error private static void Client_OnIPCReceiveSocketError(System.Net.Sockets.SocketError socketError) { Console.WriteLine($"Client Error:{socketError.ToString()}"); } // Event handler for client's IPC read private static void Client_OnIPCRead(byte[] data) { Console.WriteLine($"Client Receive: {string.Join(",", data.Select(val => val.ToString()))}"); } } } |
以上是我對C# Internal Process Communication with TCP實現方式,有完整實現程式碼,可至我的Github下載。
2023/10/24 增加目錄、研究背景、總結內容