using Microsoft.AspNetCore.Http;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Serilog;
using System.Threading;
using System.Text;
using UAParser;
using NCC.Message.Entitys.Model.IM;
using NCC.Message.Entitys.Dto.IM;
using NCC.Message.Interfaces.Message;
using System.Linq;
using NCC.JsonSerialization;
using NCC.Common.Const;
using NCC.DataEncryption;
using NCC.Common.Extension;
using NCC.Dependency;
using NCC.Common.Configuration;
using System.IO;
using NCC.Common.Helper;
using NCC.Common.Filter;
using NCC.System.Interfaces.Permission;
using System.Collections.Generic;
using NCC.System.Interfaces.System;
using NCC.System.Entitys.Permission;
namespace NCC.Message.Extensions
{
///
/// WebSocket中间件
///
public class WebSocketHandlerMiddleware
{
///
/// 下一级管道
///
private readonly RequestDelegate _next;
///
/// 缓冲区大小
///
private const int bufferSize = 1024 * 8;
///
/// URL地址后缀
///
private const string routePostfix = "/api/message/websocket";
///
/// 初始化一个类型的新实例
///
public WebSocketHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
///
/// 调用
///
///
///
public async Task Invoke(HttpContext context)
{
if (!IsWebSocket(context))
{
await _next.Invoke(context);
return;
}
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
while (webSocket.State == WebSocketState.Open)
{
string clientId = Guid.NewGuid().ToString();
var wsClient = new WebSocketClient
{
ConnectionId = clientId,
WebSocket = webSocket,
LoginIpAddress = App.HttpContext.GetRemoteIpAddressToIPv4(),
LoginPlatForm = Parser.GetDefault().Parse(context.Request.Headers["User-Agent"]).String
};
try
{
await Handle(wsClient);
}
catch (Exception ex)
{
WebSocketClientCollection.Remove(wsClient);
Log.Error(ex, "Echo websocket client {0} err.", clientId);
await context.Response.WriteAsync("closed");
Log.Information($"Websocket client closed.");
}
}
}
private async Task Handle(WebSocketClient webSocket)
{
WebSocketClientCollection.Add(webSocket);
Log.Information($"Websocket client added.");
WebSocketReceiveResult result = null;
do
{
var buffer = new ArraySegment(new byte[bufferSize]);
result = await webSocket.WebSocket.ReceiveAsync(buffer, CancellationToken.None);
while (!result.EndOfMessage)
{
result = await webSocket.WebSocket.ReceiveAsync(buffer, default(CancellationToken));
}
if (result.MessageType == WebSocketMessageType.Text && !result.CloseStatus.HasValue)
{
var msgString = Encoding.UTF8.GetString(buffer.Array);
Log.Information($"Websocket client ReceiveAsync message {msgString}.");
var message = msgString.ToObject();
message.sendClientId = webSocket.ConnectionId;
MessageRoute(message);
}
}
while (!result.CloseStatus.HasValue);
WebSocketClientCollection.Remove(webSocket);
Log.Information($"Websocket client closed.");
}
private void MessageRoute(MessageInput message)
{
var client = WebSocketClientCollection.Get(message.sendClientId);
var claims = JWTEncryption.ReadJwtToken(message.token.Replace("Bearer ", "").Replace("bearer ", ""))?.Claims;
var userId = claims.FirstOrDefault(e => e.Type == ClaimConst.CLAINM_USERID)?.Value;
if (string.IsNullOrEmpty(userId))
{
client.SendMessageAsync(new { method = "logout" }.Serialize());
}
switch (message.method)
{
#region 群聊
//加入房间/群组
case "join":
{
client.RoomNo = message.RoomNo;
if (client.RoomName.IsNullOrEmpty()) client.RoomName = message.RoomName;
UserEntity userEntity = null;
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _userService = App.GetService(services);
userEntity = _userService.GetInfoByUserId(userId);
if (userEntity != null)
{
string msg = new { method = "join", account = userEntity.Account, headIcon = userEntity.HeadIcon, realName = userEntity.RealName, client.RoomNo, client.RoomName, message = $"{userEntity.RealName} 加入聊天 .", dateTime = DateTime.Now, latestDate = DateTime.Now }.Serialize();
client.SendMessageAsync(msg);
}
});
Log.Information($"Websocket client {userEntity.RealName} join room {client.RoomNo}.");
}
break;
//发送消息到群组/房间
case "send_to_room":
{
if (string.IsNullOrEmpty(client.RoomNo) || !message.IsRoom)
{
break;
}
var toUserId = message.toUserId;
var messageType = message.messageType;
var messageContent = message.messageContent;
UserEntity userEntity = null;
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _userService = App.GetService(services);
userEntity = _userService.GetInfoByUserId(userId);
});
var clients = WebSocketClientCollection.GetRoomClients(client.RoomNo);
string msg = new { method = "send_to_room", account = userEntity.Account, headIcon = userEntity.HeadIcon, realName = userEntity.RealName, client.RoomNo, client.RoomName, message = messageContent, dateTime = DateTime.Now, latestDate = DateTime.Now }.Serialize();
clients.ForEach(c =>
{
c.SendMessageAsync(msg);
});
Log.Information($"Websocket client {userEntity.RealName} send message {messageContent} to room {client.RoomNo}");
}
break;
//离开房间
case "leave":
{
if (string.IsNullOrEmpty(client.RoomNo))
{
break;
}
UserEntity userEntity = null;
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _userService = App.GetService(services);
userEntity = _userService.GetInfoByUserId(userId);
});
var roomNo = client.RoomNo;
string msg = new { method = "leave", account = userEntity.Account, headIcon = userEntity.HeadIcon, realName = userEntity.RealName, client.RoomNo, client.RoomName, message = $"{userEntity.RealName} 离开房间 .", dateTime = DateTime.Now, latestDate = DateTime.Now }.Serialize();
client.SendMessageAsync($"{userEntity.RealName} 离开房间 .");
Log.Information($"Websocket client {userEntity.RealName} 离开房间 room {roomNo} {client.RoomName}");
client.RoomNo = ""; //删除房间号
}
break;
#endregion
case "OnConnection":
{
#region 建立连接
var isMobileDevice = message.mobileDevice;
//读取 Token,不含验证
client.UserId = userId;
client.Account = claims.FirstOrDefault(e => e.Type == ClaimConst.CLAINM_ACCOUNT)?.Value;
client.UserName = claims.FirstOrDefault(e => e.Type == ClaimConst.CLAINM_REALNAME)?.Value;
client.TenantId = claims.FirstOrDefault(e => e.Type == ClaimConst.TENANT_ID)?.Value;
client.LoginTime = string.Format("{0:yyyy-MM-dd HH:mm}", Ext.GetDateTime(claims.FirstOrDefault(e => e.Type == "iat")?.Value + "000"));
client.LoginIpAddress = client.LoginIpAddress == "0.0.0.1" ? "127.0.0.1" : client.LoginIpAddress;
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _userService = App.GetService(services);
var userEntity = _userService.GetInfoByUserId(userId);
if (userEntity != null)
client.HeadIcon = "/api/file/Image/userAvatar/" + userEntity.HeadIcon;
});
//添加在线用户缓存与单体登录
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _sysCacheService = App.GetService(services);
var list = _sysCacheService.GetOnlineUserList(client.TenantId);
if (list == null)
{
list = new List();
}
var user = list.Find(it => it.tenantId == client.TenantId && it.userId == client.UserId);
if (user != null)
{
var onlineUser = WebSocketClientCollection._clients.Find(q => q.ConnectionId == user.connectionId);
if (onlineUser != null)
{
onlineUser.SendMessageAsync(new { method = "logout", msg = "此账号已在其他地方登陆" }.Serialize());
WebSocketClientCollection._clients.RemoveAll((x) => x.ConnectionId == user.connectionId);
}
list.RemoveAll((x) => x.connectionId == user.connectionId);
}
list.Add(new UserOnlineModel()
{
connectionId = client.ConnectionId,
userId = client.UserId,
account = client.Account,
userName = client.UserName,
lastTime = DateTime.Now,
lastLoginIp = client.LoginIpAddress,
tenantId = client.TenantId,
lastLoginPlatForm = client.LoginPlatForm
});
_sysCacheService.SetOnlineUserList(client.TenantId, list);
});
lock (WebSocketClientCollection.locker)
{
var onlineUserList = WebSocketClientCollection._clients.FindAll(q => q.TenantId == client.TenantId && q.IsMobileDevice == isMobileDevice);
#region 反馈信息给登录者
var onlineUsers = onlineUserList.Select(m => m.UserId).ToList();
var webOnlineUsers = onlineUserList.FindAll(q => q.IsMobileDevice == false).Select(m => m.UserId).ToList();
//从作用域内获取出IMessageService
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _messageService = App.GetService(services); // services 传递进去
var _IMContentService = App.GetService(services);
var unreadNums = _IMContentService.GetUnreadList(userId);
var unreadNoticeCount = _messageService.GetUnreadNoticeCount(client.UserId);
var unreadMessageCount = _messageService.GetUnreadMessageCount(client.UserId);
var noticeDefaultText = _messageService.GetInfoDefaultNotice();
var messageDefaultText = _messageService.GetInfoDefaultMessage(userId);
client.SendMessageAsync(new { method = "initMessage", onlineUsers, unreadNums, unreadNoticeCount, noticeDefaultText, unreadMessageCount, messageDefaultText, dateTime = DateTime.Now }.Serialize());
});
#endregion
#region 通知所有在线用户,有用户在线
if (!webOnlineUsers.Contains(userId))
{
onlineUserList.ForEach(c =>
{
c.SendMessageAsync(new { method = "online", userId = userId }.Serialize());
});
}
#endregion
}
#endregion
}
break;
case "SendMessage":
{
#region 发送消息
var toUserId = message.toUserId;
var messageType = message.messageType;
var messageContent = message.messageContent;
var fileName = "";
if (messageType == "image")
{
var directoryPath = FileVariable.IMContentFilePath;
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);
var imageInput = messageContent.ToObeject();
fileName = imageInput.name;
//var filePath = directoryPath + fileName;
//FileHelper.MakeThumbnail(filePath, (directoryPath + "T" + fileName), 300, 300, "H", Path.GetExtension(fileName), 0, 0);
}
var onlineUser = WebSocketClientCollection._clients.FirstOrDefault(q => q.ConnectionId == client.ConnectionId);
var onlineToUser = WebSocketClientCollection._clients.FirstOrDefault(q => q.TenantId == onlineUser.TenantId && q.UserId == toUserId && q.IsMobileDevice == onlineUser.IsMobileDevice);
//将发送消息对象信息补全
var toAccount = string.Empty;
var toHeadIcon = string.Empty;
var toRealName = string.Empty;
if (onlineToUser != null)
{
toAccount = onlineToUser.Account;
toHeadIcon = onlineToUser.HeadIcon;
toRealName = onlineToUser.UserName;
}
else
{
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _userService = App.GetService(services);
var userEntity = _userService.GetInfoByUserId(userId);
toAccount = userEntity.Account;
toHeadIcon = "/api/file/Image/userAvatar/" + userEntity.HeadIcon;
toRealName = userEntity.RealName;
});
}
if (onlineUser != null)
{
#region saveMessage
if (messageType == "text")
{
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _IMContentService = App.GetService(services);
_IMContentService.SendMessage(onlineUser.UserId, toUserId, messageContent.ToString(), messageType);
});
}
else if (messageType == "image")
{
var imageInput = messageContent.ToObeject();
var toMessage = new { path = "/api/file/Image/IM/" + fileName, width = imageInput.width, height = imageInput.height };
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _IMContentService = App.GetService(services);
_IMContentService.SendMessage(onlineUser.UserId, toUserId, toMessage.ToJson(), messageType);
});
}
else if (messageType == "voice")
{
//var toMessage = new { path = fileName, length = receivedMessage["messageContent"]["length"].ToString() };
//await iMContentBll.SendMessage(onlineUser.UserId, toUserId, toMessage.ToJson(), messageType);
}
#endregion
#region sendMessage
if (messageType == "text")
{
client.SendMessageAsync(new { method = "sendMessage", onlineUser.UserId, account = onlineUser.Account, headIcon = onlineUser.HeadIcon, realName = onlineUser.UserName, toAccount, toHeadIcon, messageType, toUserId, toRealName, toMessage = messageContent, dateTime = DateTime.Now, latestDate = DateTime.Now }.Serialize());
}
else if (messageType == "image")
{
var imageInput = messageContent.ToObeject();
var toMessage = new { path = "/api/file/Image/IM/" + fileName, width = imageInput.width, height = imageInput.height };
client.SendMessageAsync(new { method = "sendMessage", onlineUser.UserId, account = onlineUser.Account, headIcon = onlineUser.HeadIcon, realName = onlineUser.UserName, toAccount, toHeadIcon, messageType, toUserId, toMessage, dateTime = DateTime.Now, latestDate = DateTime.Now }.Serialize());
}
else if (messageType == "voice")
{
//var toMessage = new { path = fileName, length = receivedMessage["messageContent"]["length"].ToString() };
//client.SendMessageAsync(new { method = "sendMessage", onlineUser.UserId, account = onlineUser.Account, headIcon = onlineUser.HeadIcon, realName = onlineUser.UserName, toAccount, toHeadIcon, messageType, toUserId, toMessage, dateTime = DateTime.Now }.Serialize());
}
#endregion
}
if (onlineToUser != null)
{
#region receiveMessage
if (messageType == "text")
{
onlineToUser.SendMessageAsync(new { method = "receiveMessage", messageType, formUserId = client.UserId, formMessage = messageContent, dateTime = DateTime.Now, latestDate = DateTime.Now, headIcon = onlineUser.HeadIcon, realName = onlineUser.UserName }.Serialize());
}
else if (messageType == "image")
{
var imageInput = messageContent.ToObeject();
var formMessage = new { path = "/api/file/Image/IM/" + fileName, width = imageInput.width, height = imageInput.height };
onlineToUser.SendMessageAsync(new { method = "receiveMessage", messageType, formUserId = onlineUser.UserId, formMessage, dateTime = DateTime.Now, latestDate = DateTime.Now, headIcon = onlineUser.HeadIcon, realName = onlineUser.UserName }.Serialize());
}
else if (messageType == "voice")
{
//var formMessage = new { path = fileName, length = receivedMessage["messageContent"]["length"].ToString() };
//SendAsync(onlineToUser.WebSocket, new { method = "receiveMessage", messageType, formUserId = onlineUser.UserId, formMessage, dateTime = DateTime.Now, latestDate = DateTime.Now, headIcon = onlineUser.HeadIcon, realName = onlineUser.UserName }.Serialize());
}
#endregion
}
#endregion
}
break;
case "UpdateReadMessage":
{
var fromUserId = message.formUserId;
var onlineUser = WebSocketClientCollection._clients.FirstOrDefault(q => q.ConnectionId == client.ConnectionId);
//从作用域内获取出IMessageService
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _IMContentService = App.GetService(services);
_IMContentService.ReadMessage(fromUserId, onlineUser.UserId);
});
}
break;
case "MessageList":
{
var sendUserId = message.toUserId; //发送者
var receiveUserId = message.formUserId; //接收者
var requestParam = new PageInputBase();
requestParam.currentPage = message.currentPage;
requestParam.pageSize = message.pageSize;
requestParam.sort = message.sord;
requestParam.keyword = message.keyword;
//从作用域内获取出IMessageService
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;
var _IMContentService = App.GetService(services);
var data = _IMContentService.GetMessageList(sendUserId, receiveUserId, requestParam);
var list = data.GetType().GetProperty("list").GetValue(data, null);
var pagination = data.GetType().GetProperty("pagination").GetValue(data, null);
client.SendMessageAsync(new { method = "messageList", list = list, pagination = pagination }.Serialize());
});
}
break;
default:
break;
}
}
private static bool Base64ToFileAndSave(string fileName, string strInput)
{
fileName = fileName.Replace("/", "\\");
bool bTrue = false;
try
{
byte[] buffer = Convert.FromBase64String(strInput);
FileStream fs = new FileStream(fileName, FileMode.CreateNew);
fs.Write(buffer, 0, buffer.Length);
fs.Close();
bTrue = true;
}
catch (Exception ex)
{
Log.Error("Base64ToFileAndSave - OnError");
Log.Error(ex.Message);
bTrue = false;
}
return bTrue;
}
///
/// 当前请求是否为WebSocket
///
/// Http上下文
///
private bool IsWebSocket(HttpContext context)
{
return context.WebSockets.IsWebSocketRequest &&
context.Request.Path == routePostfix;
}
}
}