Web端在线实时聊天,基于WebSocket(前后端分离)

这是一个简易的Demo,已经实现了基础的功能

之前一直想实现一个实时聊天的系统,一直没有去实践他。有一天吃饭的时候扫码点菜,几个人点菜能够实时更新,当时就在想,这应该是同一种技术。

刚好前段时间项目上用到了mqtt和signalR。现在抽个时间自己在梳理一遍。

下面是效果

Web端在线实时聊天,基于WebSocket(前后端分离)

 

 

直接上代码。

后端是WebApi项目,.NET Framework 4.5

Web端在线实时聊天,基于WebSocket(前后端分离)

Web端在线实时聊天,基于WebSocket(前后端分离)

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Net; 5 using System.Net.Http; 6 using System.Web.Http; 7 using System.Net.WebSockets; 8 using System.Web; 9 using System.Web.WebSockets; 10 using System.Text; 11 using System.Threading; 12 using System.Threading.Tasks; 13 14 namespace StudentSys.WebApi.Controllers 15 { 16 /// <summary> 17 /// 离线消息 18 /// </summary> 19 public class MessageInfo 20 { 21 public MessageInfo(DateTime _MsgTime, ArraySegment<byte> _MsgContent) 22 { 23 MsgTime = _MsgTime; 24 MsgContent = _MsgContent; 25 } 26 public DateTime MsgTime { get; set; } 27 public ArraySegment<byte> MsgContent { get; set; } 28 } 29 30 [RoutePrefix("api/socket")] 31 public class WebSocketController : ApiController 32 { 33 private static Dictionary<string, WebSocket> CONNECT_POOL = new Dictionary<string, WebSocket>();//用户连接池 34 private static Dictionary<string, List<MessageInfo>> MESSAGE_POOL = new Dictionary<string, List<MessageInfo>>();//离线消息池 35 36 [HttpGet] 37 [Route("connect")] 38 public HttpResponseMessage Connect() 39 { 40 //在服务端接受web socket请求,传入的函数作为web socket的处理函数,待web socket建立后该函数会被调用, 41 //在该函数中可以对web socket进行消息收发 42 HttpContext.Current.AcceptWebSocketRequest(ProcessChat); 43 //构造同意切换至web socket的response 44 return Request.CreateResponse(HttpStatusCode.SwitchingProtocols); 45 } 46 47 private async Task ProcessChat(AspNetWebSocketContext context) 48 { 49 WebSocket socket = context.WebSocket; 50 string user = context.QueryString["userid"].ToString(); 51 52 try 53 { 54 #region 用户添加连接池 55 //第一次open时,添加到连接池中 56 if (!CONNECT_POOL.ContainsKey(user)) 57 CONNECT_POOL.Add(user, socket);//不存在,添加 58 else 59 if (socket != CONNECT_POOL[user])//当前对象不一致,更新 60 CONNECT_POOL[user] = socket; 61 #endregion 62 63 #region 离线消息处理 64 if (MESSAGE_POOL.ContainsKey(user)) 65 { 66 List<MessageInfo> msgs = MESSAGE_POOL[user]; 67 foreach (MessageInfo item in msgs) 68 { 69 await socket.SendAsync(item.MsgContent, WebSocketMessageType.Text, true, CancellationToken.None); 70 } 71 MESSAGE_POOL.Remove(user);//移除离线消息 72 } 73 #endregion 74 75 string descUser = string.Empty;//目的用户 76 while (true) 77 { 78 if (socket.State == WebSocketState.Open) 79 { 80 ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]); 81 WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None); 82 83 #region 消息处理(字符截取、消息转发) 84 try 85 { 86 #region 关闭Socket处理,删除连接池 87 if (socket.State != WebSocketState.Open)//连接关闭 88 { 89 if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//删除连接池 90 break; 91 } 92 #endregion 93 94 string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);//发送过来的消息 95 string[] msgList = userMsg.Split('|'); 96 if (msgList.Length == 2) 97 { 98 if (msgList[0].Trim().Length > 0) 99 descUser = msgList[0].Trim();//记录消息目的用户 100 buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msgList[1])); 101 } 102 else 103 buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMsg)); 104 105 if (CONNECT_POOL.ContainsKey(descUser))//判断客户端是否在线 106 { 107 WebSocket destSocket = CONNECT_POOL[descUser];//目的客户端 108 if (destSocket != null && destSocket.State == WebSocketState.Open) 109 await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); 110 } 111 else 112 { 113 Task.Run(() => 114 { 115 if (!MESSAGE_POOL.ContainsKey(descUser))//将用户添加至离线消息池中 116 MESSAGE_POOL.Add(descUser, new List<MessageInfo>()); 117 MESSAGE_POOL[descUser].Add(new MessageInfo(DateTime.Now, buffer));//添加离线消息 118 }); 119 } 120 } 121 catch (Exception exs) 122 { 123 //消息转发异常处理,本次消息忽略 继续监听接下来的消息 124 } 125 #endregion 126 } 127 else 128 { 129 break; 130 } 131 }//while end 132 } 133 catch (Exception ex) 134 { 135 //整体异常处理 136 if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user); 137 } 138 } 139 140 public bool IsReusable 141 { 142 get 143 { 144 return false; 145 } 146 } 147 } 148 }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxjwj.html