標籤:winform style blog http io color ar os 使用
上篇.net平台下C#socket通訊(上)介紹了socket通訊的基本原理及最基本的通訊方式。本文在此基礎上就socket通訊時經常遇到的問題做一個簡單總結,都是項目中的一些小問題,拿來此處便於下次使用,同時對在使用socket時出現些許問題的同仁們多一個粗淺建議。不足之處請提出,謝謝。
本文主要講述:
1、正常通訊中握手建立
2、一對多的通訊
3、發送接收資料格式轉換
4、資源釋放
1、握手建立正常的通訊通道
項目需要通訊的雙方(假設是一個上位機、一個下位機)之間需要建立一個穩定的通道,以便進行通訊。本項目中具體操作是:上位機作為伺服器,下位機作為用戶端,同時制定通訊協定。上位機首先開啟監聽等待建立通道,下位機主動串連上位機後發送串連成功的資訊到上位機,上位機根據通訊協定發送資料到下位機,此時通道已經建立。但為了保險起見(同時遵循三向交握),用戶端再次發送資料到上位機告知通道建立完畢。
2、一對多通訊
項目需求是一個上位機多個下位機,這就確定了上位機做為伺服器端,下位機作為用戶端主動串連伺服器。一對一通訊時只有一個socket通道,因此無論是上位機還是下位機在發送和接收資料的時候都不會存在資料亂髮亂收的情況。一對多意味著上位機和下位機會建立起多個通道,因此在發送資料時需要記錄哪一個下位機處於哪個socket通道中,以便進行邏輯處理。本文處理一對多通訊的過程是:
1)首先建立一個對話類Session:
public class Session { public Socket ClientSocket { get; set; }//用戶端的socket public string IP;//用戶端的ip public Session(Socket clientSocket) { this.ClientSocket = clientSocket; this.IP = GetIPString(); } public string GetIPString() { string result = ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString(); return result; } }
2)在服務端socket監聽時:
IPEndPoint loaclEndPoint = new IPEndPoint(IPAddress.Any, Port); SocketLister = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); SocketLister.Bind(loaclEndPoint); try { SocketLister.Listen(MaxConnection); while (IsRunning) { ClientSocket = SocketLister.Accept(); //儲存socket Session newSession = new Session(ClientSocket); lock (sessionLock) { sessionTable.Add(newSession.IP, newSession); } SocketConnection socketConnection = new SocketConnection(ClientSocket); socketConnection.ReceiveDatagram();//接收資料 } } catch (SocketException ex) { }
//聲明
public Hashtable sessionTable = new Hashtable ();//包括用戶端工作階段
private object sessionLock = new object();
為了便於理解,把整個服務端socket的建立都寫在上面。
3)發送資料到不同的用戶端
Hashtable ht = serverSocket.sessionTable; foreach (Session session in ht.Values) { if (session.IP == "127.0.0.1")//example { SocketConnection socketConnection = new SocketConnection(session.ClientSocket); string str = "C300010002D2"; byte[] sendBytes = StrToHexByte(str); socketConnection.Send(sendBytes); } }
3、處理需要發送和接收到的資料
項目需要是上位機擷取資料進行邏輯處理,然後通過tcp/ip協議發送給下位機,下位機在接收到資料的同時發送確認資訊到上位機。項目過程中,上位機在開發過程中需要調試其發送資料、接收資料是否成功,此處藉助於USR- TCP232小工具。但是涉及到一個問題,下位機發送和接收都是byte位元組數組,那麼開發的上位機應該如何發送和接收資料?在.net平台下C#socket通訊(上),有伺服器端的發送和接收函數,發送資料時將要發送的字串轉換為byte數組,接收時再將位元組數群組轉換為16進位字串。如下:
//位元組數群組轉換為16進位字串 public string ByteToHexStr(byte[] bytes) { string str = ""; if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { str += bytes[i].ToString("X2"); } } return str; } //字串轉換為16進位byte數組 public byte[] StrToHexByte(string data) { data = data.Replace(" ", ""); if ((data.Length % 2) != 0) { data += " "; } byte[] bytes = new byte[data.Length / 2]; for (int i = 0; i < bytes.Length; i++) { bytes[i] = Convert .ToByte (data.Substring (i * 2,2),16); } return bytes; }
4、資源釋放
開發項目使用平台是.net,工具vs2010,語言是C#,因為.net有記憶體回收機制,因此在實際開發中產生的託管資源都是系統自動釋放完成。在做本項目時採用winform進行開發的,在此過程中發現一個問題:在關閉Form表單是啟動並執行系統並沒有完全關閉。尋找原因,應該是有資源沒有被釋放。而socket通訊端產生的資源恰好是非託管資源,此現象表明系統中有socket資源沒有被完全釋放掉。因此寫了一個資源釋放函數:
public void Dispose() { try { this.ClientSocket.Shutdown(SocketShutdown.Both); this.ClientSocket.Dispose(); this.ClientSocket.Close(); this.ClientSocket = null; } catch { } }
上述函數的功能就是釋放socket所產生的資源,調用後發現還是存在此問題,幾經調試發現雖然把產生socket通道的監聽用戶端資源釋放完畢,伺服器端的serversocket並沒有被釋放,於是有了下一個函數:
public void CloseSocket() { if (serverSocket != null) { serverSocket.SocketLister.Dispose(); serverSocket.SocketLister = null; serverSocket.Dispose();//調用的上一個函數 serverSocket = null; } }
在上述函數完成後,通訊端socket所產生的資源確實被釋放完畢,系統在form關閉後能真正關閉。到此資源好像已經被釋放掉,但緊接著新的問題產生了:
在什麼時候什麼地方調用釋放資源的函數?
個人簡單看法:
1)系統中止時調用
2)socket通道中斷時調用
.net平台下C#socket通訊(中)