Home  >  Article  >  Backend Development  >  Asp.net uses SignalR to implement chat room functions

Asp.net uses SignalR to implement chat room functions

高洛峰
高洛峰Original
2016-12-24 14:20:381870browse

1. Introduction
In the previous article "Asp.net uses SignalR to implement cool end-to-end chat function", I introduced to you how to implement the end-to-end chat function. In this article, I will Like how you use SignalR to implement functions such as group chat.

2. Implementation Ideas
To implement the group chat function, first we need to create a room, and then each online user can join this room for group chat. We can set a unique name for the room as an identifier. Is there such an existing method in the SignalR class library? The answer is yes.

// IGroupManager接口提供如下方法
// 作用:将连接ID加入某个组
// Context.ConnectionId 连接ID,每个页面连接集线器即会产生唯一ID
// roomName分组的名称
Groups.Add(Context.ConnectionId, roomName);
 
// 作用:将连接ID从某个分组移除
Groups.Remove(Context.ConnectionId, roomName);
 
// IHubConnectionContext接口提供了如下方法
// 调用客户端方法向房间内所有用户群发消息
// Room:分组名称
// new string[0]:过滤(不发送)的连接ID数组
 Clients.Group(Room, new string[0]).clientMethod


  The above code is the core method of implementing group chat. To put it bluntly, the Groups object is just a list object maintained by the SignalR class library. In fact, we can maintain a Dictionary> object by ourselves. When creating a room, we add the room name and enter the room. The ConnectionId of the client is added to this dictionary, and then when clicking to send a message in the chat room, we find all the ConnectionIds that join the group chat based on the room name, and then call the Clients.Clients(IList connectionIds) method to send the message Broadcast to every client. The above is the principle of implementing a chat room.

3. Use SignalR to implement the chat room function
After clarifying the implementation ideas, let’s take a look at the specific implementation code. At the same time, you can also compare the code to compare the previous implementation ideas.
First, let’s look at the implementation code of the entity class involved in the chat room function:

/// <summary>
 /// 用户类
 /// </summary>
 public class User
 {
  /// <summary>
  /// 用户Id
  /// </summary>
  public string UserId { get; set; }
 
  /// <summary>
  /// 用户的连接集合
  /// </summary>
  public List<Connection> Connections { get; set; }
 
  /// <summary>
  /// 用户房间集合,一个用户可以加入多个房间
  /// </summary>
  public List<ChatRoom> Rooms { get; set; }
 
  public User()
  {
   Connections = new List<Connection>();
   Rooms = new List<ChatRoom>();
  }
 }
 
 public class Connection
 {
  //连接ID
  public string ConnectionId { get; set; }
 
  //用户代理
  public string UserAgent { get; set; }
  //是否连接
  public bool Connected { get; set; }
 }
 
  /// <summary>
 /// 房间类
 /// </summary>
 public class ChatRoom
 {
  // 房间名称
  public string RoomName { get; set; }
 
  // 用户集合
  public List<User> Users { get; set; }
 
  public ChatRoom()
  {
   Users = new List<User>();
  }
 }
 
 /// <summary>
 /// 上下文类,用来模拟EF中的DbContext
 /// </summary>
 public class ChatContext
 {
  public List<User> Users { get; set; }
 
  public List<Connection> Connections { get; set; }
 
  public List<ChatRoom> Rooms { get; set; }
 
  public ChatContext()
  {
   Users = new List<User>();
   Connections = new List<Connection>();
   Rooms = new List<ChatRoom>();
  }
 }



2. Next, let’s see the implementation of the hub:

