stevenjin 2024-09-07 17:08 采纳率: 98%
浏览 5
已结题

websocket每次刷新网页会重新连接一次客户端

以下是用signalR通讯的一个例子,有个问题就是每次刷新网页会重新连接一次客户端,求解决办法:



//服务端
try
{
    var builder = WebApplication.CreateBuilder(args);
   
  
   
    //跨域服务配置
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("Cors", p =>
        {
            p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
        });
    });
    
 
    builder.Services.AddControllersWithViews().AddRazorRuntimeCompilation();
  
    builder.Services.AddSignalR();//添加SignalR服务,需要添加到AddRazor后 
    builder.Services.AddSingleton<SocketConnectionManage>();
    builder.Logging.ClearProviders();
    builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Error);
    builder.Host.UseNLog();
 
    //builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
 
    builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
    builder.Services.AddControllers().AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
         
    });
    builder.Services.AddControllers().AddNewtonsoftJson(option =>
    {       
        option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
    });
 
 
 
 
   
    
    
 
 
    app.Urls.Add("http://*:8080"); 
    app.UseCors("Cors"); 
    app.UseStaticFiles();
 
  
    app.UseRouting();
 
    app.UseAuthentication();//身份认证必须在静态文件之后,路由之后
 
    app.UseAuthorization();//身份授权
 
    app.UseResponseCaching();//启用服务器缓存 
 
    app.MapHub<MessagePushHub>("/messagePushHub");//启用SignalR服务
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    app.Run();
    NLogHelper.Trace("应用程序已运行");
}
 
catch(Exception exception)
{  
    NLogHelper.Error("Stopped program because of exception", exception);   
    throw;
}
finally
{   
    NLog.LogManager.Shutdown();
}
 
public class MessagePushHub : Hub
{
    private readonly SocketConnectionManage _onlineSocketClient;
    public MessagePushHub(SocketConnectionManage onlineSocketClient)
    {
        this._onlineSocketClient = onlineSocketClient;
    }
    public async Task onOpen(string userId)
    {
        string connectionId = Context.ConnectionId;
        _onlineSocketClient.AddSocket(userId, connectionId);
        Console.WriteLine($"客户端{userId},连接成功");
        await Task.CompletedTask;
    }
    public async Task onMessage(string message)
    {
        string connectionId = Context.ConnectionId;
        string userId = _onlineSocketClient.GetUserIdByConnectionId(connectionId);
        Console.WriteLine($"[服务端]=>接收到客户端{userId},发送的数据为:{message}");
        await this.Clients.Client(connectionId).SendAsync("ReceivePushMessage", message);
        await Task.CompletedTask;
    }
 
    /// <summary>
    /// 客户端连接成功
    /// </summary>
    /// <returns></returns>
    public override Task OnConnectedAsync()
    {
        return base.OnConnectedAsync();
    }
 
    /// <summary>
    /// 客户端断开
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    public override Task OnDisconnectedAsync(Exception exception)
    {
        string connectionId = Context.ConnectionId;
        string userId = _onlineSocketClient.GetUserIdByConnectionId(connectionId);
        Console.WriteLine($"客户端{userId},断开连接");
        _onlineSocketClient.RemoveSocket(userId);
        return base.OnDisconnectedAsync(exception);
    }
}
 
public class SocketConnectionManage
{
    private static ConcurrentDictionary<string, string> _sockets = new ConcurrentDictionary<string, string>();
    public void AddSocket(string userId, string connectionId)
    {
        _sockets.TryAdd(userId, connectionId);
    }
    public ConcurrentDictionary<string, string> GetClients()
    {
        return _sockets;
    }
    public void RemoveSocket(string userId)
    {
        if (IsExistsUserId(userId))
        {
            _sockets.TryRemove(userId, out var value);
        }
    }
    public string GetConnectionIdByUserId(string userId)
    {
        if (IsExistsUserId(userId))
        {
            string connectionId = _sockets.FirstOrDefault(t => t.Key == userId).Value;
            return connectionId;
        }
        return string.Empty;
    }
    public string GetUserIdByConnectionId(string connectionId)
    {
        if (IsExistsConnectionId(connectionId))
        {
            string userId = _sockets.FirstOrDefault(t => t.Value == connectionId).Key;
            return userId;
        }
        return string.Empty;
    }
    public bool IsExistsUserId(string userId)
    {
        return _sockets.Select(s => s.Key).ToList().Exists(t => t == userId);
    }
    public bool IsExistsConnectionId(string connectionId)
    {
        return _sockets.Select(s => s.Value).ToList().Exists(t => t == connectionId);
    }
}
 
//客户端
"use strict";
 
var connection = new signalR.HubConnectionBuilder()
                                                .withUrl("http://localhost:5000/messagePushHub")
                                                .withAutomaticReconnect()
                                                .build();
 
//Disable send button until connection is established
document.getElementById("btnLogin").disabled = true;
 
connection.start().then(function () {
    document.getElementById("btnLogin").disabled = false;
}).catch(function (err) {
    return console.error(err.toString());
});
 
