1.采用控制器的方法 这个只写建立链接的方法的核心方法
1.1 踩坑 网上都是直接 传个异步方法 直接接受链接 自己尝试了好多次链接是打开的,到时获取不到链接里面的内容!! (如果是我理解有问题的话,欢迎讨论,毕竟这个问题卡了我好久!)
1.2 自己建立链接的使用截图?方法
? ?
/// <summary>
/// WebSocket链接 demo地址 ws://Localhost:8080/Get
/// </summary>
[HttpGet]
public void Get()
{
if (System.Web.HttpContext.Current.IsWebSocketRequest)
{
//System.Web.HttpContext.Current.AcceptWebSocketRequest(AnalysisOptionChat);
System.Web.HttpContext.Current.AcceptWebSocketRequest(async (context) => await AnalysisOptionChat(context));
}
}
1.3?判断是WebSocket?链接后?处理消息
1
/// <summary>
/// WebSocket链接操作
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private async Task AnalysisOptionChat(AspNetWebSocketContext context)
{
var webSocket = context.WebSocket;
#region 测试demo
try
{
ConsoleWrite.ConsoleInfo($"Websocket client add--> 自己需要实现 保存链接");
WebSocketReceiveResult clientData = null;
do
{
var buffer = new ArraySegment<byte>(new byte[1024 * 1000]);
//if (clientData.MessageType == WebSocketMessageType.Text && !clientData.CloseStatus.HasValue)
if (webSocket.State == WebSocketState.Open && clientData.MessageType == WebSocketMessageType.Text)
{
string msgString = Encoding.UTF8.GetString(buffer.Array, 0, clientData.Count);
var sendData = string.Empty;
if ("rub".Equals(msgString))//心跳
{
}
else
{
int size = clientData.Count;
#region 通知
#endregion
#region DB持久化 操作
#endregion
}
}
}
//while (!clientData.CloseStatus.HasValue);
while (webSocket.State == WebSocketState.Open);
var state = webSocket.State;
ConsoleWrite.ConsoleWarning($"Websocket client closed1-->{state.ToString()}");
ConsoleWrite.ConsoleWarning($"Websocket client closed2-->{clientData.CloseStatus.Value}");
//关闭事件
await webSocket.CloseAsync(clientData.CloseStatus.Value, clientData.CloseStatusDescription, CancellationToken.None);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
#endregion
}
1.4?在Asp.Net MVC?中使用可能会遇到?跨域的问题?一下是解决方案
1
<system.webServer>
<!--全局跨域-->
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<!--<add name="Access-Control-Allow-Headers" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />-->
</customHeaders>
</httpProtocol>
<system.webServer>
1.在WepApi中使用法 这个只写建立链接的方法的核心方法
1.首先需要在Startup.cs?里面的?ConfigureServices里面配置跨域?如果不牵扯跨域的话?可以忽略 1,2 (里面的跨域) 跨域详细配置如下
services.AddCors(options =>
{
options.AddPolicy("WebSocketCors", builder =>
{
builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
});
});
2.然后在?Configure方法 里面加入管道?
app.UseWebSockets(new Microsoft.AspNetCore.Builder.WebSocketOptions
{
//保持活动间隔
KeepAliveInterval = TimeSpan.FromMinutes(5),
});
// 注意这个是重点!!!!
app.UseMiddleware<WebsocketHandlerMiddleware>();
//自定义跨域规则
app.UseCors("WebSocketCors");
详细截图如下
?3.?请求中间件 WebsocketHandlerMiddleware.cs?详细介绍如下
public class WebsocketHandlerMiddleware
{
private readonly RequestDelegate _next;
public WebsocketHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path == "/ws")
{
//客户端与服务器成功建立连接后,服务器会循环异步接收客户端发送的消息,收到消息后就会执行Handle(WebsocketClient websocketClient)中的do{}while;直到客户端断开连接
//不同的客户端向服务器发送消息后台执行do{}while;时,websocketClient实参是不同的,它与客户端一一对应
//同一个客户端向服务器多次发送消息后台执行do{}while;时,websocketClient实参是相同的
if (context.WebSockets.IsWebSocketRequest)
{
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
}
else
{
context.Response.StatusCode = 404;
}
}
else
{
await _next(context);
}
}
}
里面处理?WebSocket?请求消息的内容?和?1.3? 中方法是一样的?我这里就不重复了?两者的区别都只是?获取方式不一样而已。
1.在WepApi中使用法 这个只写建立链接的方法的核心方法?以及相关方法介绍
所使用的Nuget?包?如下:
1.1?首先需要在Startup.cs?里面的 ConfigureServices方法 注入且配置跨域.
1
#region SignalR
services.AddCors(options =>
{
options.AddPolicy("SignalRCors", builder =>
{
builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
});
});
services.AddSignalR().AddHubOptions<SignalrHub>(options =>
{
options.EnableDetailedErrors = true;
})
// 支持MessagePack
.AddMessagePackProtocol();
#endregion
同时还需要?注入加入声明周期?管控?
//特殊的 services.AddSingleton<SignalrHub>();
1.2?在Configure方法里面?的相关使用
//自定义跨域规则
app.UseCors("SignalRCors");
//需要在 Map里面 指定路由
//app.UseHttpsRedirection();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
//可以设置SignalR相关参数,这里设置地址
endpoints.MapHub<SignalrHub>("hubs/signalr", options =>
{
//配置 几种方式
options.Transports = HttpTransportType.WebSockets | HttpTransportType.LongPolling;
});
});
1.3?SignalrHub.cs 集线器类的使用介绍?里面有许多用不上的?自己按照需求自己过滤?主要就是?OnConnectedAsync()?建立链接?OnDisconnectedAsync()断开链接?方法的使用!
1
//创建SingalR中心跨域
[EnableCors("SignalRCors")]
public class SignalrHub : Hub
{
//ReceiveMessage 为客户端监听的 方法名称
private static string clientSendMethodName = "ReceiveMessage";
//Redis 存储信息Key
private static string signalrRedisKey = "SignalRConnections";
public SignalrHub()
{
}
#region Send
/// <summary>
/// 发送消息-指定用户集合发送消息
/// </summary>
/// <param name="userIds">通知用户集合</param>
/// <param name="sendObject">通知OBject</param>
/// <returns></returns>
public async Task SendUserMessage(List<int> userIds,object sendObject)
{
//从redis 获取 userIds 对应的 ConnectionId 进行推送
var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
if(connectionUserList.Count()<=0) throw new Exception("无连接用户,无法进行消息推送。");
var sentUserClient = connectionUserList.Where(x => userIds.Contains(x.UserId)).ToList();
var sendMeaaageToJson = JsonHelper.ObjectToJsonCamelCase(sendObject);
var connectionIds = sentUserClient.Select(y => y.ConnectionId);
//全部
//await Clients.All.SendAsync(clientSendMethodName, new { data = sendMeaaageToJson });
//指定
await Clients.Clients(connectionIds).SendAsync(clientSendMethodName, new { data = sendMeaaageToJson });
}
//发送消息--发送给所有连接的客户端
public async Task SendMessage(string msg)
{
//await Clients.All.SendAsync(clientSendMethodName, msg);
Console.WriteLine($"保持心跳链接-->接收参数:{msg}");
}
/// <summary>
/// 除 connectionId 之外的所有发送message
/// </summary>
/// <param name="connectionId"></param>
/// <param name="message"></param>
/// <returns></returns>
public Task SendAllExceptMe(string connectionId, string message)
{
return Clients.AllExcept(connectionId).SendAsync(clientSendMethodName, $"{Context.ConnectionId}: {message}");
}
#endregion
#region Group分组的话 依据职位 或者 角色来业务循环获取
#endregion
#region overrides
/// <summary>
/// 当新的客户端连接建立时执行的操作
/// </summary>
/// <returns></returns>
public override async Task OnConnectedAsync()
{
//建立者用户Id
var clientUserId = AuthHelper.GetUserId(Context.User);
//建立者用户Name
var clientUserName = Context.User.Identity.Name;
//建立ConnectionId
var connectionId = Context.ConnectionId;
if (clientUserId <= 0 || string.IsNullOrWhiteSpace(clientUserName))
{
throw new Exception("建立连接异常,无法获取用户信息。");
}
Console.WriteLine($"OnConnectedAsync建立链接----userId:{clientUserId},userName:{clientUserName},connectionId:{ Context.ConnectionId}");
var userConnections = new List<UserConnection>();
userConnections.Add(new UserConnection() { UserId = clientUserId, UserName = clientUserName, ConnectionId = connectionId, CreateTime = DateTime.Now });
//redis存储连接用户信息
RedisService.SetLPushValue(signalrRedisKey, false, userConnections);
//获取所有用户的链接信息
//var aaa = RedisService.GetLPushData<UserConnection>(signalrRedisKey,0,10000000);
await base.OnConnectedAsync();
}
/// <summary>
/// 当客户端断开连接时执行的操作
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override async Task OnDisconnectedAsync(Exception exception)
{
//建立者用户Id
var clientUserId = AuthHelper.GetUserId(Context.User);
//建立者用户Name
var clientUserName = Context.User.Identity.Name;
//建立ConnectionId
var connectionId = Context.ConnectionId;
//NLogHelper.ActionWarn();
Console.WriteLine($"OnDisconnectedAsync断开链接----userId:{clientUserId},clientUserName:{clientUserName},connectionId:{ connectionId}");
var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
//在redis里面依据 Value 移除
var removeItem = connectionUserList.Where(x => x.ConnectionId == connectionId).FirstOrDefault();
if (removeItem != null)
{
var Drow = await RedisService.LRemAsync(signalrRedisKey, removeItem);
//var connectionUserLists = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
}
await base.OnDisconnectedAsync(exception);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
#endregion
#region ClearOverTime 清除超时的垃圾数据
public async Task ClearOverTimeUserRedis(int houre=24)
{
var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
var thisTime = DateTime.Now;
var delConnection = connectionUserList.Where(x=>(thisTime-x.CreateTime).Duration().TotalHours >= houre).ToList();
if (delConnection != null)
{
foreach (var delItem in delConnection)
{
var Drow = await RedisService.LRemAsync(signalrRedisKey, delItem);
//var connectionUserLists = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
}
}
}
#endregion
}
1.4 SignalrHub.cs?里面所用到的用户链接?UserConnection.cs?类
[Serializable]
public class UserConnection
{
/// <summary>
/// 用户Id
/// </summary>
public int UserId { set; get; }
/// <summary>
/// 用户名称
/// </summary>
public string UserName { set; get; }
/// <summary>
/// 连接Id
/// </summary>
public string ConnectionId { set; get; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { set; get; }
}
作者声明:
1.文章转载请注明出处 !!!?
2.文章 如有不正确之处欢迎大家讨论,交流,?如果感觉写的还行,或者帮助了您,请点个赞哈,再次谢过~
|