zoukankan      html  css  js  c++  java
  • Web端在线实时聊天,基于WebSocket(前后端分离)

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

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

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

    下面是效果

    直接上代码。

    后端是WebApi项目,.NET Framework 4.5

      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 }
    View Code

    前端是vue项目

      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>
    View Code
  • 相关阅读:
    [HNOI2008]神奇的国度(最大势算法)
    学习笔记——prufer序列
    [NOIP模拟题]chess(最短路计数)
    2019暑假图论总结
    [NOIP2016]天天爱跑步(桶)
    [NOIP2012]疫情控制(贪心)
    [NOIP2016]蚯蚓(单调性乱搞)
    暑假考试题4:星际旅行(欧拉路)
    暑假考试题3:jigsaw 黄金拼图(乱搞)
    暑假考试题3:baritone 上低音号与星星(链表+矩形统计)
  • 原文地址:https://www.cnblogs.com/heyiping/p/14949234.html
Copyright © 2011-2022 走看看