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

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

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

刚好前段时间项目上用到了mqtt和signalR。现在抽个时间自己在梳理一遍。文章地址https://www.yii666.com/article/764343.html

下面是效果文章来源地址https://www.yii666.com/article/764343.html

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

直接上代码。网址:yii666.com<文章来源地址:https://www.yii666.com/article/764343.html

后端是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 }

前端是vue项目网址:yii666.com

Web端在线实时聊天,基于WebSocket(前后端分离)Web端在线实时聊天,基于WebSocket(前后端分离)
  1 <template>
2 <div class="page">
3 <van-cell-group>
4 <van-field v-model="userid" label="你的昵称" placeholder="请输入" />
5 </van-cell-group>
6 <van-button plain type="info" @click="lianjie" size="mini"
7 >开始聊天</van-button
8 >
9 <van-button plain type="info" @click="btnDisconnect" size="mini"
10 >关闭聊天</van-button
11 >
12
13 <van-cell-group>
14 <van-field v-model="party_userid" label="对方昵称" placeholder="请输入" />
15 </van-cell-group>
16 <van-field
17 v-model="sendstr"
18 center
19 clearable
20 label="消息"
21 placeholder="请输入内容"
22 type="textarea"
23 maxlength="50"
24 show-word-limit
25 >
26 <template #button>
27 <van-button type="primary" @click="btnSend" size="mini"
28 >发送</van-button
29 >
30 </template>
31 </van-field>
32
33 <!-- 消息 -->
34 <van-divider
35 :style="{ color: '#1989fa', borderColor: '#1989fa', padding: '0 16px' }"
36 >
37 消息
38 </van-divider>
39 <van-steps direction="vertical">
40 <van-step v-for="item in news" :key="item.time">
41 <h3>{{ item.text }}</h3>
42 <p>{{ item.time }}</p>
43 </van-step>
44 </van-steps>
45 </div>
46 </template>
47
48 <script>
49 var ws;
50 export default {
51 data() {
52 return {
53 userid: "",
54 party_userid: "",
55 sendstr: "",
56
57 news: [],
58 };
59 },
60 methods: {
61 lianjie() {
62 let url = "ws://47.100.30.65/MobileGadgetsApi/api/socket/connect";
63 ws = new WebSocket(`${url}?userid=${this.userid}`);
64
65 let that = this;
66 ws.onopen = function () {
67 // Toast("提示内容");
68 console.log("Connected!");
69 };
70 ws.onmessage = function (result) {
71 console.log(result);
72 that.news.unshift({
73 text: result.data,
74 time: that.$moment().format("YYYY-MM-DD HH:mm:ss"),
75 });
76 };
77 ws.onerror = function (error) {
78 console.log("error");
79 console.log(error);
80 console.log(error.data);
81 console.log("errorend");
82 };
83 ws.onclose = function () {
84 console.log("Disconnected!");
85 };
86 },
87
88 btnDisconnect() {
89 ws.close();
90 },
91
92 btnSend() {
93 if (ws.readyState == WebSocket.OPEN) {
94 ws.send(`${this.party_userid}|${this.sendstr}`);
95 } else {
96 console.log("Connection is Closed!");
97 // $("messageSpan").text("Connection is Closed!");
98 }
99 },
100 },
101 };
102 </script>
103
104 <style lang="scss" scoped>
105 </style>

版权声明:本文内容来源于网络,版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。文本页已经标记具体来源原文地址,请点击原文查看来源网址,站内文章以及资源内容站长不承诺其正确性,如侵犯了您的权益,请联系站长如有侵权请联系站长,将立刻删除

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信图片_20190322181744_03.jpg

微信扫一扫打赏

请作者喝杯咖啡吧~

支付宝扫一扫领取红包,优惠每天领

二维码1

zhifubaohongbao.png

二维码2

zhifubaohongbao2.png