751 lines
24 KiB
C#
751 lines
24 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using NLog;
|
||
|
||
namespace FATrace.OEMApp.Services
|
||
{
|
||
/// <summary>
|
||
/// Socket 服务器 - 基于原生 TcpListener 实现的多客户端 TCP 服务器
|
||
/// 功能:接收客户端数据、解析为字符串、触发事件通知外部、支持向客户端发送数据
|
||
/// 协议:使用 CRLF (\r\n) 作为行分隔符的文本协议
|
||
/// 编码:UTF-8
|
||
/// </summary>
|
||
public class SocketService
|
||
{
|
||
#region 私有字段
|
||
|
||
/// <summary>
|
||
/// NLog 日志记录器
|
||
/// </summary>
|
||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||
|
||
/// <summary>
|
||
/// TCP 监听器
|
||
/// </summary>
|
||
private TcpListener _listener;
|
||
|
||
/// <summary>
|
||
/// 服务器是否正在运行
|
||
/// </summary>
|
||
private bool _isRunning = false;
|
||
|
||
/// <summary>
|
||
/// 取消令牌源,用于停止服务器
|
||
/// </summary>
|
||
private CancellationTokenSource _cts;
|
||
|
||
/// <summary>
|
||
/// 已连接的客户端字典(SessionId -> ClientSession)
|
||
/// 使用线程安全的 ConcurrentDictionary
|
||
/// </summary>
|
||
private readonly ConcurrentDictionary<string, ClientSession> _clients = new ConcurrentDictionary<string, ClientSession>();
|
||
|
||
/// <summary>
|
||
/// 会话 ID 计数器
|
||
/// </summary>
|
||
private int _sessionIdCounter = 0;
|
||
|
||
#endregion
|
||
|
||
#region 公共属性
|
||
|
||
/// <summary>
|
||
/// 服务器监听 IP 地址
|
||
/// 默认:127.0.0.1(本地回环)
|
||
/// 0.0.0.0 表示监听所有网卡
|
||
/// </summary>
|
||
public string ListenIp { get; set; } = "127.0.0.1";
|
||
|
||
/// <summary>
|
||
/// 服务器监听端口
|
||
/// 默认:6001
|
||
/// </summary>
|
||
public int ListenPort { get; set; } = 6001;
|
||
|
||
/// <summary>
|
||
/// 接收超时时间(秒)
|
||
/// 默认:30 秒
|
||
/// 如果客户端在此时间内无数据交互,连接将被关闭
|
||
/// </summary>
|
||
public int ReceiveTimeout { get; set; } = 30;
|
||
|
||
/// <summary>
|
||
/// 发送超时时间(秒)
|
||
/// 默认:30 秒
|
||
/// </summary>
|
||
public int SendTimeout { get; set; } = 30;
|
||
|
||
/// <summary>
|
||
/// 服务器是否正在运行
|
||
/// </summary>
|
||
public bool IsRunning => _isRunning;
|
||
|
||
/// <summary>
|
||
/// 当前连接的客户端数量
|
||
/// </summary>
|
||
public int ConnectedClientCount
|
||
{
|
||
get
|
||
{
|
||
return _clients.Count;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 事件定义
|
||
|
||
/// <summary>
|
||
/// 数据接收事件参数
|
||
/// </summary>
|
||
public class DataReceivedEventArgs : EventArgs
|
||
{
|
||
/// <summary>
|
||
/// 会话 ID(客户端唯一标识)
|
||
/// </summary>
|
||
public string SessionId { get; set; }
|
||
|
||
/// <summary>
|
||
/// 客户端 IP 地址
|
||
/// </summary>
|
||
public string RemoteIp { get; set; }
|
||
|
||
/// <summary>
|
||
/// 客户端端口
|
||
/// </summary>
|
||
public int RemotePort { get; set; }
|
||
|
||
/// <summary>
|
||
/// 接收到的数据(已解析为字符串)
|
||
/// </summary>
|
||
public string Data { get; set; }
|
||
|
||
/// <summary>
|
||
/// 接收时间
|
||
/// </summary>
|
||
public DateTime ReceivedTime { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 客户端连接事件参数
|
||
/// </summary>
|
||
public class ClientConnectedEventArgs : EventArgs
|
||
{
|
||
/// <summary>
|
||
/// 会话 ID
|
||
/// </summary>
|
||
public string SessionId { get; set; }
|
||
|
||
/// <summary>
|
||
/// 客户端 IP 地址
|
||
/// </summary>
|
||
public string RemoteIp { get; set; }
|
||
|
||
/// <summary>
|
||
/// 客户端端口
|
||
/// </summary>
|
||
public int RemotePort { get; set; }
|
||
|
||
/// <summary>
|
||
/// 连接时间
|
||
/// </summary>
|
||
public DateTime ConnectedTime { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 客户端断开事件参数
|
||
/// </summary>
|
||
public class ClientDisconnectedEventArgs : EventArgs
|
||
{
|
||
/// <summary>
|
||
/// 会话 ID
|
||
/// </summary>
|
||
public string SessionId { get; set; }
|
||
|
||
/// <summary>
|
||
/// 客户端 IP 地址
|
||
/// </summary>
|
||
public string RemoteIp { get; set; }
|
||
|
||
/// <summary>
|
||
/// 客户端端口
|
||
/// </summary>
|
||
public int RemotePort { get; set; }
|
||
|
||
/// <summary>
|
||
/// 断开时间
|
||
/// </summary>
|
||
public DateTime DisconnectedTime { get; set; }
|
||
|
||
/// <summary>
|
||
/// 断开原因
|
||
/// </summary>
|
||
public string Reason { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 数据接收事件
|
||
/// 当接收到客户端数据时触发,外部可订阅此事件进行业务处理
|
||
/// </summary>
|
||
public event EventHandler<DataReceivedEventArgs> DataReceived;
|
||
|
||
/// <summary>
|
||
/// 客户端连接事件
|
||
/// 当有新客户端连接时触发
|
||
/// </summary>
|
||
public event EventHandler<ClientConnectedEventArgs> ClientConnected;
|
||
|
||
/// <summary>
|
||
/// 客户端断开事件
|
||
/// 当客户端断开连接时触发
|
||
/// </summary>
|
||
public event EventHandler<ClientDisconnectedEventArgs> ClientDisconnected;
|
||
|
||
/// <summary>
|
||
/// 服务器错误事件
|
||
/// 当服务器发生错误时触发
|
||
/// </summary>
|
||
public event EventHandler<Exception> ServerError;
|
||
|
||
#endregion
|
||
|
||
#region 公共方法
|
||
|
||
/// <summary>
|
||
/// 启动 Socket 服务器
|
||
/// </summary>
|
||
/// <returns>启动是否成功</returns>
|
||
public async Task<bool> StartAsync()
|
||
{
|
||
try
|
||
{
|
||
// 检查是否已经在运行
|
||
if (_isRunning)
|
||
{
|
||
Logger.Warn("Socket 服务器已经在运行中,无需重复启动");
|
||
return true;
|
||
}
|
||
|
||
Logger.Info($"正在启动 Socket 服务器,监听地址:{ListenIp}:{ListenPort},超时时间:{ReceiveTimeout}秒");
|
||
|
||
// 解析 IP 地址
|
||
IPAddress ipAddress;
|
||
if (!IPAddress.TryParse(ListenIp, out ipAddress))
|
||
{
|
||
Logger.Error($"无效的 IP 地址:{ListenIp}");
|
||
return false;
|
||
}
|
||
|
||
// 创建 TCP 监听器
|
||
_listener = new TcpListener(ipAddress, ListenPort);
|
||
_listener.Start();
|
||
|
||
// 创建取消令牌
|
||
_cts = new CancellationTokenSource();
|
||
|
||
_isRunning = true;
|
||
Logger.Info($"Socket 服务器启动成功,监听地址:{ListenIp}:{ListenPort}");
|
||
|
||
// 启动接受客户端连接的任务
|
||
_ = Task.Run(() => AcceptClientsAsync(_cts.Token), _cts.Token);
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"启动 Socket 服务器时发生异常:{ex.Message}");
|
||
OnServerError(ex);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止 Socket 服务器
|
||
/// </summary>
|
||
/// <returns>停止任务</returns>
|
||
public async Task StopAsync()
|
||
{
|
||
try
|
||
{
|
||
if (!_isRunning || _listener == null)
|
||
{
|
||
Logger.Warn("Socket 服务器未运行,无需停止");
|
||
return;
|
||
}
|
||
|
||
Logger.Info("正在停止 Socket 服务器...");
|
||
|
||
_isRunning = false;
|
||
|
||
// 取消所有异步操作
|
||
_cts?.Cancel();
|
||
|
||
// 停止监听器
|
||
_listener?.Stop();
|
||
|
||
// 断开所有客户端
|
||
foreach (var client in _clients.Values)
|
||
{
|
||
try
|
||
{
|
||
client.TcpClient?.Close();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Warn(ex, $"关闭客户端连接时发生异常:SessionId={client.SessionId}");
|
||
}
|
||
}
|
||
|
||
// 清空客户端字典
|
||
_clients.Clear();
|
||
|
||
// 释放资源
|
||
_cts?.Dispose();
|
||
_cts = null;
|
||
_listener = null;
|
||
|
||
Logger.Info("Socket 服务器已停止");
|
||
|
||
await Task.CompletedTask;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"停止 Socket 服务器时发生异常:{ex.Message}");
|
||
OnServerError(ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 向指定客户端发送数据
|
||
/// </summary>
|
||
/// <param name="sessionId">会话 ID(客户端唯一标识)</param>
|
||
/// <param name="data">要发送的字符串数据</param>
|
||
/// <returns>发送是否成功</returns>
|
||
public async Task<bool> SendToClientAsync(string sessionId, string data)
|
||
{
|
||
try
|
||
{
|
||
// 参数校验
|
||
if (string.IsNullOrWhiteSpace(sessionId))
|
||
{
|
||
Logger.Warn("发送数据失败:SessionId 为空");
|
||
return false;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(data))
|
||
{
|
||
Logger.Warn($"发送数据失败:数据为空,SessionId={sessionId}");
|
||
return false;
|
||
}
|
||
|
||
// 查找会话
|
||
if (!_clients.TryGetValue(sessionId, out var client))
|
||
{
|
||
Logger.Warn($"发送数据失败:未找到会话,SessionId={sessionId}");
|
||
return false;
|
||
}
|
||
|
||
// 发送数据(自动添加 CRLF 行结束符)
|
||
var dataToSend = data.EndsWith("\r\n") ? data : data + "\r\n";
|
||
var buffer = Encoding.UTF8.GetBytes(dataToSend);
|
||
|
||
await client.Stream.WriteAsync(buffer, 0, buffer.Length);
|
||
await client.Stream.FlushAsync();
|
||
|
||
Logger.Debug($"向客户端发送数据成功,SessionId={sessionId},数据长度={buffer.Length}字节,内容={data}");
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"向客户端发送数据时发生异常:SessionId={sessionId},错误={ex.Message}");
|
||
OnServerError(ex);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 向所有已连接的客户端广播数据
|
||
/// </summary>
|
||
/// <param name="data">要广播的字符串数据</param>
|
||
/// <returns>成功发送的客户端数量</returns>
|
||
public async Task<int> BroadcastAsync(string data)
|
||
{
|
||
try
|
||
{
|
||
if (string.IsNullOrEmpty(data))
|
||
{
|
||
Logger.Warn("广播数据失败:数据为空");
|
||
return 0;
|
||
}
|
||
|
||
// 获取所有客户端
|
||
var clientList = _clients.Values.ToList();
|
||
|
||
if (clientList.Count == 0)
|
||
{
|
||
Logger.Debug("广播数据:当前无已连接的客户端");
|
||
return 0;
|
||
}
|
||
|
||
// 准备数据
|
||
var dataToSend = data.EndsWith("\r\n") ? data : data + "\r\n";
|
||
var buffer = Encoding.UTF8.GetBytes(dataToSend);
|
||
|
||
// 向所有客户端发送
|
||
int successCount = 0;
|
||
foreach (var client in clientList)
|
||
{
|
||
try
|
||
{
|
||
await client.Stream.WriteAsync(buffer, 0, buffer.Length);
|
||
await client.Stream.FlushAsync();
|
||
successCount++;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"向客户端广播数据失败:SessionId={client.SessionId},错误={ex.Message}");
|
||
}
|
||
}
|
||
|
||
Logger.Info($"广播数据完成,成功发送到 {successCount}/{clientList.Count} 个客户端,数据长度={buffer.Length}字节");
|
||
return successCount;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"广播数据时发生异常:{ex.Message}");
|
||
OnServerError(ex);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 断开指定客户端的连接
|
||
/// </summary>
|
||
/// <param name="sessionId">会话 ID</param>
|
||
/// <returns>断开是否成功</returns>
|
||
public async Task<bool> DisconnectClientAsync(string sessionId)
|
||
{
|
||
try
|
||
{
|
||
if (string.IsNullOrWhiteSpace(sessionId))
|
||
{
|
||
Logger.Warn("断开客户端失败:SessionId 为空");
|
||
return false;
|
||
}
|
||
|
||
if (!_clients.TryGetValue(sessionId, out var client))
|
||
{
|
||
Logger.Warn($"断开客户端失败:未找到会话,SessionId={sessionId}");
|
||
return false;
|
||
}
|
||
|
||
client.TcpClient?.Close();
|
||
_clients.TryRemove(sessionId, out _);
|
||
Logger.Info($"已主动断开客户端连接,SessionId={sessionId}");
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"断开客户端连接时发生异常:SessionId={sessionId},错误={ex.Message}");
|
||
OnServerError(ex);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有已连接客户端的会话 ID 列表
|
||
/// </summary>
|
||
/// <returns>会话 ID 列表</returns>
|
||
public List<string> GetConnectedSessionIds()
|
||
{
|
||
return _clients.Keys.ToList();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 私有方法 - 客户端处理
|
||
|
||
/// <summary>
|
||
/// 接受客户端连接的循环
|
||
/// </summary>
|
||
private async Task AcceptClientsAsync(CancellationToken cancellationToken)
|
||
{
|
||
Logger.Info("开始接受客户端连接...");
|
||
|
||
while (!cancellationToken.IsCancellationRequested && _isRunning)
|
||
{
|
||
try
|
||
{
|
||
// 等待客户端连接
|
||
var tcpClient = await _listener.AcceptTcpClientAsync();
|
||
|
||
// 生成会话 ID
|
||
var sessionId = $"Session_{Interlocked.Increment(ref _sessionIdCounter)}_{DateTime.Now.Ticks}";
|
||
|
||
// 获取客户端信息
|
||
var remoteEndPoint = tcpClient.Client.RemoteEndPoint as IPEndPoint;
|
||
var remoteIp = remoteEndPoint?.Address.ToString() ?? "Unknown";
|
||
var remotePort = remoteEndPoint?.Port ?? 0;
|
||
|
||
// 创建客户端会话
|
||
var clientSession = new ClientSession
|
||
{
|
||
SessionId = sessionId,
|
||
TcpClient = tcpClient,
|
||
Stream = tcpClient.GetStream(),
|
||
RemoteIp = remoteIp,
|
||
RemotePort = remotePort,
|
||
ConnectedTime = DateTime.Now
|
||
};
|
||
|
||
// 添加到客户端字典
|
||
if (_clients.TryAdd(sessionId, clientSession))
|
||
{
|
||
Logger.Info($"客户端已连接,SessionId={sessionId},远程地址={remoteIp}:{remotePort},当前连接数={ConnectedClientCount}");
|
||
|
||
// 触发客户端连接事件
|
||
OnClientConnected(new ClientConnectedEventArgs
|
||
{
|
||
SessionId = sessionId,
|
||
RemoteIp = remoteIp,
|
||
RemotePort = remotePort,
|
||
ConnectedTime = clientSession.ConnectedTime
|
||
});
|
||
|
||
// 启动处理客户端数据的任务
|
||
_ = Task.Run(() => HandleClientAsync(clientSession, cancellationToken), cancellationToken);
|
||
}
|
||
else
|
||
{
|
||
Logger.Warn($"添加客户端会话失败,SessionId={sessionId}");
|
||
tcpClient.Close();
|
||
}
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// 监听器已被释放,正常退出
|
||
Logger.Info("监听器已关闭,停止接受新连接");
|
||
break;
|
||
}
|
||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.Interrupted)
|
||
{
|
||
// 监听被中断,正常退出
|
||
Logger.Info("监听被中断,停止接受新连接");
|
||
break;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"接受客户端连接时发生异常:{ex.Message}");
|
||
OnServerError(ex);
|
||
await Task.Delay(1000, cancellationToken); // 等待一秒后重试
|
||
}
|
||
}
|
||
|
||
Logger.Info("停止接受客户端连接");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理单个客户端的数据接收
|
||
/// </summary>
|
||
private async Task HandleClientAsync(ClientSession client, CancellationToken cancellationToken)
|
||
{
|
||
var sessionId = client.SessionId;
|
||
var stream = client.Stream;
|
||
var reader = new StreamReader(stream, Encoding.UTF8);
|
||
|
||
Logger.Debug($"开始处理客户端数据,SessionId={sessionId}");
|
||
|
||
try
|
||
{
|
||
// 设置读取超时
|
||
stream.ReadTimeout = ReceiveTimeout * 1000;
|
||
|
||
while (!cancellationToken.IsCancellationRequested && _isRunning)
|
||
{
|
||
try
|
||
{
|
||
// 读取一行数据(以 CRLF 或 LF 结尾)
|
||
var line = await reader.ReadLineAsync();
|
||
|
||
// 如果读取到 null,表示连接已关闭
|
||
if (line == null)
|
||
{
|
||
Logger.Info($"客户端连接已关闭,SessionId={sessionId}");
|
||
break;
|
||
}
|
||
|
||
// 记录接收日志
|
||
Logger.Debug($"接收到客户端数据,SessionId={sessionId},远程地址={client.RemoteIp}:{client.RemotePort},数据长度={line.Length}字符,内容={line}");
|
||
|
||
// 触发数据接收事件
|
||
OnDataReceived(new DataReceivedEventArgs
|
||
{
|
||
SessionId = sessionId,
|
||
RemoteIp = client.RemoteIp,
|
||
RemotePort = client.RemotePort,
|
||
Data = line,
|
||
ReceivedTime = DateTime.Now
|
||
});
|
||
}
|
||
catch (IOException ex) when (ex.InnerException is SocketException socketEx)
|
||
{
|
||
// 连接被重置或超时
|
||
Logger.Warn($"客户端连接异常,SessionId={sessionId},错误={socketEx.SocketErrorCode}");
|
||
break;
|
||
}
|
||
catch (IOException ex)
|
||
{
|
||
// 读取超时或其他 IO 异常
|
||
Logger.Warn(ex, $"读取客户端数据时发生 IO 异常,SessionId={sessionId}");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"处理客户端数据时发生异常,SessionId={sessionId},错误={ex.Message}");
|
||
OnServerError(ex);
|
||
}
|
||
finally
|
||
{
|
||
// 清理客户端连接
|
||
try
|
||
{
|
||
client.TcpClient?.Close();
|
||
_clients.TryRemove(sessionId, out _);
|
||
|
||
Logger.Info($"客户端已断开,SessionId={sessionId},远程地址={client.RemoteIp}:{client.RemotePort},当前连接数={ConnectedClientCount}");
|
||
|
||
// 触发客户端断开事件
|
||
OnClientDisconnected(new ClientDisconnectedEventArgs
|
||
{
|
||
SessionId = sessionId,
|
||
RemoteIp = client.RemoteIp,
|
||
RemotePort = client.RemotePort,
|
||
DisconnectedTime = DateTime.Now,
|
||
Reason = "连接关闭"
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Warn(ex, $"清理客户端连接时发生异常,SessionId={sessionId}");
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 私有方法 - 事件触发
|
||
|
||
/// <summary>
|
||
/// 触发数据接收事件
|
||
/// </summary>
|
||
private void OnDataReceived(DataReceivedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
DataReceived?.Invoke(this, e);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"触发 DataReceived 事件时发生异常:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发客户端连接事件
|
||
/// </summary>
|
||
private void OnClientConnected(ClientConnectedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
ClientConnected?.Invoke(this, e);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"触发 ClientConnected 事件时发生异常:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发客户端断开事件
|
||
/// </summary>
|
||
private void OnClientDisconnected(ClientDisconnectedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
ClientDisconnected?.Invoke(this, e);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(ex, $"触发 ClientDisconnected 事件时发生异常:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发服务器错误事件
|
||
/// </summary>
|
||
private void OnServerError(Exception ex)
|
||
{
|
||
try
|
||
{
|
||
ServerError?.Invoke(this, ex);
|
||
}
|
||
catch (Exception eventEx)
|
||
{
|
||
Logger.Error(eventEx, $"触发 ServerError 事件时发生异常:{eventEx.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 内部类 - 客户端会话
|
||
|
||
/// <summary>
|
||
/// 客户端会话信息
|
||
/// </summary>
|
||
private class ClientSession
|
||
{
|
||
/// <summary>
|
||
/// 会话 ID
|
||
/// </summary>
|
||
public string SessionId { get; set; }
|
||
|
||
/// <summary>
|
||
/// TCP 客户端
|
||
/// </summary>
|
||
public TcpClient TcpClient { get; set; }
|
||
|
||
/// <summary>
|
||
/// 网络流
|
||
/// </summary>
|
||
public NetworkStream Stream { get; set; }
|
||
|
||
/// <summary>
|
||
/// 远程 IP 地址
|
||
/// </summary>
|
||
public string RemoteIp { get; set; }
|
||
|
||
/// <summary>
|
||
/// 远程端口
|
||
/// </summary>
|
||
public int RemotePort { get; set; }
|
||
|
||
/// <summary>
|
||
/// 连接时间
|
||
/// </summary>
|
||
public DateTime ConnectedTime { get; set; }
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|