connection.onreconnecting(error => {
    console.log("断线重连中...");
});
 
connection.onreconnected(connectionId => {
    console.log("重连成功...");
 
    //重新绑定用户连接
    var txtUserId = document.getElementById("txtUserId").value;
    connection.invoke("onOpen", txtUserId).then(function () {
    }).catch(function (err) {
        return console.error(err.toString());
    });
 
});
 
connection.on("ReceivePushMessage", function (msg) {
    var encodedMsg = "服务端=>向客户端推送的消息为:"+msg;
    var li = document.createElement("li");
    li.textContent = encodedMsg;
    document.getElementById("messagesList").appendChild(li);
});
 
document.getElementById("btnLogin").addEventListener("click", function (event) {
    var txtUserId = document.getElementById("txtUserId").value;
    connection.invoke("onOpen", txtUserId).then(function () {
        var li = document.createElement("li");
        li.textContent = "连接成功";
        document.getElementById("messagesList").appendChild(li);
        document.getElementById("btnLogin").disabled = true;
    }).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});
 
document.getElementById("btnSendMessage").addEventListener("click", function (event) {
    var messageInput = document.getElementById("messageInput").value;
    connection.invoke("onMessage", messageInput).catch(function (err) {
        return console.error(err.toString());
    });
    var li = document.createElement("li");
    li.textContent = "客户端=>向服务端发送的消息为:"+messageInput;
    document.getElementById("messagesList").appendChild(li);
    event.preventDefault();
});
  • 写回答

2条回答 默认 最新

  • Unity打怪升级 2024-09-07 18:01
    关注

    SignalR 客户端在每次刷新网页时重新连接是正常的行为,因为每次刷新都意味着创建了一个新的浏览器环境,所以需要重新建立与服务器的连接。如果你想要避免每次刷新都重新连接,你可以考虑以下几种方法:

    1. 使用持久化连接

      • 确保你的 SignalR 服务端和客户端都支持持久化连接。这通常意味着使用 WebSockets 或 Server-Sent Events,这两种技术都可以在页面刷新后保持连接。
    2. 页面缓存

      • 考虑使用浏览器的缓存机制来避免不必要的刷新。例如,你可以在客户端使用 localStoragesessionStorage 来存储连接状态,并在页面加载时检查这些状态。
    3. 单页应用(SPA)

      • 如果你的应用是一个单页应用,你可以通过前端路由控制来避免全页面刷新。这样,用户在应用内部导航时不会触发页面刷新,从而保持 SignalR 连接。
    4. 自定义重连逻辑

      • SignalR 允许你自定义重连逻辑。你可以在客户端设置一个标志,用来指示是否已经连接过。如果已经连接过,你可以在重连时跳过某些初始化步骤。
    5. 服务端会话保持

      • 在服务端,你可以使用会话或其他持久化机制来跟踪客户端的状态。这样,即使客户端重新连接,服务端也可以识别出客户端,并恢复之前的状态。
    6. 避免不必要的刷新

      • 考虑用户体验,避免在用户与应用交互时进行不必要的页面刷新。例如,使用 AJAX 请求来更新页面的特定部分,而不是刷新整个页面。
    7. 使用服务端推送

      • 如果你的应用场景允许,可以考虑使用服务端推送技术,如 WebSockets,这样即使在页面不刷新的情况下,服务端也可以主动推送消息到客户端。

    在你的代码中,你已经使用了 withAutomaticReconnect() 方法,这会在连接断开时自动尝试重新连接。如果你想要进一步控制重连行为,你可以自定义重连策略,例如:

    var connection = new signalR.HubConnectionBuilder()
        .withUrl("http://localhost:5000/messagePushHub")
        .configureAutomaticReconnect(function () {
            return [0, 2000, 5000, 10000]; // 初始延迟0ms,之后是2秒、5秒、10秒
        })
        .build();
    

    这样,你可以更精细地控制重连的尝试间隔。但是,请注意,这并不会阻止每次刷新页面时的重新连接,它只是改变了断开连接后的重连行为。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 9月15日
  • 已采纳回答 9月7日
  • 修改了问题 9月7日
  • 创建了问题 9月7日

悬赏问题

  • ¥15 电视版的优酷可以设置电影连续播放吗?
  • ¥50 复现论文;matlab代码编写
  • ¥30 echarts 3d地图怎么实现一进来页面散点数据和卡片一起轮播
  • ¥15 数字图像的降噪滤波增强
  • ¥15 心碎了,为啥我的神经网络训练的时候第二个批次反向传播会报错呀,第一个批次都没有问题
  • ¥15 MSR2680-XS路由器频繁卡顿问题
  • ¥15 VB6可以成功读取的文件,用C#读不了
  • ¥15 如何使用micpyhon解析Modbus RTU返回指定站号的湿度值,并确保正确?
  • ¥15 C++ 句柄后台鼠标拖动如何实现
  • ¥15 有人会SIRIUS 5.8.0这个软件吗