关于WebSocket其实很早就想发了,奈何之前项目中的WebSocket的后端不是我做的,而我又想前后端都发出来和大家讨论讨论~于是挤出点时间研究了一下WebSocket的后端实现(所以才有了这篇文章)。
首先是概念导论,现在大家在百度、谷歌很容易就能搜索到一大堆这样的socket(百度百科)、Socket详解(太多了就不一一列举)
看过这些概念导论以后,我们准备先实现WebSocket的服务端(基础概念都没掌握的童鞋们需要补课了)。
说到.net的WebSocket实现,就不得不说SuperSocket,你可以点击这里了解和下载它。
看过上面链接以后,你应该知道它是有多好用的工具,接下来我们就可以搭建我们的WebSocketServer了:
首先,创建一个窗体应用程序(winForm),当然你也可以创建控制台应用程序。
然后,需要将下载好的这几个DLL引入你的项目中:SuperSocket.Common、SuperSocket.SocketBase、SuperSocket.SocketEngine、SuperWebSocket、log4net
其中SuperSocket相关类库版本为1.6,SuperWebSocket版本为0.9,log4net为1.2(版本错误可能会出一些谁也看不懂的碧油鸡)。
接下来,我们需要声明一个webSocket对象,并且给它绑定好事件,比如这样:
1 2 3 4 5 6 7 | WebSocketServer ws = new WebSocketServer(); ws.NewMessageReceived += Ws_NewMessageReceived; //当有信息传入时 ws.NewSessionConnected += Ws_NewSessionConnected; //当有用户连入时 ws.SessionClosed += Ws_SessionClosed; //当有用户退出时 ws.NewDataReceived += Ws_NewDataReceived; //当有数据传入时 if (ws.Setup(10086)) //绑定端口 ws.Start(); //启动服务 |
注释标注的应该清楚,不过这里我没有对传入数据做操作,各位看官可以忽略不计。
接下来我们应该怎么做呢?答案是重写生成的事件方法。很容易看出我们要先处理用户连入和退出,并且保存和移除用户信息(不然你都不知道你在和谁聊天),比如这样:
1 | Dictionary<WebSocketSession, string > userlist = new Dictionary<WebSocketSession, string >(); //用户列表 |
眼尖的同学们一定发现了一个新类型:WebSocketSession,它是做什么的呢?F12进去以后是这样的:
![](https://i-blog.csdnimg.cn/blog_migrate/6b1212269750ba885004fc47e50fa493.png)
貌似什么都没有?别急,我们再看它的父类:
![](https://i-blog.csdnimg.cn/blog_migrate/2be0f642cc5bbe840e40bc777646e8b1.png)
到这里很多人就明白了,关于连入用户的诸多信息都在这个类内,还提供了Send方法以便于Server端与之通讯。
搞明白WebSocketSession类型能做什么以后,就可以针对不同操作进行响应了
客户端在连入服务端时,需要发送一串信息来告诉服务端“who i am”,这时就需要动用Ws_NewMessageReceived事件的方法。
如:客户端连入时,向服务端发送了“{'user':'001号学生','active':'login'}”,那么我们就可以在服务端将该用户的信息存储至上面定义好的userlist中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | //添加至用户集合的方法 public void AddUser( string UserName, WebSocketSession session) { userList.Add(session, UserName); } //login对应封装dic方法 public Dictionary< string , string > login(Dictionary< string , string > Query, T curUser) { UserConnHandle userConn = () => { return Query[ "userid" ]; }; Dictionary< string , string > Respon = new Dictionary< string , string >(); if (!service.userList.ContainsKey(curUser)) { if (Query[ "user" ] == "" || Query[ "user" ] == null ) { Respon.Add( "user" , Guid.NewGuid().ToString().Replace( "-" , "" )); } else { Respon.Add( "userid" , Query[ "userid" ]); } Respon.Add( "type" , "1" ); //为了前端更容易操作 Respon.Add( "send" , "0" ); //这里0是告诉发给所有用户还是当前用户 service.AddUser(Respon[ "user" ], curUser); //将当前用户添加至用户集合 } return Respon; } //有新消息传入时 private void Ws_NewMessageReceived(WebSocketSession session, string value) { Dictionary< string , string > res = login(value, session, this ) as Dictionary< string , string >; switch (res[ "send" ]) { case "0" : res.Remove( "send" ); Send(res); //全部发送 break ; case "1" : res.Remove( "send" ); string username = res[ "to" ]; WebSocketSession keys = userlist.Where(q => q.Value == username).Select(q => q.Key).First(); SendTo(keys, res); //针对发送 break ; } } |
这里我封装成dic只是为了简单调用,大家不习惯可以封装成别的(比如List<T>),下面是Send和SendTo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //发送给所有用户 public void Send( string msg) { foreach ( var item in userList) { item.Key.Send(msg); } } //发送给单个用户 public void SendTo(WebSocketSession session, string msg) { session.Send(msg); } |
其实公聊就是给所有的连入用户做个遍历广播,而私聊只需要针对某用户广播即可。
至此,基于SuperSocket实现的WebSocketServer就已经被简单实现了。
本文中并没有抽出接口,也没有做IOC(实际项目上我是做了的),因为这样写大家更容易理解,后面的架构优化大家可以自行发挥~
关于web客户端对接,后面我会再出文章的(才不要烂尾!)~~