1 功能設計
連接服務端地址;
從服務端自動獲取a,b的值;
通過向服務端發送數據,實現所有客戶端同步更新;
將使用中一些信息顯示出來。
2 界面設計
3 代碼實現
3.1 命名空間與引用
using System.Threading;//CancellationToken using System.Net.WebSockets; using Newtonsoft.Json;
3.2 數據格式
先還是約定好數據格式,通過json形式傳輸。

/// <summary> /// //以dictionary將數據的鍵值對匹配,然后進行json序列化,避免定義類的麻煩。 /// </summary> /// <param name="valueA"></param> /// <param name="valueB"></param> /// <returns></returns> public static string SerializeJson(string valueA, string valueB) { if (valueA.Length == 0) { valueA = "-"; } if (valueB.Length == 0) { valueB = "-"; } //以dictionary將數據的鍵值對匹配,然后進行json序列化,避免定義類的麻煩。參考:https://www.cnblogs.com/kevinWu7/p/10163455.html Dictionary<string, string> dic = new Dictionary<string, string>(){ { "a",valueA }, { "b",valueB } }; string Jsondata = JsonConvert.SerializeObject(dic); return Jsondata; }
//用於json反序列化獲取實體 public class TestValue { public string a { get; set; } public string b { get; set; } }
3.3 連接服務端
先實例化一個ClientWebSocket對象,由於之后別的關閉連接事件之類的要用到,所以設為靜態。
static ClientWebSocket client = new ClientWebSocket();//實例化客戶端對象
然后開啟連接

//開啟連接 private void btnStartConnect_Click(object sender, EventArgs e) { if (txtServerAddress.Enabled == true) { MessageBox.Show("請先確認地址"); return; } string ServerAddress = txtServerAddress.Text; //如果已經連上了服務端,想要再次進行連接,需要進行判斷,關閉當前連接后才能進行 if (client.State == WebSocketState.Open) { MessageBox.Show("當前client對象連接狀態為open,若要重新連接,請先關閉當前連接"); return; } try { client = new ClientWebSocket();//這一句不要進行狀態判斷,因為除了Open和Closed,還有Abort等好幾種狀態。干脆每次連接重新初始化。 client.ConnectAsync(new Uri(ServerAddress), CancellationToken.None).Wait(); txtInfo.AppendText("開啟了連接" + DateTime.Now.ToString() + "\n"); } catch (Exception ex) { txtInfo.AppendText(ex.ToString()+ DateTime.Now.ToString() + "\n"); MessageBox.Show("連接出現問題,請檢查網絡是否通暢,地址是否正確,服務端是否開啟"); return; } finally { lblState.Text = client.State.ToString(); } StartReceiving(client); }
由於client之前可能開關失敗過或者什么的,會導致對象被dispose,直接連接會導致報“對象已經被釋放”的錯,因此此處每次進行異步連接的時候,都將對象初始化一下。
之前這個問題困擾了我,此處感謝CSDN論壇熱心大佬 wanghui0380 的幫助。
3.4 接收數據
先判斷連接狀態正常,之后保持循環異步讀取,通過client.ReceiveAsync()進行接收,獲取到數據之后,字節數組轉為字符串,由於服務端以json字符串發來,需要進行反序列化,使用Newtonsoft.Json這個工具的方法,最終獲取到a,b的值,將其顯示到文本框。

/// <summary> /// 異步接收服務端數據,獲取json數據后反序列化,然后顯示到文本框控件中 /// </summary> /// <param name="client"></param> private async void StartReceiving(ClientWebSocket client) { if (client.State != WebSocketState.Open)//正常來說進入到此方法的狀態都為Open { lblState.Text = client.State.ToString(); MessageBox.Show("StartReceiving方法:連接狀態異常,請嘗試重新連接"); return; } try//有可能中途連接斷開 { while (true) { var array = new byte[2048]; if (!((client.State == WebSocketState.Open) || (client.State == WebSocketState.CloseSent))) { //接收消息的有效狀態是Open和CloseSent,如果不是這兩種狀態,則退出。 //主動退出也會影響異步線程的接收,因此先進行判斷 lblState.Text="Closed"; txtInfo.AppendText("StartReceiving方法:連接狀態異常,退出循環接收,請檢查" + DateTime.Now.ToString() + "\n"); return; } var result = await client.ReceiveAsync(new ArraySegment<byte>(array), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Text) { //獲取字節數組並轉為字符串,此字符串應為json類型,需要反序列化 string jsonmsg = Encoding.UTF8.GetString(array, 0, result.Count); try//將反序列化內容放入try中,避免無法匹配、內容為空等可能報錯的地方 { //將轉換后的字符串內容進行json反序列化。參考:https://www.cnblogs.com/yinmu/p/12160343.html TestValue tv = JsonConvert.DeserializeObject<TestValue>(jsonmsg); //將收到的a,b的值顯示到文本框 if (tv != null) { string valueA = string.Empty, valueB = string.Empty; if (tv.a != null && tv.a.Length > 0) { valueA = tv.a; } if (tv.a != null && tv.b.Length > 0) { valueB = tv.b; } txtValueA.Text = valueA; txtValueB.Text = valueB; } } catch (Exception ex) { //如果json反序列化出了問題 txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n");//將錯誤類型顯示出來 txtInfo.AppendText(jsonmsg+"\n");//將收到的原始字符串顯示出來 } } } } catch (Exception ex)//看看什么類型的錯誤 { lblState.Text = client.State.ToString(); //MessageBox.Show(ex.ToString());//暫且注釋,彈出消息框影響觀感 if (ex.GetType().ToString() == "System.Net.WebSockets.WebSocketException" && client.State != WebSocketState.Open) { //客戶端關閉時會拋出此錯誤 txtInfo.AppendText("連接被關閉,請檢查網絡或服務器"+ DateTime.Now.ToString() + "\n"); } else { txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n"); } } //finally //{ // if (client != null) // { // client.Dispose(); // } //} }
3.5 發送數據
這里很簡單,將a,b的值包裝成json字符串。判斷連接狀態后,用client.SendAsync()方法即可。

//向服務端發送數據 private void btnSendMsg_Click(object sender, EventArgs e) { if (txtServerAddress.Enabled == true) { MessageBox.Show("請先確認地址"); return; } string valueA = string.Empty, valueB = string.Empty; valueA = txtValueA.Text; valueB = txtValueB.Text; string jsondata = SerializeJson(valueA, valueB); var array = new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsondata)); //此處需要捕捉異常,連接是否通暢? try { if (client.State == WebSocketState.Open)//連通狀態才允許發送 { client.SendAsync(array, WebSocketMessageType.Text, true, CancellationToken.None); } else { MessageBox.Show("連接狀態異常,請嘗試重新連接"); } } catch (Exception ex) { txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n"); return; } finally { lblState.Text = client.State.ToString(); } }
winform客戶端部分就到這里,下一篇是HTML頁的客戶端。
整個項目源碼可以到上一篇服務端末尾下載。