WebSocket 网页聊天室的实现(服务器端:.net + windows服务,前端:Html5)
websocket是HTML5中的比较有特色一块,它使得以往在客户端软件中常用的socket在web程序中也能轻松的使用,较大的提高了效率。废话不多说,直接进入题。
网页聊天室包括2个部分,后端服务器+前端页面。
1、后端服务部分:.net4.0 + windows服务。相比寄宿在iis中,寄宿在进程中的windows服务更加的稳定可靠(文章中的例子用windows控制台程序演示,后面给出完整的windows服务的代码)。
2、前端部分:html5 + jQuery + bootstrap。基本的前端快速开发利器。
一、分析一下聊天室的场景需求,以便构建合适的数据结构。
1、在线用户类 OnlineUser 用户的基本特征为姓名Name(性别年龄啥的先忽略),当然在系统设计中,姓名并不能很好的区分不同用户,所以得需要一个唯一标识符Id。另外,由于可能存在多个聊天室,因此聊天室的编号RoomId也是用户的特征之一。综上,可得在线用户类OnlineUser的结构为:
/// <summary>
/// 在线用户信息
/// <summary>
public class OnlineUser
{
public int Id { get; set; }
public string Name { get; set; }
public string RoomId { get; set; }
public string SessionId { get; set; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
PS:SessionId是后续加入的属性,此处可先忽略。
2、消息类 Message 消息是聊天室最核心的部分,我最简单的消息机构应包含如下成员,消息发送者FromUserId,消息接受者ToUserId,消息类型Type,消息内容Content,消息时间Time。
public class Message
{
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public int Type { get; set; }
public string Time { get; set; }
public string Content { get; set; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
PS:这里Type用的是int类型表示的,是出于后续消息传递时,以便于将其包裹成Json格式传递。但在判断时,会将其转换成MessageType格式的枚举型,MessageType暂定包含以下几种状态,
public enum MessageType
{
/// <summary>
/// 新用户进入
/// <summary>
NewUserIn = 1,
/// <summary>
/// 用户离开
/// <summary>
UserExit = 2,
/// <summary>
/// 新用户提供自身信息
/// <summary>
ReprotUserInfo = 3,
/// <summary>
/// 新文字消息
/// </summary>
NewTextMessage = 4,
/// <summary>
/// 广播基本信息
/// </summary>
BroadcastBasicInfo = 5
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
二、项目结构
1、服务端项目结构如下:
这是一个windows控制台程序,Program.cs是入口,ChatWebSocket.cs是核心代码快,外部引用的dll包括Json操作类库和Socket操作类库。
其中,Model文件夹下的是上面提到的一些基本数据结构,这里看一下核心的代码ChatWebSocket。这当中用到的SuperSocket已经将socket的主要操作封装的很完备了,使用方法如下:
/* 侦听地址(注意,此处的地址一定要和前端js中的地址一致!!) */
const string IP = "127.0.0.1";
/* 侦听端口 */
const int PORT = 2016;
/* SuperWebSocket中的WebSocketServer对象 */
WebSocketServer wsServer = null;
/* 当前在线用户列表 */
List<OnlineUser> olUserList = new List<OnlineUser>();
/* 定时通知客户端线程 */
BackgroundWorker bkWork = null;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
这里,WebSocketServer是SuperSocket中封装好的Socket服务端类,olUserList 是在线用户列表,bkWork 是后台线程,负责定时向客户端发送一些系统信息。
构造函数中:
public ChatWebSocket()
{
/* 初始化 以及 相关事件注册 */
wsServer = new WebSocketServer();
/* 有新会话握手并连接成功 */
wsServer.NewSessionConnected += WsServer_NewSessionConnected;
/* 有会话被关闭 可能是服务端关闭 也可能是客户端关闭 */
wsServer.SessionClosed += WsServer_SessionClosed;
/* 有新文本消息被接收 */
wsServer.NewMessageReceived += WsServer_NewMessageReceived;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
WebSocketServer有几个比较重要的事件,
1、NewSessionConnected 有新会话握手并连接成功
2、SessionClosed 有会话被关闭 可能是服务端关闭 也可能是客户端关闭
3、NewMessageReceived 有新文本消息被接收
其中NewMessageReceived事件是消息传递的重要事件,也是我们重点处理的事件,下面将3个事件的方法体列出:
// 有新会话握手并连接成功
private void WsServer_NewSessionConnected(WebSocketSession session)
{
LogHelper.Write(session.SessionID.ToString() + " Connect!");
}
// 有新文本消息被接收
private void WsServer_NewMessageReceived(WebSocketSession session, string value)
{
LogHelper.Write("Receive Message:" + value);
var msg = JsonConvert.DeserializeObject<Message>(value);
MessageType mt = (MessageType)msg.Type;
switch (mt)
{
/* 用户报告自己信息,将UserId与SessionId关联 */
case MessageType.ReprotUserInfo:
olUserList.Add(new OnlineUser
{
SessionId = session.SessionID,
Id = msg.FromUserId,
RealName = msg.FromUserName,
RoomId = msg.RoomId
});
/* 通知其他用户 */
SendMessage(session, new Message
{
FromUserId = msg.FromUserId,
FromUserName = msg.FromUserName,
ToUserId = 0,// 同一房间的人
Type = (int)MessageType.NewUserIn,
Content = "",
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
RoomId = msg.RoomId
});
break;
/* 用户文字(图片)消息,服务器进行转发 */
case MessageType.NewTextMessage:
/* 通知其他用户 */
msg.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
SendMessage(session, msg);
break;
default: break;
}
}
// 有会话被关闭 可能是服务端关闭 也可能是客户端关闭
private void WsServer_SessionClosed(WebSocketSession session, CloseReason value)
{
LogHelper.Write(session.SessionID.ToString() + " Exit!");
var u = olUserList.Find(m => m.SessionId == session.SessionID);
if (u == null)
{
return;
}
olUserList.Remove(u);
// 通知其他用户
SendMessage(session, new Message
{
FromUserId = u.Id,
FromUserName = u.RealName,
ToUserId = 0,// 同一房间的人
Type = (int)MessageType.UserExit,
Content = "",
RoomId = u.RoomId
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
其中,SendMessage(WebSocketSession session, Message msg)
方法如下:
private void SendMessage(WebSocketSession session, Message msg)
{
// -1:全体;0:同一房间;剩下:特定的用户
var users = msg.ToUserId == -1 ? olUserList : msg.ToUserId == 0 ? olUserList.Where(m => m.RoomId == msg.RoomId).ToList() : olUserList.Where(m => m.Id == msg.ToUserId).ToList();
users.ForEach(u =>
{
var ss = session.AppServer.GetAppSessionByID(u.SessionId);
if (ss != null)
{
ss.Send(JsonConvert.SerializeObject(msg));
}
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
另外,WebSocketServer的启动和停止也非常的简单:
public void Start()
{
if (!wsServer.Setup(IP, PORT))
{
throw new Exception("设置WebSocket服务侦听地址失败!");
}
if (!wsServer.Start())
{
throw new Exception("启动WebSocket服务侦听失败!");
}
}
public void Stop()
{
if (wsServer != null)
{
wsServer.Stop();
}
}
WebSocket 网页聊天室的实现(服务器端:.net + windows服务,前端:Html5)的更多相关文章
- WebSocket 网页聊天室
先给大家开一个原始的websocket的连接使用范例 <?php /* * recv是从套接口接收数据,也就是拿过来,但是不知道是什么 * read是读取拿过来的数据,就是要知道recv过来的是 ...
- JAVA实现webSocket网页聊天室
一.什么是webSocket WebSocket 是一种网络通信协议,是持久化协议.RFC6455 定义了它的通信标准. WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全 ...
- Java和WebSocket开发网页聊天室
小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ Java和WebSocket开发网页聊天室 一.项 ...
- JavaWeb网页聊天室(WebSocket即时通讯)
原文:http://baike.xsoftlab.net/view/656.html Git地址 http://git.oschina.net/loopcc/WebSocketChat 概要: Web ...
- Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G
code&monkey Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...
- 基于flask的网页聊天室(四)
基于flask的网页聊天室(四) 前言 接前天的内容,今天完成了消息的处理 具体内容 上次使用了flask_login做用户登录,但是直接访问login_requare装饰的函数会报401错误,这里可 ...
- 基于WebSocket实现聊天室(Node)
基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...
- websocket+golang聊天室
原文地址: http://www.niu12.com/article/3 websocket+golang聊天室 main.go和index.html放在同一目录下 main.go package m ...
- Spring之WebSocket网页聊天以及服务器推送
Spring之WebSocket网页聊天以及服务器推送 转自:http://www.xdemo.org/spring-websocket-comet/ /Springframework /Spring ...
随机推荐
- gitlab恢复、迁移
文件说明 安装包:gitlab-ce_8.11.5-ce.0_amd64.deb 备份的数据:533751277_gitlab_backup.tar 系统:Ubuntu 16.04.4 LTS \n ...
- 文艺平衡树-splay的区间操作
真的是个神题,蒟蒻表示无力吐槽.刚开始以为是一个板子题,看着题解打了一遍,大概也理解了他是怎么实现的,然后我就去做别的题了,然后就在Three_D大佬的询问下蒙*了.最后还是问的nc哥,并思考了一个中 ...
- JAVA基础——IO流字节流
在Java中把不同的输入输出源(键盘.文件.网路连接)抽象表述为“流”. 1.输入流.输出流 .字节输入流通过FileInputStream和来操作 字节输出流通过FileOutputStream来操 ...
- swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?
date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowf ...
- react入门--------安装react
创建一个单页面应用 Create React App是开始构建新的React单页应用程序的最佳方式. 它可以帮助您快速集成您的开发环境,以便您可以使用最新的JavaScript功能,它提供了一个很好的 ...
- List lambda 排序
Comparator<PromotionRule> comparator = Comparator.comparing(PromotionRule::getCreatedTime); pr ...
- PLSQL 下载地址 Spring jar包
PLSQL https://www.allroundautomations.com/ instantclient http://www.oracle.com/technetwork/topic ...
- java多线程编程核心技术(一)--多线程技能
1.进程和线程的概念 1.进程:进程是操作系统的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动,是程序在一个数据集合上运行的过程,他是系统进行资源分配和调度的一个独立单位. ...
- linux 创建 bootable iso 文件
windows制作iso文件通过ultraiso可以实现. linux下用mkisofs这个命令就可以 最简单的用法 mkisofs -o target.iso source 要制作可以启动的iso文 ...
- 十进制浮点数转换成IEEE754标准的32浮点数的二进制格式
参考: http://jimmygod.blog.163.com/blog/static/43511339200792605627411/ http://blog.csdn.net/archersab ...