[HubName("chatRoomHub")]
 public class GroupsHub : Hub
 {
  public static ChatContext DbContext = new ChatContext();
 
  #region IHub Members
  // 重写Hub连接事件
  public override Task OnConnected()
  {
   // 查询用户
   var user = DbContext.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId);
 
   if (user == null)
   {
    user = new User
    {
     UserId = Context.ConnectionId
    };
 
    DbContext.Users.Add(user);
   }
 
   // 发送房间列表
   var items = DbContext.Rooms.Select(p => new {p.RoomName});
   Clients.Client(this.Context.ConnectionId).getRoomList(JsonHelper.ToJsonString(items.ToList()));
   return base.OnConnected();
  }
 
  // 重写Hub连接断开的事件
  public override Task OnDisconnected(bool stopCalled)
  {
   // 查询用户
   var user = DbContext.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId);
 
   if (user != null)
   {
    // 删除用户
    DbContext.Users.Remove(user);
 
    // 从房间中移除用户
    foreach (var item in user.Rooms)
    {
     RemoveUserFromRoom(item.RoomName);
    }
   }
   return base.OnDisconnected(stopCalled);
  }
 
  #endregion
 
  #region Public Methods
 
  // 为所有用户更新房间列表
  public void UpdateRoomList()
  {
   var itme = DbContext.Rooms.Select(p => new {p.RoomName});
   var jsondata = JsonHelper.ToJsonString(itme.ToList());
   Clients.All.getRoomlist(jsondata);
  }
 
  /// <summary>
  /// 加入聊天室
  /// </summary>
  public void JoinRoom(string roomName)
  {
   // 查询聊天室
   var room = DbContext.Rooms.Find(p => p.RoomName == roomName);
 
   // 存在则加入
   if (room == null) return;
 
   // 查找房间中是否存在此用户
   var isExistUser = room.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId);
 
   // 不存在则加入
   if (isExistUser == null)
   {
    var user = DbContext.Users.Find(u => u.UserId == Context.ConnectionId);
    user.Rooms.Add(room);
    room.Users.Add(user);
     
    // 将客户端的连接ID加入到组里面
    Groups.Add(Context.ConnectionId, roomName);
 
    //调用此连接用户的本地JS(显示房间)
    Clients.Client(Context.ConnectionId).joinRoom(roomName);
   }
   else
   {
    Clients.Client(Context.ConnectionId).showMessage("请勿重复加入房间!");
   }
  }
 
  /// <summary>
  /// 创建聊天室
  /// </summary>
  /// <param name="roomName"></param>
  public void CreateRoom(string roomName)
  {
   var room = DbContext.Rooms.Find(a => a.RoomName == roomName);
   if (room == null)
   {
    var cr = new ChatRoom
    {
     RoomName = roomName
    };
 
    //将房间加入列表
    DbContext.Rooms.Add(cr);
 
    // 本人加入聊天室
    JoinRoom(roomName);
    UpdateRoomList();
   }
   else
   {
    Clients.Client(Context.ConnectionId).showMessage("房间名重复!");
   }
  }
 
  public void RemoveUserFromRoom(string roomName)
  {
   //查找房间是否存在
   var room = DbContext.Rooms.Find(a => a.RoomName == roomName);
 
   //存在则进入删除
   if (room == null)
   {
    Clients.Client(Context.ConnectionId).showMessage("房间名不存在!");
    return;
   }
 
   // 查找要删除的用户
   var user = room.Users.FirstOrDefault(a => a.UserId == Context.ConnectionId);
   // 移除此用户
   room.Users.Remove(user);
   //如果房间人数为0,则删除房间
   if (room.Users.Count <= 0)
   {
    DbContext.Rooms.Remove(room);
   }
 
   Groups.Remove(Context.ConnectionId, roomName);
 
   //提示客户端
   Clients.Client(Context.ConnectionId).removeRoom("退出成功!");
  }
 
  /// <summary>
  /// 给房间内所有的用户发送消息
  /// </summary>
  /// <param name="room">房间名</param>
  /// <param name="message">信息</param>
  public void SendMessage(string room, string message)
  {
   // 调用房间内所有客户端的sendMessage方法
   // 因为在加入房间的时候,已经将客户端的ConnectionId添加到Groups对象中了,所有可以根据房间名找到房间内的所有连接Id
   // 其实我们也可以自己实现Group方法,我们只需要用List记录所有加入房间的ConnectionId
   // 然后调用Clients.Clients(connectionIdList),参数为我们记录的连接Id数组。
   Clients.Group(room, new string[0]).sendMessage(room, message + " " + DateTime.Now);
  }
  #endregion
}



3. Above The code implementation of the SignalR server has been completed. Next, let us take a look at the implementation of the client view:

