Try   HackMD

LOSoMan的C# Socket通訊講解(C#為主)

tags: LOSo講解 C# Socket通訊

Socket通訊概論

Socket通訊為使用Ethernet傳輸資訊的一種方式。運作的方式就如同電話撥接,透過設定插座的號碼,其他人就可透過這串號碼與其通話。而Socket的號碼就是IP Number加上TCP Port。

Socket的連線端可以分為主機端(Server)及客戶端(Client)。通常Server要先設定並連接上傳輸埠口,才能開始監聽是否有Client請求連接;而Client也一樣要連接上傳輸埠口,才能發送要求連線。而當通訊結束時,則會互相確認要中斷連線。

C#裡的Socket類別及常用方法

在C#程式裡,提供了Socket類別讓programmer可以更簡單的設計出可連線的應用程式。

Socket類別的宣告,如:(參閱連結)

Socket serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

常使用的方法有:

  1. Bind(EndPoint): 使 Socket 與本機端點建立關聯。"EndPoint"為要與 EndPoint 關聯的本機 Socket。
  2. Listen(Int32)Listen(): 將 Socket 置於接聽狀態。
  3. Accept(): 建立新建立連接的新 Socket。
  4. Connect(EndPoint): 建立與遠端主機的連線。
  5. Send(Byte[]): 傳送資料至已連接的 Socket。
  6. Receive(Byte[]): 從已繫結的 Socket 接收資料至接收緩衝區中。
  7. Close(): 關閉 Socket 連接並釋放所有相關資源。

範例程式

以下提供可執行Socket範例裡的Server及Client底層部分程式:

Server端程式:

