对于FastHttpApi来说搭建一个基于Websocket的页面聊天室是一个非常简单的事件;毕竟基于FastHttpApi编写的接口默认就提供了WebSocket支持,因此在做基于Websocket通讯应用的时候和传统ajax数据交互应用没有多大的差别;以下讲解如何实现一个聊天室和管理功能。
功能描述
- 用户功能,主要功能:登陆,创建房间,进行房间,发言和查询房间用户信息
- 管理功能,主要功能:查看所有用户的发言进出房间情况,删除房间,踢用户下线
功能原型图
用户功能实现
用户功能不多,登陆、创建房间、进入房间、发言和查询房间用户功能。在传统的网络服务中编写起来可能有点因难,但在组件的支持下和传统web api没有两样。可能有人员会有点疑惑,毕竟web服务的api是无法向其他用户主动发信息,但在FastHttpApi则是可以的。接下来我们看一下这几个功能的API代码。
用户登陆
public bool Login(string userName, IHttpContext context)
{
context.Session.Name = userName;
if (context.Session.Name == "admin")
lock (mAdminList)
mAdminList.Add(context.Session);
return true;
}
登陆功能很简单提交昵称即可;由于是示例这里就不作太多安全性的处理,当用户名是admin就直接加入到管理员列表中了(在管理员列表的用户是可以接收其他用户的发言信息)。`
创建房间
public object CreateRoom(string roomName, IHttpContext context)
{
roomName = roomName.ToLower();
if (mRooms.Count > 200)
return new ActionResult(503, "房间已经满,不能再创建");
if (mRooms.ContainsKey(roomName))
return new ActionResult(504, "房间已经存在");
Room room = new Room();
room.Name = roomName;
room.Controller = this;
mRooms[room.Name] = room;
context.SendToWebSocket(new ActionResult(new Command { Type = "CreateRoom", Name = roomName }));
return true;
}
在这里对创建房间的数量进行了一个限制,创建成功后需要向所有用户广播一个创建房间信息。对于组件来说可以通过IHttpContext向当前服务中所有websocket连接发送数据;最后它还是要响应回当前请求的用户便建房间成功(代码中涉及的状态都是随意定义,和具体的HTTP状态并没有直接关系)。
进入和退出房间
public object CheckInRoom(string roomName, IHttpContext context)
{
Room room;
if (mRooms.TryGetValue(roomName, out room))
room.CheckIn(context);
else
return new ActionResult(404, "房间不存在");
return true;
}
public object CheckOutRoom(string roomName, IHttpContext context)
{
Room room;
if (mRooms.TryGetValue(roomName, out room))
room.CheckOut(context);
else
return new ActionResult(404, "房间不存在");
return true;
}
以上是进入和退出房间的代码,这个代码只是告诉当前用户进入房间的情况;我们还需要告诉当前房间的其他用户和管理员谁进或退出房间
public void CheckIn(IHttpContext context)
{
if (!Sessions.Contains(context.Request))
{
Command cmd = new Command();
cmd.Type = "CheckIn";
cmd.Room = Name;
cmd.Name = context.Session.Name;
context.Session["room"] = this.Name;
lock (this.Sessions)
Sessions.Add(context.Request);
SendMessage(cmd, context.Server);
Controller.SendToAdmin(cmd);
}
}
public void CheckOut(IHttpContext context)
{
Command cmd = new Command();
cmd.Type = "CheckOut";
cmd.Room = Name;
cmd.Name = context.Session.Name;
lock (this.Sessions)
Sessions.Remove(context.Request);
SendMessage(cmd, context.Server);
Controller.SendToAdmin(cmd);
}
通过以上代码,用户在进入或退出房间时都会把消息发给当前房间的所有用户和管理员。
用户发言
public void SendMessage(string message, IHttpContext context)
{
string name;
if (context.WebSocket)
{
name = context.Session.Name;
Room room = GetRoom(context.Session);
if (room != null)
Command cmd = room.Talk(name, message, context);
}
}
public Command Talk(string username, string message, IHttpContext context)
{
Command cmd = new Command();
cmd.Type = "Talk";
cmd.Message = message;
cmd.Room = Name;
cmd.Name = username;
SendMessage(cmd, context.Server);
Controller.SendToAdmin(cmd);
return cmd;
}
首先需要判断一下当的上下文是不是WebSocket,如果是就允许继续操作,从前用户Session获取房间信息,如果房间信息存在就把消息推送给房间的其他用户和管理员。这样用户的基础的服务功能已经完成。
管理端代码
管理端主要是监控和管理,所以功能相对并不多,主要就是删除房间和踢除在线用户。
public void CloseSession([BodyParameter]List<int> sessions, IHttpContext context)
{
foreach (int i in sessions)
{
ISession session = context.Server.BaseServer.GetSession(i);
if (session != null)
session.Dispose();
}
}
public void CloseRoom(string roomName, IHttpContext context)
{
Room room;
if (mRooms.Remove(roomName, out room))
{
room.Sessions.Clear();
Command cmd = new Command();
cmd.Type = "Delete";
cmd.Room = roomName;
context.SendToWebSocket(new ActionResult(cmd));
}
}
组件可以根据会话ID,获取会话对象后直接释放。对于关闭房间,在清除房间后广播一条删除房间消息给所有用户即可。
页面整合
当编写完成方法后,VS插件会自动生成对应的调用脚本,页面只需经引用脚即可调用.在页面处理上给合Vuejs还是可以很方便地把功能整合到一起的。
用户登陆方法
async function login() {
if (!$('#userName').val() || $('#userName').val().toLowerCase() == 'admin') {
alert('登陆名称无效!');
return;
}
var result = await $samples$chat$Login($('#userName').val());
if (result.Code != 200) {
alert(result.Error);
}
else {
alert("登陆成功");
lstTalkControl.Data = [];
$('#loginBar').hide();
$('#lstbody').empty();
loginStatus = true;
activeRoom = null;
listRoom();
}
}
用户发送消息
async function sendMessage() {
if (!$('#talkMsg').val()) {
alert('请输入发言的内容!');
return;
}
var result = await $samples$chat$SendMessage($('#talkMsg').val());
if (result.Code == 200) {
$('#talkMsg').val('');
}
else {
alert(result.Error);
}
}
定义接收广播消息
api_receive(function (result) {
switch (result.Data.Type) {
case "Talk":
lstTalkControl.Data.push({ Type: 'Talk', Time: GetTime(), Name: result.Data.Name, Message: ' 说:' + result.Data.Message });
break;
case "CheckIn":
lstTalkControl.Data.push({ Type: 'CheckIn', Time: GetTime(), Name: result.Data.Name, Message: ' 进入房间' });
listRoomUsers();
break;
case "CheckOut":
lstTalkControl.Data.push({ Type: 'CheckOut', Time: GetTime(), Name: result.Data.Name, Message: ' 退出房间' });
listRoomUsers();
break;
}
});
踢除用户
async function closeSession() {
var items = getSelectItems();
if (items.length > 0) {
if (confirm('是否要清除告诉的连接?')) {
var result = await $samples$chat$CloseSession(items);
}
}
}
这样聊天室用户和管理功能就集成完成,对于其他一些功能如用户连接断开广播消息,Uvejs数据绑定这些功能就不细说了可以通过sample代码来细看。