標籤:多個 call rda 網路通訊 zed 面試 poi 儲存 產生
完全是基礎,新手可以隨意看看,大牛可以關閉瀏覽頁了,哈哈。
在開始介紹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。
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通訊(上)