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
  • 相关阅读:
    linux权限补充:rwt rwT rws rwS 特殊权限
    关于Linux操作系统下文件特殊权限的解释
    Java学习笔记——Java程序运行超时后退出或进行其他操作的实现
    Java实现 蓝桥杯 算法提高 判断名次
    Java实现 蓝桥杯 算法提高 判断名次
    Java实现 蓝桥杯 算法提高 日期计算
    Java实现 蓝桥杯 算法提高 日期计算
    Java实现 蓝桥杯 算法提高 概率计算
    Java实现 蓝桥杯 算法提高 概率计算
    Java实现 蓝桥杯 算法提高 复数四则运算
  • 原文地址:https://www.cnblogs.com/heyiping/p/14949234.html
Copyright © 2011-2022 走看看