.NET 4.5简化了WebSocket的处理,不用再单独用socket去解析RFC6455的协议。
Windows .NET平台上要使用WebSocket,.NET版本不低于4.5、IIS至少8、IIS要启用WebSocket。
源码点我下载。
前端:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="jquery-3.3.1.js"></script>
<script>
var ws;
$().ready(function () {
$('#conn').click(function () {
ws = new WebSocket('ws://' + window.location.host + '/WSHandler4.ashx?user='+$('#user').val());
$('#list').append('<p>' + '正在连接' + '</p>');
ws.onopen = function () {
$('#list').append('<p>' + '连接成功' + '</p>');
};
ws.onmessage = function (evt) {
$('#list').append('<p>' + evt.data + '</p>');
};
ws.onerror = function (evt) {
$('#tips').text(JSON.stringify(evt));
};
ws.onclose = function () {
$('#list').append('<p>已断开</p>');
};
});
$('#send').click(function () {
if (ws.readyState == WebSocket.OPEN) {
ws.send($('#to').val() + '|' + $('#msg').val());
} else {
$('#tips').text('已断开');
}
});
$('#close').click(function () {
ws.close();
})
});
</script>
</head>
<body>
<div>
user:<input type="text" id="user" /><input type="button" id="conn" value="连接" /><input type="button" id="close" value="断开" />
</div>
<div>
msg:<input type="text" id="msg" />send to:<input type="text" id="to"/><input type="button" id="send" value="发送" />
</div>
<div id="tips"></div>
<div id="list"></div>
</body>
</html>
后端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
namespace MSMQ_WebSocket
{
/// <summary>
/// WSHandler4 的摘要说明
/// </summary>
public class WSHandler4 : IHttpHandler
{
private static bool MQCreated = false;
private static Dictionary<string, WebSocket> CONN_POOL = new Dictionary<string, WebSocket>();
public void ProcessRequest(HttpContext context)
{
//context.Response.ContentType = "text/plain";
//context.Response.Write("Hello World");
if (!MQCreated)
{
MSMQ_SRV.CreateMQ();
MQCreated = true;
}
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(ProcessChat);
}
}
async Task ProcessChat(AspNetWebSocketContext context)
{
string user = context.QueryString["user"].ToString();
WebSocket ws = context.WebSocket;
if (CONN_POOL.Keys.Contains(user))
{
if (CONN_POOL[user] != ws)
{
CONN_POOL[user] = ws;
}
}
else
{
CONN_POOL.Add(user, ws);
}
Message[] msgsOffline = MSMQ_SRV.GetMessageByDestUser(user);//发送者|接收者|消息内容
if (msgsOffline.Length > 0 && ws.State == WebSocketState.Open)
{
foreach (var item in msgsOffline)
{
//await ws.SendAsync(GetArrSegFromStr(item.Body.ToString()), WebSocketMessageType.Text, true, CancellationToken.None);
await ws.SendAsync(GetArrSegFromStr(item.Body.ToString().Split('|')[0] + ":" + item.Body.ToString().Split('|')[2]), WebSocketMessageType.Text, true, CancellationToken.None);
MSMQ_SRV.RemoveMessage(item);
}
}
while (true)
{
if (ws.State == WebSocketState.Open)
{
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
WebSocketReceiveResult result = await ws.ReceiveAsync(buffer, CancellationToken.None);
try
{
#region 连接关闭,删除连接池
if (ws.State != WebSocketState.Open)
{
if (CONN_POOL.Keys.Contains(user))
{
CONN_POOL.Remove(user);
}
break;
}
#endregion
string msgs = System.Text.Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
string desUser = msgs.Split('|')[0];
string desMsg = msgs.Split('|')[1];
if (CONN_POOL.Keys.Contains(desUser))
{
if (CONN_POOL[desUser].State == WebSocketState.Open)
{
await CONN_POOL[desUser].SendAsync(GetArrSegFromStr(desMsg), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
else
{
//有连接,但是断线了。MSMQ离线存储.
//发送者|接收者|消息内容
MSMQ_SRV.SendMSG(string.Format("{0}|{1}|{2}:{3}", user, desUser, DateTime.Now.ToString(), desMsg));
}
}
catch (Exception ex)
{
if (CONN_POOL.Keys.Contains(user))
{
CONN_POOL.Remove(user);
}
}
}
else
{
//发送消息时已离线。退出
break;
}
}
}
public bool IsReusable
{
get
{
return false;
}
}
/// <summary>
/// 字符串转ArraySegment<byte>,用于WebSocket发送消息
/// </summary>
/// <param name="text">要转换的文本</param>
/// <returns></returns>
ArraySegment<byte> GetArrSegFromStr(string text)
{
return new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(text));
}
/// <summary>
/// ArraySegment<byte>转字符串,用于消息队列存储
/// </summary>
/// <param name="text">要转换的ArraySegment对象</param>
/// <returns></returns>
string GetStrFromArrSeg(ArraySegment<byte> arrSeg)
{
return System.Text.Encoding.UTF8.GetString(arrSeg.Array);
}
}
}
离线消息处理(消息队列msmq)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Messaging;
namespace MSMQ_WebSocket
{
public class MSMQ_SRV
{
public static void CreateMQ()
{
MessageQueue mq = null;
string path = @".\private$\mqDemo4";
if (MessageQueue.Exists(path))
{
mq = new MessageQueue(path);
}
else
{
mq = MessageQueue.Create(path);
}
mq.SetPermissions("Anonymous LogOn", MessageQueueAccessRights.FullControl);
mq.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
mq.SetPermissions("Administrator", MessageQueueAccessRights.FullControl);
System.Diagnostics.Debug.WriteLine("mqDemo4建立成功");
}
public static void SendMSG(String text)
{
Message msg = new Message()
{
Body = text,
Formatter = new XmlMessageFormatter(new Type[] { typeof(string) })
};
MessageQueue mq = new MessageQueue(@"FormatName:Direct=TCP:127.0.0.1\private$\mqDemo4");
mq.Send(msg);
}
/// <summary>
/// 根据接收者获取队列
/// </summary>
/// <param name="destUser"></param>
/// <returns></returns>
public static Message[] GetMessageByDestUser(string destUser)
{
string path = @".\private$\mqDemo4";
MessageQueue mq = new MessageQueue()
{
Path = path,
Formatter = new XmlMessageFormatter(new Type[] { typeof(string) })
};
return mq.GetAllMessages().Where(t => t.Body.ToString().Split('|')[1] == destUser).ToArray();
}
public static void RemoveMessage(Message msg)
{
string path = @".\private$\mqDemo4";
MessageQueue mq = new MessageQueue()
{
Path = path,
Formatter = new XmlMessageFormatter(new Type[] { typeof(string) })
};
mq.ReceiveById(msg.Id);
}
public static void RemoveMessages(Message[] msgs)
{
string path = @".\private$\mqDemo4";
MessageQueue mq = new MessageQueue()
{
Path = path,
Formatter = new XmlMessageFormatter(new Type[] { typeof(string) })
};
foreach (var item in msgs)
{
mq.ReceiveById(item.Id);
}
}
}
}