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()); } /// /// 您将需要在网站的 Web.config 文件中配置此处理程序 /// 并向 IIS 注册它,然后才能使用它。有关详细信息, /// 请参见下面的链接: http://go.microsoft.com/?linkid=8101007 /// #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 _allContexts = new List(); static readonly Dictionary> _allRooms = new Dictionary>(); static readonly Dictionary _allRoomsCache = new Dictionary(); static readonly Dictionary>> _roomsSendingQueue = new Dictionary>>(); 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 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[] 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 room; lock (_allContexts) { _allContexts.Add(context); if (!_allRooms.TryGetValue(roomid, out room)) { room = new List(); _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(new byte[2048]); var result = await socket.ReceiveAsync(buffer, CancellationToken.None); if (result.Count > 0) { //房间 lock (_roomsSendingQueue) { Queue> queue; if (!_roomsSendingQueue.TryGetValue(roomid, out queue)) { queue = new Queue>(0x400); _roomsSendingQueue.Add(roomid, queue); } lock (queue) { queue.Enqueue(new ArraySegment(buffer.Array, 0, result.Count)); } _eventSingal.Set(); } } } else { break; } } lock (_allContexts) { _allContexts.Remove(context); } lock (_allRooms) { List roomlist; if (_allRooms.TryGetValue(roomid, out roomlist)) { lock (roomlist) { roomlist.Remove(context); } } } lock (_allRoomsCache) { if (_allRoomsCache.ContainsKey(roomid)) _allRoomsCache.Remove(roomid); } } } }