Files
FATrace/FATrace.OEMApp/Services/SocketService_使用示例.md
2026-01-13 15:03:02 +08:00

11 KiB
Raw Blame History

SocketService 使用示例

概述

SocketService 是一个基于原生 TcpListener 实现的稳定可靠的 Socket 服务器,支持多客户端同时连接。

主要特性

  • 基于原生 TcpListener稳定可靠
  • 支持多客户端同时连接
  • 使用 CRLF (\r\n) 行分隔符的文本协议
  • UTF-8 编码
  • 详细的 NLog 日志记录
  • 事件驱动架构,方便外部处理业务逻辑
  • 支持单播和广播发送数据

配置参数

// 创建服务实例
var socketService = new SocketService();

// 配置参数(可选,有默认值)
socketService.ListenIp = "127.0.0.1";      // 监听 IP默认 127.0.0.1
socketService.ListenPort = 6001;            // 监听端口,默认 6001
socketService.ReceiveTimeout = 30;          // 接收超时(秒),默认 30
socketService.SendTimeout = 30;             // 发送超时(秒),默认 30

基本使用

1. 启动服务器

// 启动服务器
bool success = await socketService.StartAsync();
if (success)
{
    Console.WriteLine("服务器启动成功");
}
else
{
    Console.WriteLine("服务器启动失败");
}

2. 订阅事件

数据接收事件

socketService.DataReceived += (sender, e) =>
{
    // 处理接收到的数据
    Console.WriteLine($"收到数据SessionId={e.SessionId}, IP={e.RemoteIp}:{e.RemotePort}");
    Console.WriteLine($"数据内容:{e.Data}");
    Console.WriteLine($"接收时间:{e.ReceivedTime}");
    
    // 在这里处理你的业务逻辑
    // 例如:解析数据、保存到数据库、触发其他操作等
};

客户端连接事件

socketService.ClientConnected += (sender, e) =>
{
    Console.WriteLine($"客户端已连接SessionId={e.SessionId}, IP={e.RemoteIp}:{e.RemotePort}");
    Console.WriteLine($"连接时间:{e.ConnectedTime}");
    Console.WriteLine($"当前连接数:{socketService.ConnectedClientCount}");
};

客户端断开事件

socketService.ClientDisconnected += (sender, e) =>
{
    Console.WriteLine($"客户端已断开SessionId={e.SessionId}, IP={e.RemoteIp}:{e.RemotePort}");
    Console.WriteLine($"断开原因:{e.Reason}");
    Console.WriteLine($"断开时间:{e.DisconnectedTime}");
    Console.WriteLine($"当前连接数:{socketService.ConnectedClientCount}");
};

服务器错误事件

socketService.ServerError += (sender, ex) =>
{
    Console.WriteLine($"服务器发生错误:{ex.Message}");
    Console.WriteLine($"错误堆栈:{ex.StackTrace}");
};

3. 发送数据

向指定客户端发送数据

string sessionId = "Session_1_xxx";  // 从事件参数中获取的 SessionId
string data = "Hello Client!";

bool success = await socketService.SendToClientAsync(sessionId, data);
if (success)
{
    Console.WriteLine("数据发送成功");
}

向所有客户端广播数据

string data = "Broadcast Message to All!";

int successCount = await socketService.BroadcastAsync(data);
Console.WriteLine($"广播成功发送到 {successCount} 个客户端");

4. 断开指定客户端

string sessionId = "Session_1_xxx";

bool success = await socketService.DisconnectClientAsync(sessionId);
if (success)
{
    Console.WriteLine("客户端已断开");
}

5. 获取所有已连接客户端

List<string> sessionIds = socketService.GetConnectedSessionIds();
Console.WriteLine($"当前连接的客户端数量:{sessionIds.Count}");
foreach (var sessionId in sessionIds)
{
    Console.WriteLine($"  - {sessionId}");
}

6. 停止服务器

await socketService.StopAsync();
Console.WriteLine("服务器已停止");

完整示例

using FATrace.OEMApp.Services;
using System;
using System.Threading.Tasks;

public class SocketServerExample
{
    private SocketService _socketService;

    public async Task RunAsync()
    {
        // 1. 创建服务实例
        _socketService = new SocketService
        {
            ListenIp = "0.0.0.0",  // 监听所有网卡
            ListenPort = 6001,
            ReceiveTimeout = 30,
            SendTimeout = 30
        };

        // 2. 订阅事件
        _socketService.DataReceived += OnDataReceived;
        _socketService.ClientConnected += OnClientConnected;
        _socketService.ClientDisconnected += OnClientDisconnected;
        _socketService.ServerError += OnServerError;

        // 3. 启动服务器
        bool success = await _socketService.StartAsync();
        if (!success)
        {
            Console.WriteLine("服务器启动失败");
            return;
        }

        Console.WriteLine("服务器已启动,按任意键停止...");
        Console.ReadKey();

        // 4. 停止服务器
        await _socketService.StopAsync();
    }

    private void OnDataReceived(object sender, SocketService.DataReceivedEventArgs e)
    {
        Console.WriteLine($"[数据接收] SessionId={e.SessionId}, 数据={e.Data}");

        // 处理业务逻辑
        // 例如:回复客户端
        _ = _socketService.SendToClientAsync(e.SessionId, $"收到:{e.Data}");
    }