@{
 Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
 <meta name="viewport" content="width=device-width" />
 <title>Index</title>
 <script src="~/Scripts/jquery-2.2.2.min.js"></script>
 <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
 <script src="~/Scripts/layer/layer.min.js"></script>
 <!--这里要注意,这是虚拟目录,也就是你在OWIN Startup中注册的地址-->
 <script src="/signalr/hubs"></script>
 
 <script type="text/javascript">
  var chat;
  var roomcount = 0;
   
  $(function() {
   chat = $.connection.chatRoomHub;
   chat.client.showMessage = function(message) {
    alert(message);
   };
   chat.client.sendMessage = function(roomname, message) {
    $("#" + roomname).find("ul").each(function() {
     $(this).append(&#39;<li>&#39; + message + &#39;</li>&#39;);
    });
   };
   chat.client.removeRoom = function(data) {
    alert(data);
   };
   chat.client.joinRoom = function (roomname) {
    var html = &#39;<p style="float:left; margin-left:360px; border:double; height:528px;width:493px" id="&#39; + roomname + &#39;" roomname="&#39; + roomname + &#39;"><button onclick="RemoveRoom(this)">退出</button>\
         &#39; + roomname + &#39;房间\
            聊天记录如下:<ul>\
            </ul>\
         <textarea class="ChatCore_write" id="ChatCore_write" style="width:400px"></textarea> <button onclick="SendMessage(this)">发送</button>\
         </p>&#39;;
    $("#RoomList").append(html);
   };
 
   //注册查询房间列表的方法
   chat.client.getRoomlist = function(data) {
    if (data) {
     var jsondata = $.parseJSON(data);
     $("#roomlist").html(" ");
     for (var i = 0; i < jsondata.length; i++) {
      var html = &#39; <li>房间名:&#39; + jsondata[i].RoomName + &#39;<button roomname="&#39; + jsondata[i].RoomName + &#39;" onclick="AddRoom(this)">加入</button></li>&#39;;
      $("#roomlist").append(html);
     }
    }
   };
   // 获取用户名称。
   $(&#39;#username&#39;).html(prompt(&#39;请输入您的名称:&#39;, &#39;&#39;));
 
   $.connection.hub.start().done(function() {
    $(&#39;#CreatRoom&#39;).click(function() {
     chat.server.createRoom($("#Roomname").val());
    });
   });
  });
   
  function SendMessage(btn) {
   var message = $(btn).prev().val();
   var room = $(btn).parent();
   var username = $("#username").html();
   message = username + ":" + message;
   var roomname = $(room).attr("roomname");
   chat.server.sendMessage(roomname, message);
   $(btn).prev().val(&#39;&#39;).focus();
  }
   
  function RemoveRoom(btn) {
   var room = $(btn).parent();
   var roomname = $(room).attr("roomname");
   chat.server.removeUserFromRoom(roomname);
  }
   
  function AddRoom(roomname) {
   var data =$(roomname).attr("roomname");
   chat.server.joinRoom(data);
  }
 
 </script>
</head>
<body>
 <p>
  <p>名称:<p id="username"></p></p>
  输入房间名:
  <input type="text" value="聊天室1" id="Roomname" />
  <button id="CreatRoom">创建聊天室</button>
 </p>
 <p style="float:left;border:double">
  <p>房间列表</p>
  <ul id="roomlist"></ul>
 </p>
 <p id="RoomList">
 </p>
</body>
</html>


4. After the above 3 steps, the chat room function has been completed. Let’s look at the specific effects. Previously, a helper code was attached here:

/// <summary>
 /// JSON 帮助类
 /// </summary>
 public class JsonHelper
 {
  /// <summary>
  /// 从一个对象信息生成Json字符串
  /// </summary>
  /// <param name="obj"></param>
  /// <returns></returns>
  public static string ToJsonString(object obj)
  {
   return JsonConvert.SerializeObject(obj);
  }
 
  /// <summary>
  /// 从Json字符串生成对象
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="jsonString"></param>
  /// <returns></returns>
  public static T ToObject<T>(string jsonString)
  {
   return JsonConvert.DeserializeObject<T>(jsonString);
  }
 }


IV. Operation results

Next, let’s take a look at the operation effect of the chat room function. The specific operation effect is shown in the figure below:

Asp.net uses SignalR to implement chat room functions

At this point, all the content of this article has been introduced. Next, I will write an article about how to use SignalR to realize the function of sending pictures.

For more articles related to Asp.net using SignalR to implement chat room functions, please pay attention to the PHP Chinese website!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn