C# winform websocket學習筆記(三)winform客戶端


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;
        }
View Code
//用於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);            
        }
View Code

由於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();
            //    }
            //}
            
        }
View Code

 

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();
            }
                        
        }
View Code

 

winform客戶端部分就到這里,下一篇是HTML頁的客戶端。

整個項目源碼可以到上一篇服務端末尾下載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM