.net平臺下C#socket通訊(上)

徹底是基礎,新手能夠隨意看看,大牛能夠關閉瀏覽頁了,哈哈。html

 

在開始介紹socket前先補充補充基礎知識,在此基礎上理解網絡通訊纔會瓜熟蒂落,固然有基礎的能夠跳過去了。都是廢話,進入正題。面試

  TCP/IP:Transmission Control Protocol/Internet Protocol,傳輸控制協議/因特網互聯協議,又名網絡通信協議。簡單來講:TCP控制傳輸數據,負責發現傳輸的問題,一旦有問題就發出信號,要求從新傳輸,直到全部數據安全正確地傳輸到目的地,而IP是負責給因特網中的每一臺電腦定義一個地址,以便傳輸。從協議分層模型方面來說:TCP/IP由:網絡接口層(鏈路層)、網絡層、傳輸層、應用層。它和OSI的七層結構以及對於協議族不一樣,下圖簡單表示:數組

 

注:上圖左圖:TCP/IP的四層結構對應OSI七層結構。安全

中間的圖示:TCP/IP協議族在OSI七層中的位置及對應的功能。服務器

上圖右圖:TCP/IP協議模塊關係圖。網絡

 

現階段socket通訊使用TCP、UDP協議,相對應UDP來講,TCP則是比較安全穩定的協議了。本文只涉及到TCP協議來講socket通訊。首先講述TCP/IP的三次握手,在握手基礎上延伸socket通訊的基本過程。多線程

下面介紹對於應屆生畢業面試來講是很是熟悉的,同時也是最臭名昭著的三次握手:異步

1 客戶端發送syn報文到服務器端,並置發送序號爲x。socket

2 服務器端接收到客戶端發送的請求報文,而後向客戶端發送syn報文,而且發送確認序號x+1,並置發送序號爲y。學習

3 客戶端受到服務器發送確認報文後,發送確認信號y+1,並置發送序號爲z。至此客戶端和服務器端創建鏈接。

 

在此基礎上,socket鏈接過程:

服務器監聽:服務器端socket並不定位具體的客戶端socket,而是處於等待監聽狀態,實時監控網絡狀態。

客戶端請求:客戶端clientSocket發送鏈接請求,目標是服務器的serverSocket。爲此,clientSocket必須知道serverSocket的地址和端口號,進行掃描發出鏈接請求。

鏈接確認:當服務器socket監聽到或者是受到客戶端socket的鏈接請求時,服務器就響應客戶端的請求,建議一個新的socket,把服務器socket發送給客戶端,一旦客戶端確認鏈接,則鏈接創建。

注:在鏈接確認階段:服務器socket即便在和一個客戶端socket創建鏈接後,還在處於監聽狀態,仍然能夠接收到其餘客戶端的鏈接請求,這也是一對多產生的緣由。

下圖簡單說明鏈接過程:

 

socket鏈接原理知道了,此處編寫最基本最簡單的socket通訊:

服務器端:

int port = 6000;
            string host = "127.0.0.1";

            IPAddress ip = IPAddress.Parse(host);
            IPEndPoint ipe = new IPEndPoint(ip, port);

            Socket sSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            sSocket.Bind(ipe);
            sSocket.Listen(0);
            Console.WriteLine("監聽已經打開,請等待");

            //receive message
            Socket serverSocket = sSocket.Accept();
            Console.WriteLine("鏈接已經創建");
            string recStr = "";
            byte[] recByte = new byte[4096];
            int bytes = serverSocket.Receive(recByte, recByte.Length, 0);
            recStr += Encoding.ASCII.GetString(recByte, 0, bytes);

            //send message
            Console.WriteLine("服務器端得到信息:{0}", recStr);
            string sendStr = "send to client :hello";
            byte[] sendByte = Encoding.ASCII.GetBytes(sendStr);
            serverSocket.Send(sendByte, sendByte.Length, 0);
            serverSocket.Close();
            sSocket.Close();

客戶端:

int port = 6000;
            string host = "127.0.0.1";//服務器端ip地址

            IPAddress ip = IPAddress.Parse(host);
            IPEndPoint ipe = new IPEndPoint(ip, port);

            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientSocket.Connect(ipe);

            //send message
            string sendStr = "send to server : hello,ni hao";
            byte[] sendBytes = Encoding.ASCII.GetBytes(sendStr);
            clientSocket.Send(sendBytes);

            //receive message
            string recStr = "";
            byte[] recBytes = new byte[4096];
            int bytes = clientSocket.Receive(recBytes, recBytes.Length, 0);
            recStr += Encoding.ASCII.GetString(recBytes, 0, bytes);
            Console.WriteLine(recStr);

            clientSocket.Close();


  上述服務器端和客戶端創建通訊,在互相發送一次信息後通訊便結束,而在你們進行的項目中,這樣的通訊確定知足不了需求。因而接着介紹異步通訊,簡單來講就是服務器端和客戶端能夠進行屢次互發信息的通訊而不用擔憂通道會關閉。在介紹異步通訊時,客戶端和服務器端的鏈接和上面介紹的同步通訊創建鏈接的方式是同樣的,這裏只寫出服務器端和客戶端發送信息的方法和接收信息的方法。(服務器端和客戶端的發送、接收的方法是同樣的)

首先寫出異步鏈接的方法吧:

