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形式传输。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <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();//实例化客户端对象
然后开启连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//开启连接 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的值,将其显示到文本框。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <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()方法即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//向服务端发送数据 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页的客户端。
整个项目源码可以到上一篇服务端末尾下载。