Add XCode skills for entity caching, ORM, and sharding ETL
|
---
name: network-server-sessions
description: >
使用 NewLife.Net 构建高性能网络æœåŠ¡å™¨ï¼Œæ¶µç›– NetServer/NetSession 生命周期管ç†ã€
管é“ç¼–è§£ç é…ç½®ã€TCP/UDP å议选择ã€SSL/TLSã€ä¼šè¯æ•°æ®å˜å‚¨ã€å¼‚æ¥è¯·æ±‚-å“应,
ä»¥åŠæ³›åž‹å¼ºç±»åž‹ä¼šè¯è®¾è®¡ã€‚é€‚ç”¨äºŽå³æ—¶é€šè®¯ã€æ¸¸æˆæœåС噍ã€IoT æ•°æ®é‡‡é›†ã€è‡ªå®šä¹‰åè®®æœåŠ¡ç‰åœºæ™¯ã€‚
argument-hint: >
è¯´æ˜Žä½ çš„ç½‘ç»œæœåŠ¡åœºæ™¯ï¼šTCP 还是 UDP;是å¦éœ€è¦è‡ªå®šä¹‰æ¶ˆæ¯å议(粘包处ç†ï¼‰ï¼›
是å¦éœ€è¦è‡ªå®šä¹‰ä¼šè¯ç±»åž‹ï¼ˆæŒæœ‰ä¸šåŠ¡çŠ¶æ€ï¼‰ï¼›æ˜¯å¦éœ€è¦ SSL/TLSï¼›
是å¦éœ€è¦å¼‚æ¥è¯·æ±‚-å“应模å¼ï¼ˆSendMessageAsync)。
---
# 网络æœåŠ¡å™¨ä¸Žä¼šè¯ç®¡ç†æŠ€èƒ½
## 适用场景
- 构建自定义 TCP/UDP æœåŠ¡å™¨ï¼Œéœ€è¦ç®¡ç†å®¢æˆ·ç«¯è¿žæŽ¥ç”Ÿå‘½å‘¨æœŸï¼ˆè¿žæŽ¥/æ–å¼€/æ•°æ®æ”¶å‘)。
- 需è¦ä¸ºæ¯ä¸ªè¿žæŽ¥ç»´æŠ¤ä¸šåŠ¡çŠ¶æ€ï¼ˆç™»å½•ä¿¡æ¯ã€ç”¨æˆ· IDã€ä¼šè¯ Token ç‰ï¼‰â€”— 自定义 `NetSession`。
- 需è¦åšå议编解ç (粘包处ç†ã€JSON 消æ¯ã€äºŒè¿›åˆ¶å议)—— é…ç½®ç®¡é“ `Pipeline`。
- 需è¦å¼‚æ¥è¯·æ±‚-å“应模å¼ï¼ˆå‘逿¶ˆæ¯å¹¶ç‰å¾…特定å“应)—— `SendMessageAsync`。
- 代ç å®¡æŸ¥ï¼šæ£€æŸ¥ä¼šè¯æ˜¯å¦åŠæ—¶é‡Šæ”¾ã€ç®¡é“处ç†å™¨é¡ºåºæ˜¯å¦æ£ç¡®ã€ä¼šè¯æ•°æ®å˜å‚¨æ˜¯å¦çº¿ç¨‹å®‰å…¨ã€‚
## æ ¸å¿ƒåŽŸåˆ™
1. **æ¯è¿žæŽ¥ç‹¬ç«‹ä¼šè¯**:æ¯ä¸ªå®¢æˆ·ç«¯è¿žæŽ¥ç”±ä¸€ä¸ª `INetSession` 实例管ç†ï¼›æœ‰å†…部状æ€çš„处ç†å™¨ï¼ˆå¦‚拆包缓冲区)必须为æ¯ä¸ªè¿žæŽ¥ç‹¬ç«‹åˆ›å»ºã€‚
2. **管é“负责编解ç **:业务层接收到的 `e.Message` 是ç»è¿‡ç®¡é“è§£ç åŽçš„对象;å‘逿—¶ `SendMessage` ç»ç®¡é“ç¼–ç åŽæ‰å†™å…¥ Socket。
3. **事件 vs 继承**ï¼šç®€å•æœåŠ¡ç”¨ `server.Received += ...`ï¼ˆäº‹ä»¶ï¼‰ï¼›å¤æ‚业务逻辑继承 `NetSession` 并覆写 `OnReceive/OnConnected/OnDisconnected`。
4. **泛型æœåС噍æä¾›å¼ºç±»åž‹è®¿é—®**:`NetServer<MySession>` + `NetSession<MyServer>` 使会è¯å†…通过 `Host.SomeProperty` 访问自定义æœåŠ¡å™¨å±žæ€§ï¼Œæ— éœ€å¼ºåˆ¶è½¬åž‹ã€‚
5. **生命周期顺åº**:`OnConnected → æ•°æ®æ”¶å‘循环 → OnDisconnected → Dispose`;覆写生命周期方法必须调用 `base.*`。
## æ•°æ®æµ
```
接收:Socket → ProcessEvent → OnPreReceive → Pipeline.Read → OnReceive/Received事件 → 业务层
å‘é€ï¼šä¸šåС层.SendMessage → Pipeline.Write → OnSend → Socketå‘é€
```
## 执行æ¥éª¤
### ä¸€ã€æœ€ç®€ Echo æœåС噍
```csharp
var server = new NetServer
{
Name = "EchoServer",
Port = 12345,
Log = XTrace.Log,
};
server.Received += (sender, e) =>
{
if (sender is INetSession session)
session.Send(e.Packet); // Echo åŽŸæ ·å›žå¤
};
server.Start();
// server.Stop("Shutdown"); // åœæ¢æ—¶
```
### 二ã€è‡ªå®šä¹‰ä¼šè¯ï¼ˆæŽ¨è夿‚业务)
```csharp
public class GameSession : NetSession<GameServer>
{
public Int32 PlayerId { get; set; }
public String? Nickname { get; set; }
protected override void OnConnected()
{
base.OnConnected();
WriteLog("玩家连接:{0}", Remote);
Send("Welcome!");
}
protected override void OnDisconnected(String reason)
{
base.OnDisconnected(reason);
WriteLog("玩家æ–开:{0} åŽŸå› ï¼š{1}", Remote, reason);
// 清ç†çŽ©å®¶æˆ¿é—´æ•°æ®ç‰
}
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
var msg = e.Message as GameRequest; // ç»ç®¡é“è§£ç åŽçš„æ¶ˆæ¯å¯¹è±¡
if (msg == null) return;
// 访问æœåŠ¡å™¨è‡ªå®šä¹‰å±žæ€§ï¼ˆå¼ºç±»åž‹ï¼Œæ— å¼ºåˆ¶è½¬åž‹ï¼‰
var db = Host.Database;
ProcessCommand(msg, db);
}
}
public class GameServer : NetServer<GameSession>
{
public IDatabase Database { get; set; } // 注入到所有会è¯
protected override void OnStart()
{
base.OnStart();
// 注册管é“ç¼–è§£ç 器(按æ£å‘处ç†é¡ºåºï¼‰
Add(new LengthFieldCodec { Size = 4 });
Add(new MessageCodec<GameRequest>());
}
}
```
### 三ã€é…ç½®æœåС噍傿•°
```csharp
var server = new GameServer
{
Port = 9000,
ProtocolType = NetType.Tcp, // ä»… TCPï¼›Udp / Unknownï¼ˆåŒæ—¶ç›‘å¬ï¼‰
AddressFamily = AddressFamily.Unspecified, // åŒæ—¶ IPv4 + IPv6
SessionTimeout = 600, // 会è¯è¶…时秒数(默认 1200)
SslProtocol = SslProtocols.Tls12, // å¯ç”¨ TLS
Certificate = cert, // X509 è¯ä¹¦
Tracer = DefaultTracer.Instance, // APM 链路追踪
ServiceProvider = serviceProvider, // 注入 DI 容器
};
```
### å››ã€é…置编解ç 器管é“
```csharp
// 简å•内置方案(4å—节头部长度 + æ ‡å‡†æ¶ˆæ¯ï¼‰
server.Add<StandardCodec>();
// 或 JSON ç¼–è§£ç 器
server.Add<JsonCodec>();
// 或自定义粘包处ç†é“¾
server.Add(new LengthFieldCodec { Size = 2 }); // 2å—节头部
server.Add(new MyProtocolCodec()); // 自定义消æ¯è§£æž
```
### 五ã€å‘逿•°æ®çš„å¤šç§æ–¹å¼
```csharp
// 在 NetSession åç±»ä¸
Send(bytes); // å—节数组(原始包)
Send("æ¶ˆæ¯æ–‡æœ¬"); // å—符串(UTF-8)
SendMessage(new GameResponse { Code = 0 }); // ç»ç®¡é“ç¼–ç åŽå‘é€ï¼ˆåŒæ¥ï¼‰
var resp = await SendMessageAsync(request); // 异æ¥å‘é€å¹¶ç‰å¾…å…³è”å“应
SendReply(response, receivedEventArgs); // 回å¤å½“å‰è¯·æ±‚(关è”原请求上下文)
```
### å…ã€ä¼šè¯å˜å‚¨ä¸Žç®¡ç†
```csharp
// 会è¯å†…å˜å‚¨æ•°æ®
this["userId"] = 12345;
var userId = (Int32)this["userId"];
// 获å–全部会è¯
foreach (var kv in server.Sessions)
Console.WriteLine($"[{kv.Key}] {kv.Value.Remote}");
// èŽ·å–æŒ‡å®š ID 的会è¯ï¼ˆå¼ºç±»åž‹ï¼‰
var session = server.GetSession(sessionId) as GameSession;
session?.Send("Server push message");
// 查看当å‰/历å²å³°å€¼ä¼šè¯æ•°
Console.WriteLine($"å½“å‰ {server.SessionCount} / 峰值 {server.MaxSessionCount}");
```
## é‡ç‚¹æ£€æŸ¥é¡¹
- [ ] 有状æ€çš„管é“处ç†å™¨ï¼ˆæŒæœ‰æ‹†åŒ…缓冲区)是å¦åœ¨ `NewSession` äº‹ä»¶ä¸æ¯æ¬¡ `new` 一个新实例?
- [ ] `OnConnected`/`OnDisconnected`/`OnReceive` 覆写是å¦éƒ½è°ƒç”¨äº† `base.*`?
- [ ] 会è¯è¶…时(`SessionTimeout`)是å¦è®¾ç½®åˆç†ï¼ˆé¿å…僵尸连接å 用资æºï¼‰ï¼Ÿ
- [ ] `SendMessageAsync` æ˜¯å¦æœ‰è¶…时(`CancellationToken`),é¿å…æ— é™ç‰å¾…?
- [ ] æœåŠ¡å™¨åœæ¢ï¼ˆ`Stop`)时,是å¦ç‰å¾…所有活跃会è¯ä¼˜é›…退出,还是强制æ–链?
- [ ] SSL è¯ä¹¦æ˜¯å¦åœ¨åº”用å¯åŠ¨æ—¶åŠ è½½å¹¶éªŒè¯ï¼ˆè€Œéžæ¯è¿žæŽ¥é‡æ–°è¯»æ–‡ä»¶ï¼‰ï¼Ÿ
## è¾“å‡ºè¦æ±‚
- **接å£**:`INetSession`ï¼ˆä¼šè¯æŽ¥å£ï¼‰ã€`INetHandler`(处ç†å™¨æŽ¥å£ï¼‰ã€`ISocketServer`(底层æœåŠ¡å™¨ï¼‰ã€‚
- **基类**:`NetServer`/`NetServer<TSession>`(æœåŠ¡å™¨ï¼‰ã€`NetSession`/`NetSession<TServer>`(会è¯ï¼‰ã€‚
- **内置编解ç 器**:`LengthFieldCodec`(粘包拆包)ã€`StandardCodec`ï¼ˆé»˜è®¤æ¶ˆæ¯æ ¼å¼ï¼‰ã€`JsonCodec`(JSON ç¼–è§£ç )。
- **测试**:å¯åœ¨ loopback 地å€å¯åЍæœåŠ¡ï¼Œç”¨ `NetUri.CreateRemote()` 创建测试客户端验è¯è¡Œä¸ºã€‚
## å‚考资料
å‚考示例与模å¼è¯æ®è§ `references/newlife-netserver-patterns.md`。
|