Light12306/Web12306/ChatServers.cs
2014-08-14 21:33:47 +08:00

206 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
namespace Web12306
{
public class ChatServers : IHttpHandler
{
static ChatServers()
{
ThreadPool.QueueUserWorkItem(_ => SendMessageQueue());
}
/// <summary>
/// 您将需要在网站的 Web.config 文件中配置此处理程序
/// 并向 IIS 注册它,然后才能使用它。有关详细信息,
/// 请参见下面的链接: http://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpHandler Members
public bool IsReusable
{
// 如果无法为其他请求重用托管处理程序,则返回 false。
// 如果按请求保留某些状态信息,则通常这将为 false。
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
{
//if(context.WebSocketNegotiatedProtocol=="Fish12306")
context.AcceptWebSocketRequest(ProcessChat);
}
else
{
context.Response.ContentType = "application/json";
context.Response.WriteFile(context.Server.MapPath("/chatservers.json"));
}
}
#endregion
static readonly List<AspNetWebSocketContext> _allContexts = new List<AspNetWebSocketContext>();
static readonly Dictionary<string, List<AspNetWebSocketContext>> _allRooms = new Dictionary<string, List<AspNetWebSocketContext>>();
static readonly Dictionary<string, AspNetWebSocketContext[]> _allRoomsCache = new Dictionary<string, AspNetWebSocketContext[]>();
static readonly Dictionary<string, Queue<ArraySegment<byte>>> _roomsSendingQueue = new Dictionary<string, Queue<ArraySegment<byte>>>();
static AutoResetEvent _eventSingal = new AutoResetEvent(true);
static void SendMessageQueue()
{
while (true)
{
_eventSingal.WaitOne();
var allQueues = _roomsSendingQueue.ToArray();
foreach (var queueData in allQueues)
{
var roomid = queueData.Key;
var queue = queueData.Value;
AspNetWebSocketContext[] contexts;
lock (_allRoomsCache)
{
if (!_allRoomsCache.TryGetValue(roomid, out contexts))
{
List<AspNetWebSocketContext> contextList;
lock (_allRooms)
{
if (!_allRooms.TryGetValue(roomid, out contextList) || contextList == null)
continue;
}
lock (contextList)
{
contexts = contextList.ToArray();
_allRoomsCache.Add(roomid, contexts);
}
}
}
if (queue.Count > 0)
{
ArraySegment<byte>[] dataItems = null;
lock (queue)
{
dataItems = queue.ToArray();
queue.Clear();
}
if (dataItems.Length == 0)
continue;
foreach (var context in contexts)
{
if (!context.IsClientConnected || context.WebSocket.State != WebSocketState.Open)
continue;
try
{
for (int i = 0; i < dataItems.Length; i++)
{
context.WebSocket.SendAsync(dataItems[i], WebSocketMessageType.Text, i == dataItems.Length - 1, CancellationToken.None);
}
}
catch (Exception ex)
{
}
finally
{
}
}
}
}
}
}
private async Task ProcessChat(AspNetWebSocketContext context)
{
WebSocket socket = context.WebSocket;
var path = context.Path.Trim('/');
var roomid = path.Substring(path.LastIndexOf('/') + 1);
List<AspNetWebSocketContext> room;
lock (_allContexts)
{
_allContexts.Add(context);
if (!_allRooms.TryGetValue(roomid, out room))
{
room = new List<AspNetWebSocketContext>();
_allRooms.Add(roomid, room);
}
room.Add(context);
}
lock (_allRoomsCache)
{
if (_allRoomsCache.ContainsKey(roomid))
_allRoomsCache.Remove(roomid);
}
while (true)
{
if (socket.State == WebSocketState.Open)
{
var buffer = new ArraySegment<byte>(new byte[2048]);
var result = await socket.ReceiveAsync(buffer, CancellationToken.None);
if (result.Count > 0)
{
//房间
lock (_roomsSendingQueue)
{
Queue<ArraySegment<byte>> queue;
if (!_roomsSendingQueue.TryGetValue(roomid, out queue))
{
queue = new Queue<ArraySegment<byte>>(0x400);
_roomsSendingQueue.Add(roomid, queue);
}
lock (queue)
{
queue.Enqueue(new ArraySegment<byte>(buffer.Array, 0, result.Count));
}
_eventSingal.Set();
}
}
}
else
{
break;
}
}
lock (_allContexts)
{
_allContexts.Remove(context);
}
lock (_allRooms)
{
List<AspNetWebSocketContext> roomlist;
if (_allRooms.TryGetValue(roomid, out roomlist))
{
lock (roomlist)
{
roomlist.Remove(context);
}
}
}
lock (_allRoomsCache)
{
if (_allRoomsCache.ContainsKey(roomid))
_allRoomsCache.Remove(roomid);
}
}
}
}