public void Connect(IPAddress ip, int port)
        {
            this.clientSocket.BeginConnect(ip, port, new AsyncCallback(ConnectCallback), this.clientSocket);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                handler.EndConnect(ar);
            }
            catch (SocketException ex)
            { }
        }

發送信息方法:

public void Send(string data)
        {
            Send(System.Text.Encoding.UTF8.GetBytes(data));
        }

        private void Send(byte[] byteData)
        {
            try
            {
                int length = byteData.Length;
                byte[] head = BitConverter.GetBytes(length);
                byte[] data = new byte[head.Length + byteData.Length];
                Array.Copy(head, data, head.Length);
                Array.Copy(byteData, 0, data, head.Length, byteData.Length);
                this.clientSocket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), this.clientSocket);
            }
            catch (SocketException ex)
            { }
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                handler.EndSend(ar);
            }
            catch (SocketException ex)
            { }
        }

接收信息的方法:

public void ReceiveData()
        {
            clientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                int REnd = clientSocket.EndReceive(ar);
                if (REnd > 0)
                {
                    byte[] data = new byte[REnd];
                    Array.Copy(MsgBuffer, 0, data, 0, REnd);

                    //在這次能夠對data進行按需處理

                    clientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
                }
                else
                {
                    dispose();
                }
            }
            catch (SocketException ex)
            { }
        }

        private void dispose()
        {
            try
            {
                this.clientSocket.Shutdown(SocketShutdown.Both);
                this.clientSocket.Close();
            }
            catch (Exception ex)
            { }
        }

 

  異步問題解決了,再寫一個本身在使用過程當中常常出現的一個問題。接收的數據包處理問題:在網絡通訊中,使用異步進行通訊,那麼客戶端在接收服務器發送來的數據包的處理上會有一些麻煩,好比粘包、斷包,這是一些小問題,此處簡單寫寫本身處理此問題的一個方法。
粘包處理:

public Hashtable DataTable = new Hashtable();//由於要接收到多個服務器(ip)發送的數據,此處按照ip地址分開存儲發送數據

        public void DataArrial(byte[] Data , string ip)
        {
            try
            {
                if (Data.Length < 12)//按照需求進行判斷
                {
                    lock (DataTable)
                    {
                        if (DataTable.Contains(ip))
                        {
                            DataTable[ip] = Data;
                            return;
                        }
                    }
                }

                if (Data[0] != 0x1F || Data[1] != 0xF1)//標誌位(按照需求編寫)
                {
                    if (DataTable.Contains(ip))
                    {
                        if (DataTable != null)
                        {
                            byte[] oldData = (byte[])DataTable[ip];//取出粘包數據
                            if (oldData[0] != 0x1F || oldData[1] != 0xF1)
                            {
                                return;
                            }
                            byte[] newData = new byte[Data.Length + oldData.Length];
                            Array.Copy(oldData, 0, newData, 0, oldData.Length);
                            Array.Copy(Data, 0, newData, oldData.Length, Data.Length);//組成新數據數組,先到的數據排在前面,後到的數據放在後面

                            lock (DataTable)
                            {
                                DataTable[ip] = null;
                            }
                            DataArrial(newData, ip);
                            return;
                        }
                    }
                    return;
                }

                int revDataLength = Data[2];//打算髮送數據的長度
                int revCount = Data.Length;//接收的數據長度
                if (revCount > revDataLength)//若是接收的數據長度大於發送的數據長度,說明存在多幀數據,繼續處理
                {
                    byte[] otherData = new byte[revCount - revDataLength];
                    Data.CopyTo(otherData, revCount - 1);
                    Array.Copy(Data, revDataLength, otherData, 0, otherData.Length);
                    Data = (byte[])Redim(Data, revDataLength);
                    DataArrial(otherData, ip);
                }
                if (revCount < revDataLength) //接收到的數據小於要發送的長度
                {
                    if (DataTable.Contains(ip))
                    {
                        DataTable[ip] = Data;//更新當前粘包數據
                        return;
                    }
                }

                //此處能夠按需進行數據處理
            }
            catch (Exception ex)
            { }
        }

        private Array Redim(Array origArray, Int32 desizedSize)
        {
            //確認每一個元素的類型
            Type t = origArray.GetType().GetElementType();
            //建立一個含有指望元素個數的新數組
            //新數組的類型必須匹配數組的類型
            Array newArray = Array.CreateInstance(t, desizedSize);
            //將原數組中的元素拷貝到新數組中。
            Array.Copy(origArray, 0, newArray, 0, Math.Min(origArray.Length, desizedSize));
            //返回新數組
            return newArray;
        }


socket最基本的內容終於寫完了,結合上面的信息進行簡單的應用應該是沒有問題,但是若是牽涉到比較服務的通訊問題,其解決的方法就須要委託、多線程、接口方面的知識了,這方面最近正在學習,最近有一個感悟:委託是.net下C#中最基本最重要的部分了吧,應該必須學會。

吐槽:一個委託不可怕,可怕的是多個委託,而且委託中套用委託纔是最可怕的。

哎,慢慢學習吧。也許下文在一個星期後,或者一個月後會寫出來吧,也許更久吧

 

.net平臺下C#socket通訊(中)在一個小項目的進行過程當中被書寫出來了,歡迎指出其中不足之處,謝謝。

相關文章
相關標籤/搜索