C# Web及Android应用登录系统在线状态实现方案咨询
C# Web与Android登录系统在线状态最优实现方案
一、C# Web端(基于.NET Authentication + SQL Server)
1. 在线状态维持
- 首先在SQL Server中新增
UserOnlineStatus表,字段建议包含:UserId(与用户表关联,主键+外键)LastActiveTime(UTC时间,记录最后活跃时刻)IsOnline(bit类型,标识在线状态)SessionId(可选,关联ASP.NET Session,用于区分同用户多会话)
- 实现请求中间件,拦截用户的有效请求(排除静态资源、心跳接口等),每次请求时更新当前登录用户的
LastActiveTime为当前UTC时间,并将IsOnline设为true。
中间件代码示例:
public class OnlineStatusMiddleware { private readonly RequestDelegate _next; public OnlineStatusMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, AppDbContext dbContext) { if (context.User.Identity.IsAuthenticated) { var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrWhiteSpace(userId)) { var status = await dbContext.UserOnlineStatus .FirstOrDefaultAsync(s => s.UserId == userId); if (status == null) { status = new UserOnlineStatus { UserId = userId, LastActiveTime = DateTime.UtcNow, IsOnline = true }; dbContext.UserOnlineStatus.Add(status); } else { status.LastActiveTime = DateTime.UtcNow; status.IsOnline = true; } await dbContext.SaveChangesAsync(); } } await _next(context); } } // Program.cs中注册中间件(放在身份认证中间件之后) app.UseAuthentication(); app.UseAuthorization(); app.UseMiddleware<OnlineStatusMiddleware>();
2. 异常退出(死机、关窗)的离线处理
- 定时清理机制:实现.NET后台托管服务,每隔固定时间(比如5分钟)扫描
UserOnlineStatus表,将LastActiveTime超过阈值(比如10分钟,需大于中间件更新间隔)的用户IsOnline设为false。 - Session联动(可选):如果使用InProc模式的Session,可在
Session_End事件中直接标记用户离线;若用StateServer/SQL Server Session,仍需依赖定时任务,因为Session_End不会触发。
后台清理服务代码示例:
public class OnlineStatusCleanupService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; // 离线判定阈值:10分钟未活跃则标记为离线 private readonly TimeSpan _inactiveThreshold = TimeSpan.FromMinutes(10); public OnlineStatusCleanupService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { using var scope = _scopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); var inactiveUsers = await dbContext.UserOnlineStatus .Where(s => s.LastActiveTime < DateTime.UtcNow - _inactiveThreshold && s.IsOnline) .ToListAsync(stoppingToken); foreach (var user in inactiveUsers) { user.IsOnline = false; } await dbContext.SaveChangesAsync(stoppingToken); // 每5分钟执行一次清理 await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); } } } // Program.cs中注册服务 builder.Services.AddHostedService<OnlineStatusCleanupService>();
二、Android端在线状态实现
1. 在线状态维持
- 登录成功后,调用后端接口更新
UserOnlineStatus表,标记用户为在线,并可额外记录DeviceType(Android)、DeviceId(用于区分多设备)。 - 心跳机制:使用WorkManager实现周期性心跳任务,每隔3-5分钟向后端发送轻量请求(比如
POST /api/user/heartbeat),后端收到后更新该用户的LastActiveTime。
Android心跳任务代码示例(Kotlin):
class HeartbeatWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { return try { val token = getSavedAuthToken() // 从SharedPreferences读取登录token if (!token.isNullOrEmpty()) { val apiService = RetrofitClient.create(ApiService::class.java) val response = apiService.sendHeartbeat(token) if (response.isSuccessful) Result.success() else Result.retry() } else { Result.success() // 未登录,终止心跳任务 } } catch (e: Exception) { Result.retry() // 网络异常,重试 } } companion object { fun start(context: Context) { val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val request = PeriodicWorkRequestBuilder<HeartbeatWorker>(3, TimeUnit.MINUTES) .setConstraints(constraints) .build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( "HeartbeatWorker", ExistingPeriodicWorkPolicy.KEEP, request ) } fun stop(context: Context) { WorkManager.getInstance(context).cancelUniqueWork("HeartbeatWorker") } } } // 登录成功后启动心跳:HeartbeatWorker.start(applicationContext) // 主动退出时停止心跳并调用离线接口: // HeartbeatWorker.stop(applicationContext) // apiService.logout(token)
2. 异常离线处理
- 依赖后端的定时清理机制(和Web端共用),当Android端无心跳超过阈值时,自动标记为离线。
- 可选优化:监听应用前后台切换、网络断开事件,在应用退到后台或网络断开时,若网络可用则主动发送离线请求;网络不可用时,待恢复后补传(或直接依赖后端超时)。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

