命名空間:編程
在網絡環境下,咱們最感興趣的兩個命名空間是System.Net和 System.Net.Sockets。c#
System.Net命名空間一般與較高程的操做有關,例如download或upload,試用HTTP和其餘協議進行Web請求等等數組
System.Net.Sockets命名空間所包含的類一般與較低程的操做有關。若是要直接使用Sockets或者 TCP/IP之類的協議服務器
Socket對象建立:網絡
針對Socket編程,.NET 框架的 Socket 類是 Winsock32 API 提供的套接字服務的託管代碼版本。Socket能夠象流Stream同樣被視爲一個數據通道,這個通道架設在應用程序端(客戶端)和遠程服務器端之間,然後,數據的讀取(接收)和寫入(發送)均針對這個通道來進行。框架
Socket 類支持兩種基本模式:同步和異步。其區別在於:在同步模式中,對執行網絡操做的函數(如 Send 和 Receive)的調用一直等到操做完成後纔將控制返回給調用程序。在異步模式中,這些調用當即返回。 dom
在使用以前,你須要首先建立Socket對象的實例,這能夠經過Socket類的構造方法來實現: 異步
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType); socket
其中,addressFamily 參數指定 Socket 使用的尋址方案,socketType 參數指定 Socket 的類型,protocolType 參數指定 Socket 使用的協議。 ide
下面的示例語句建立一個 Socket,它可用於在基於 TCP/IP 的網絡(如 Internet)上通信。
Socket temp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
若要使用 UDP 而不是 TCP,須要更改協議類型,以下面的示例所示:
Socket temp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
一旦建立 Socket,在客戶端,你將能夠經過Connect方法鏈接到指定的服務器,並經過Send/SendTo
Socket操做流程:
方法向遠程服務器發送數據,然後能夠經過 Receive/ReceiveFrom從服務端接收數據;而在服務器端,你須要使用Bind方法綁定所指定的接口使Socket與一個本地終結點相聯,並經過Listen方法偵聽該接口上的請求,當偵聽到用戶端的鏈接時,調用Accept完成鏈接的操做,建立新的Socket以處理傳入的鏈接請求。使用完 Socket 後,記住使用 Shutdown 方法禁用 Socket,並使用 Close 方法關閉 Socket。
終結點:
在Internet中,TCP/IP 使用套接字(一個網絡地址和一個服務端口號)來惟一標識設備。網絡地址(IP)標識網絡上的特定設備;端口號(Port)標識要鏈接到的該設備上的特定服務。網絡地址和服務端口的組合稱爲終結點,在 .NET 框架中正是由 EndPoint 類表示這個終結點,它提供表示網絡資源或服務的抽象,用以標誌網絡地址等信息。
.Net同時也爲每一個受支持的地址族定義了 EndPoint 的子代;對於 IP 地址族,該類爲 IPEndPoint。IPEndPoint 類包含應用程序鏈接到主機上的服務所需的主機和端口信息。
IP地址獲取方式:
用到IPEndPoint類的時候就不可避免地涉及到計算機IP地址,System.Net命名空間中有兩種類能夠獲得IP地址實例:
IPAddress類: 在同一局域網中採用的獲取方式。 IPAddress 類包含計算機在 IP 網絡上的地址。其Parse方法可將 IP 地址字符串轉換爲 IPAddress 實例。下面的語句建立一個 IPAddress 實例:
IPAddress myIP = IPAddress.Parse("192.168.0.1");
Dns 類: 在互聯網中採用的獲取方式。 向使用 TCP/IP Internet 服務的應用程序提供域名服務。其Resolve 方法查詢 DNS 服務器以將用戶友好的域名(如"host.mydomain.com")映射到數字形式的 Internet 地址(如 192.168.0.1)。
Resolve方法 返回一個 IPHostEnty 實例,該實例包含所請求名稱的地址和別名的列表。大多數狀況下,可使用 AddressList 數組中返回的第一個地址。下面的代碼獲取一個 IPAddress 實例,該實例包含服務器 host.mydomain.com 的 IP 地址。
IPHostEntry ipHostInfo = Dns.Resolve("host.mydomain.com ");
IPAddress ipAddress = ipHostInfo.AddressList[0];
你也可使用GetHostName方法獲得IPHostEntry實例:
IPHosntEntry hostInfo=Dns.GetHostByName("host.mydomain.com ")
在使用以上方法時,你將可能須要處理如下幾種異常:
SocketException異常:訪問Socket時操做系統發生錯誤引起
ArgumentNullException異常:參數爲空引用引起
ObjectDisposedException異常:Socket已經關閉引起
通訊過程示例:
服務器端:
服務器端接收客戶端數據步驟:
public class CM { public static bool ISAddSocekt = true;//如今是否繼續接收新的客戶端鏈接 public static int AddressIndex = 0;//這是當前與服務器端鏈接的第幾個客戶端 //病歷號和客戶端IP關聯表(key:病歷號 value:客戶端的IP地址,不包含port) public static Dictionary<string, string> dic_Patient_IP = new Dictionary<string, string>(); //客戶端IP和客戶端鏈接順序關聯表(key:病歷號 value:客戶端的IP地址,不包含port) public static Dictionary<string, int> dic_IP_Index = new Dictionary<string, int>(); //客戶端集合 key:客戶端IP地址,Value:客戶端實體 public static Dictionary<string, Socket> dic_IP_Socket = new Dictionary<string, Socket>(); private delegate void AcceptDelegete(); private delegate void ReadDelegete(Socket s); public static int ServerPort = 2000;//服務器端端口 public static Socket serverSocket;//服務器端 //用20個線程分別接受一個設備的數據 public static int ClientCount_Max = 20;//客戶端的最大數量 public static void Listen() { AcceptDelegete listen = AcceptConnection; IAsyncResult asy = listen.BeginInvoke(null, null); } private static void AcceptConnection() { try { serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint localEP = new IPEndPoint(IPAddress.Any, ServerPort); //將socket綁定到本地的終結點上 serverSocket.Bind(localEP); //開始監聽 serverSocket.Listen(ClientCount_Max); serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null); } catch (Exception ex) { } } private static void AcceptCallBack(IAsyncResult iar) { try { while (ISAddSocekt) { // 調用EndAccept完成BeginAccept異步調用,返回一個新的Socket處理與客戶的通訊 Socket clientSocket = serverSocket.EndAccept(iar); IPEndPoint clientPoint = clientSocket.RemoteEndPoint as IPEndPoint; if (!dic_IP_Index.ContainsKey(clientPoint.Address.ToString())) { dic_IP_Index.Add(clientPoint.Address.ToString(), AddressIndex); AddressIndex += 1; //去除舊的客戶端實體 dic_IP_Socket.Remove(clientPoint.Address.ToString()); } dic_IP_Socket.Add(clientPoint.Address.ToString(), clientSocket); //開啓新線程讀取數據 CM_Data client = new CM_Data(clientSocket); Thread thClient = new Thread(new ThreadStart(client.ClientServer)); thClient.Start(); //在達到多大的客戶端鏈接限制前,繼續監聽是否有新的客戶端鏈接 if (dic_IP_Index.Count < ClientCount_Max) { serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null); } } } catch (Exception ex) { } } } //在新線程中不一樣的循環接收客戶端發送過來的數據信息 // CM_Data類裏的代碼 public class CM_Data { Encoding encoding = Encoding.GetEncoding("GB2312"); //解碼器(能夠用於漢字) private Socket client;//當前與服務器端通訊的客戶端實體 private byte[] receiveBytes = new byte[1024];//服務器端設置緩衝區 private int recCount; byte[] byte_Data; //傳遞鏈接socket public CM_Data(Socket ClientSocket) { this.client = ClientSocket; } public void ClientServer() { try { while (true) { recCount = client.Receive(receiveBytes, receiveBytes.Length, 0);//從客戶端接收信息,recCount爲有效數據的個數 if (recCount != 0)//當服務器端的緩衝區接收到的信息不爲空時 { byte_Data = new byte[recCount]; for (int i = 0; i < recCount; i++) { byte_Data[i] = receiveBytes[i]; } //獲取數據後的數據處理 DataRead(byte_Data); } else { break; } } } catch (Exception ex) { MessageBox.Show("數據接收時發生錯誤:"+ex.Message); } client.Close(); }
服務器端向客戶端發送數據:
public void WorkCommand(string clientIP,int WorkState) { Socket client = (Socket)CM.dic_IP_Socket[clientIP]; byte[] b1 = new byte[6]; b1[0] = 0x55; b1[1] = 0xaa; b1[2] = 0x04; b1[3] = 0x81; switch (WorkState) { case 1: b1[4] = 0x01; break; case 2: b1[4] = 0x02; break; case 3: b1[4] = 0x03; break; case 4: b1[4] = 0x04; break; } b1[5] = CM_Data.GetParityBit(b1) ; client.BeginSend(b1, 0, b1.Length, SocketFlags.None, new AsyncCallback(SendCallBack), client); } private void SendCallBack(IAsyncResult iar) { Socket workerSocket = (Socket)iar.AsyncState; workerSocket.EndSend(iar); }
客戶端:
項目的客戶端代碼是否下位機完成的(使用C++代碼),下面的客戶端示例,是我在測試程序中使用到的c#版本的代碼示例。
客戶端接收服務器端數據步驟:
private int serverPort = 2000; public byte[] dataBuffer = new byte[10000000]; Socket client; //鏈接服務器 private void ServerConnection() { try { client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ip = IPAddress.Parse("192.168.10.188"); IPEndPoint iep = new IPEndPoint(ip, 2000); client.BeginConnect(iep, new AsyncCallback(Connect), null); } catch { } } private void Connect(IAsyncResult asy) { try { client.EndConnect(asy); client.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallBack), null); } catch { } } //接收byte字節方式 private void RecieveCallBack(IAsyncResult iar) { try { int iRx = client.EndReceive(iar); byte[] byte_Receive = new byte[iRx]; for (int i = 0; i < iRx; i++) { byte_Receive[i] = dataBuffer[i]; listBox1.Items.Add(Convert.ToString(byte_Receive[i], 16)); } string revStr = System.Text.Encoding.UTF7.GetString(byte_Receive, 0, iRx); //繼續監聽下一次的數據接收 client.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallBack), null); } catch { } }
Void SendMessage() { try { byte[] buffer = System.Text.Encoding.Default.GetBytes(textBox1.Text); int snLen = 0; client.BeginSend(buffer, snLen, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), client); } catch { } }