public class AsServer { #region --公開事件-- public event EventHandler<EventArgsMsg> ServerGetMsgEvent; //Server獲得回訊的事件 public event EventHandler<EventArgsStateChange> ServerStateChangeEvent; //Server狀態改變的事件 #endregion --公開事件-- #region --欄位-- Socket serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//地址型別,流,協議型別 //192.168.43.76 本機ip:127.0.0.1 Socket clientSocket = null; //宣告一個client的socket類別 IPEndPoint ipendpoint = null; //宣告一個通訊端位置 #endregion --欄位-- #region --屬性(傳出、接收)-- public string WriteInMsg { get; set; } //用於傳出訊息 public string MsgReceive { get; set; } //用於接收訊息 #endregion --屬性(傳出、接收)-- #region --建構式-- public AsServer(string ip, int port) //建構式,須帶入ip位置及通訊埠編號 { IPAddress ipaddress = IPAddress.Parse(ip); ipendpoint = new IPEndPoint(ipaddress, port); } #endregion --建構式-- #region --公開方法-- public void StartConnect() //開始連線的方法 { if (ipendpoint != null) { try { serverSocket.Bind(ipendpoint);//繫結完成 StateChange(EConnect.Connected, string.Format("Server Connected")); serverSocket.Listen(0);//處理連結佇列個數 為0則為不限制 ClientAccept(); } catch { StateChange(EConnect.Disconnect, string.Format("Server has already been connect")); } } } public void Stop() //關閉socket的方法 { if (serverSocket != null) serverSocket.Close(); if (clientSocket != null) clientSocket.Close(); StateChange(EConnect.Disconnect, "Disconnected!!"); } #endregion --公開方法-- #region --私有方法-- private void ClientAccept() //此方法用於監聽Client的連線狀態 { if (clientSocket != null) //排除錯誤問題 { Stop(); StateChange(EConnect.Disconnect, string.Format("Client Socket Error")); return; } Task.Factory.StartNew(() => { clientSocket = serverSocket.Accept();//接收一個客戶端連結 StateChange(EConnect.Connected, string.Format("Client Connected")); Task.Factory.StartNew(() => { MessageSender(); }); //建立新執行緒,以傳出訊息 Task.Factory.StartNew(() => { MessageReceiver(); }); //建立新執行緒,以接收訊息 }); } private void MessageSender() //傳出訊息的方法 { while (Configuration.ConnectState.Equals(EConnect.Connected)) { while (Configuration.ConnectState.Equals(EConnect.Connected) && string.IsNullOrEmpty(WriteInMsg)) //當沒有要傳的資料時,在這打轉 { Thread.Sleep(1); } byte[] date = System.Text.Encoding.UTF8.GetBytes(WriteInMsg);//將string加密,轉換成為bytes陣列 clientSocket.Send(date); //傳輸資料 WriteInMsg = string.Empty; } clientSocket.Close(); //關閉socket } private void MessageReceiver() //接收訊息的方法 { while (Configuration.ConnectState.Equals(EConnect.Connected)) { byte[] dateBuffer = new byte[1024]; //宣告接收資訊的空間 int count = clientSocket.Receive(dateBuffer); //存介面接收到資料 MsgReceive = System.Text.Encoding.UTF8.GetString(dateBuffer, 0, count); //解密為string格式 if (ServerGetMsgEvent != null) { EventArgsMsg msgArg = new EventArgsMsg(); msgArg.Message = MsgReceive; ServerGetMsgEvent.Invoke(this, msgArg); //呼叫上層方法執行UI變更 } MsgReceive = string.Empty; } clientSocket.Close(); //關閉socket } private void StateChange(EConnect state, string changeMsg = "") //當狀態改變,可呼叫上層變更UI的方法 { Configuration.ConnectState = state; if (ServerStateChangeEvent != null) { EventArgsStateChange arg = new EventArgsStateChange(); arg.StateMessage = changeMsg; ServerStateChangeEvent.Invoke(this, arg); } } #endregion --私有方法-- }

Client端程式:

public class AsClient { #region --公開事件-- public event EventHandler<EventArgsMsg> ClientGetMsgEvent; //Client獲得回訊的事件 public event EventHandler<EventArgsStateChange> ClientStateChangeEvent; //Client狀態改變的事件 #endregion --公開事件-- #region --欄位-- Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //宣告一個client的socket類別 IPEndPoint ipendpoint = null; //宣告一個通訊端位置 #endregion --欄位-- #region --屬性(傳出、接收)-- public string WriteInMsg { get; set; } //用於傳出訊息 public string MsgReceive { get; set; } //用於接收訊息 #endregion --屬性(傳出、接收)-- #region --建構式-- public AsClient(string ip, int port) //建構式,須帶入ip位置及通訊埠編號 { IPAddress ipaddress = IPAddress.Parse(ip); ipendpoint = new IPEndPoint(ipaddress, port); } #endregion --建構式-- #region --公開方法-- public void StartConnect() //開始連線的方法 { if (ipendpoint != null) { try //連接成功 { clientSocket.Connect(ipendpoint);//繫結完成 StateChange(EConnect.Connected, string.Format("Client Connected")); } catch //連接失敗 { StateChange(EConnect.Disconnect, string.Format("Connect Fail, please make sure that Server has been online")); return; } Task.Factory.StartNew(() => { MessageSender(); }); //建立新執行緒,以傳出訊息 Task.Factory.StartNew(() => { MessageReceiver(); }); //建立新執行緒,以接收訊息 } } public void Stop() //關閉socket的方法 { if (clientSocket != null) clientSocket.Close(); StateChange(EConnect.Disconnect, "Disconnected!!"); } #endregion --公開方法-- #region --私有方法-- private void MessageSender() //傳出訊息的方法 { while (Configuration.ConnectState.Equals(EConnect.Connected)) { while (Configuration.ConnectState.Equals(EConnect.Connected) && string.IsNullOrEmpty(WriteInMsg)) //當沒有要傳的資料時,在這打轉 { Thread.Sleep(1); } byte[] date = System.Text.Encoding.UTF8.GetBytes(WriteInMsg); //將string加密,轉換成為bytes陣列 clientSocket.Send(date); //傳輸資料 WriteInMsg = string.Empty; } clientSocket.Close(); //關閉socket } private void MessageReceiver() //接收訊息的方法 { while (Configuration.ConnectState.Equals(EConnect.Connected)) { byte[] data = new byte[1024]; //宣告接收資訊的空間 int count = clientSocket.Receive(data); //存介面接收到資料 MsgReceive = System.Text.Encoding.UTF8.GetString(data, 0, count); //解密為string格式 if (ClientGetMsgEvent != null) { EventArgsMsg msgArg = new EventArgsMsg(); msgArg.Message = MsgReceive; ClientGetMsgEvent.Invoke(this, msgArg); //呼叫上層方法執行UI變更 } MsgReceive = string.Empty; } clientSocket.Close(); //關閉socket } private void StateChange(EConnect state, string changeMsg = "") //當狀態改變,可呼叫上層變更UI的方法 { Configuration.ConnectState = state; if (ClientStateChangeEvent != null) { EventArgsStateChange arg = new EventArgsStateChange(); arg.StateMessage = changeMsg; ClientStateChangeEvent.Invoke(this, arg); } } #endregion --私有方法-- }

執行結果:

執行結果畫面

參考資料

  1. TCP/IP 協定與 Internet 網路:第八章 TCP Socket 程式介面
  2. Microsoft Document Socket 類別