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页的客户端。

整个项目源码可以到上一篇服务端末尾下载。

posted @ 2020-07-01 15:29  月光下的待宵草  阅读(3833)  评论(4编辑  收藏  举报