    private void OnClientConnected(object sender, SocketService.ClientConnectedEventArgs e)
    {
        Console.WriteLine($"[客户端连接] SessionId={e.SessionId}, IP={e.RemoteIp}:{e.RemotePort}");

        // 欢迎消息
        _ = _socketService.SendToClientAsync(e.SessionId, "欢迎连接到服务器!");
    }

    private void OnClientDisconnected(object sender, SocketService.ClientDisconnectedEventArgs e)
    {
        Console.WriteLine($"[客户端断开] SessionId={e.SessionId}, 原因={e.Reason}");
    }

    private void OnServerError(object sender, Exception ex)
    {
        Console.WriteLine($"[服务器错误] {ex.Message}");
    }
}

在 WinForms 中使用

public partial class MainApp : Form
{
    private SocketService _socketService;

    private async void MainApp_Load(object sender, EventArgs e)
    {
        // 初始化 Socket 服务
        _socketService = new SocketService
        {
            ListenIp = "127.0.0.1",
            ListenPort = 6001
        };

        // 订阅事件(注意:需要使用 Invoke 更新 UI
        _socketService.DataReceived += (s, args) =>
        {
            this.Invoke(new Action(() =>
            {
                // 更新 UI
                txtLog.AppendText($"收到数据:{args.Data}\r\n");
            }));
        };

        // 启动服务器
        bool success = await _socketService.StartAsync();
        if (success)
        {
            lblStatus.Text = "服务器运行中";
            lblStatus.ForeColor = Color.Green;
        }
    }

    private async void MainApp_FormClosing(object sender, FormClosingEventArgs e)
    {
        // 停止服务器
        if (_socketService != null && _socketService.IsRunning)
        {
            await _socketService.StopAsync();
        }
    }

    private async void btnSendToAll_Click(object sender, EventArgs e)
    {
        // 广播消息
        string message = txtMessage.Text;
        int count = await _socketService.BroadcastAsync(message);
        MessageBox.Show($"已发送到 {count} 个客户端");
    }
}

协议说明

数据格式

  • 编码UTF-8
  • 行分隔符CRLF (\r\n)
  • 每次发送/接收:一行文本数据

客户端示例C#

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;

public class SimpleClient
{
    public static async Task Main()
    {
        using var client = new TcpClient();
        await client.ConnectAsync("127.0.0.1", 6001);
        
        var stream = client.GetStream();
        var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };
        var reader = new StreamReader(stream, Encoding.UTF8);

        // 发送数据
        await writer.WriteLineAsync("Hello Server!");

        // 接收数据
        string response = await reader.ReadLineAsync();
        Console.WriteLine($"服务器回复:{response}");
    }
}

客户端示例Python

import socket

# 连接服务器
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 6001))

# 发送数据(注意:需要添加 \r\n
message = "Hello Server!\r\n"
client.send(message.encode('utf-8'))

# 接收数据
response = client.recv(1024).decode('utf-8')
print(f"服务器回复:{response}")

client.close()

日志说明

SocketService 使用 NLog 记录详细日志,包括:

  • Info 级别:服务器启动/停止、客户端连接/断开、广播完成
  • Debug 级别:数据接收/发送的详细内容
  • Warn 级别:参数错误、会话未找到、连接异常
  • Error 级别:异常堆栈信息

日志示例:

2026-01-01 21:00:00.123 [INFO] 正在启动 Socket 服务器监听地址127.0.0.1:6001超时时间30秒
2026-01-01 21:00:00.456 [INFO] Socket 服务器启动成功监听地址127.0.0.1:6001
2026-01-01 21:00:05.789 [INFO] 客户端已连接SessionId=Session_1_xxx远程地址=127.0.0.1:54321当前连接数=1
2026-01-01 21:00:10.123 [DEBUG] 接收到客户端数据SessionId=Session_1_xxx远程地址=127.0.0.1:54321数据长度=13字符内容=Hello Server!

注意事项

  1. 线程安全:所有公共方法都是线程安全的,可以在多线程环境中使用
  2. 事件处理:事件回调在后台线程执行,如需更新 UI请使用 Invoke
  3. 超时设置:如果客户端长时间无数据交互,会自动断开连接
  4. 数据格式:发送的数据会自动添加 CRLF接收的数据会自动去除 CRLF
  5. 异常处理:所有异常都会被捕获并记录日志,不会导致服务器崩溃

常见问题

Q1: 如何监听所有网卡?

socketService.ListenIp = "0.0.0.0";

Q2: 如何获取客户端的 SessionId

SessionId 会在事件参数中提供(DataReceivedEventArgs.SessionId),你可以保存它用于后续发送数据。

Q3: 如何处理粘包问题?

当前实现使用行分隔符CRLF每次 ReadLineAsync 读取一行完整数据,自动处理粘包。

Q4: 如何实现心跳检测?

可以在客户端定期发送心跳数据,服务器通过 ReceiveTimeout 自动断开无响应的客户端。

Q5: 如何修改协议格式?

如需使用其他协议(如固定长度、带长度头等),需要修改 HandleClientAsync 方法中的数据读取逻辑。

性能建议

  1. 连接数:理论上支持数千个并发连接,实际取决于服务器性能
  2. 数据量:适合中小数据量传输,大文件传输建议使用其他方案
  3. 日志级别:生产环境建议将 Debug 日志关闭,减少性能开销