c#實現Socket網絡編程

命名空間:編程

  在網絡環境下,咱們最感興趣的兩個命名空間是System.Net和 System.Net.Socketsc#

  System.Net命名空間一般與較高程的操做有關,例如downloadupload,試用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 地址族,該類爲 IPEndPointIPEndPoint 類包含應用程序鏈接到主機上的服務所需的主機和端口信息

 

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已經關閉引起

 

通訊過程示例:

 

服務器端:

 

服務器端接收客戶端數據步驟:

 

  1. 建立一個服務器端的實體
  2. 建立服務器端對客戶端的監聽
  3. EndAccept方法返回客戶端實體
  4. 新建線程負責接收客戶端的數據

 

 

 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();
         }
服務器端接收客戶端數據

 

 

服務器端向客戶端發送數據:

  1. 獲取當前與服務器端通訊的客戶端(或者要與之通訊的目標客戶端)
  2. BeginSend 將數據發送,要發送的數據使用的是byte數組的形式。
  3. 示例中向客戶端發送的數據也是異步發送的
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#版本的代碼示例。

客戶端接收服務器端數據步驟:

  1. 建立客戶端實體,IPEndPoint 裏指定的是服務器端的IP地址和服務器端的端口號。
  2. 客戶端等待服務器端的鏈接。
  3. 客戶端循環等待服務器端的發送數據
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 { }
}
客戶端向服務器端發送數據
相關文章
相關標籤/搜索