v9.10.2019.0101 全面巩固批量Insert/Update/Upsert,支持数据备份、恢复和同步,支持实体列表保存到文件以及加载
|
# NewLife.Net 网络库使用手册
## 目录
- [概述](/NewLife/X/Blob/dev/Doc/#概述)
- [架构设计](/NewLife/X/Blob/dev/Doc/#架构设计)
- [快速入门](/NewLife/X/Blob/dev/Doc/#快速入门)
- [æ ¸å¿ƒç»„ä»¶](/NewLife/X/Blob/dev/Doc/#æ ¸å¿ƒç»„ä»¶)
- [高级特性](/NewLife/X/Blob/dev/Doc/#高级特性)
- [最佳实践](/NewLife/X/Blob/dev/Doc/#最佳实践)
- [性能优化](/NewLife/X/Blob/dev/Doc/#性能优化)
- [常è§é—®é¢˜](/NewLife/X/Blob/dev/Doc/#常è§é—®é¢˜)
---
## 概述
NewLife.Net 是新生命团队开å‘çš„é«˜æ€§èƒ½ç½‘ç»œé€šä¿¡åº“ï¼Œæ”¯æŒ TCP/UDP åè®®ï¼ŒåŒæ—¶å…¼å®¹ IPv4 å’Œ IPv6。该库设计精良,久ç»ç”Ÿäº§çŽ¯å¢ƒè€ƒéªŒï¼Œé€‚ç”¨äºŽæž„å»ºå„ç§ç½‘络应用æœåŠ¡å™¨ã€‚
### 特性
- **多å议支æŒ**ï¼šåŒæ—¶æ”¯æŒ TCP å’Œ UDP åè®®
- **åŒæ ˆæ”¯æŒ**ï¼šå®Œç¾Žæ”¯æŒ IPv4 å’Œ IPv6
- **高性能**ï¼šåŸºäºŽå¼‚æ¥ IO 模型,使用 IOCP 实现高并å‘
- **管é“处ç†**ï¼šçµæ´»çš„æ¶ˆæ¯ç®¡é“,支æŒå议编解ç
- **SSL/TLS**:原生支æŒå®‰å…¨ä¼ 输
- **会è¯ç®¡ç†**:完善的连接会è¯ç®¡ç†æœºåˆ¶
- **线程安全**ï¼šæ‰€æœ‰æ ¸å¿ƒæ“ä½œé‡‡ç”¨åŽŸåæ“ä½œä¿è¯çº¿ç¨‹å®‰å…¨
- **坿‰©å±•**:易于扩展的架构设计
### 适用场景
- 峿—¶é€šè®¯æœåС噍
- æ¸¸æˆæœåС噍
- 物è”网数æ®é‡‡é›†
- RPC 远程调用
- 自定义åè®®æœåŠ¡
---
## 架构设计
### 整体架构
```
┌─────────────────────────────────────────────────────────────────â”
│ 应用层 │
│ NetServer / NetServer<TSession> │
│ ├── INetSession (会è¯ç®¡ç†) │
│ ├── INetHandler (æ•°æ®å¤„ç†å™¨) │
│ └── Pipeline (消æ¯ç®¡é“) │
├─────────────────────────────────────────────────────────────────┤
│ SocketæœåС层 │
│ ISocketServer │
│ ├── TcpServer (TCPæœåŠ¡ç«¯) │
│ └── UdpServer (UDPæœåŠ¡ç«¯) │
├─────────────────────────────────────────────────────────────────┤
│ Socket会è¯å±‚ │
│ ISocketSession │
│ ├── TcpSession (TCP会è¯) │
│ └── UdpSession (UDP会è¯) │
├─────────────────────────────────────────────────────────────────┤
│ 基础设施层 │
│ SessionBase │
│ ├── ProcessEvent (æ ¸å¿ƒæŽ¥æ”¶å¤„ç†) │
│ ├── 异æ¥IO (SocketAsyncEventArgs) │
│ └── ç¼“å†²åŒºç®¡ç† â”‚
└─────────────────────────────────────────────────────────────────┘
```
### æ ¸å¿ƒç±»å…³ç³»
```
NetServer
├── ISocketServer[] Servers // SocketæœåС噍集åˆ
│ ├── TcpServer // TCPæœåŠ¡
│ └── UdpServer // UDPæœåŠ¡
├── IDictionary<Int32, INetSession> Sessions // 网络会è¯é›†åˆ
└── IPipeline Pipeline // 消æ¯ç®¡é“
INetSession (网络会è¯)
├── ISocketSession Session // 底层Socket会è¯
├── NetServer Host // 所属æœåС噍
└── INetHandler Handler // æ•°æ®å¤„ç†å™¨
ISocketSession (Socket会è¯)
├── TcpSession // TCP连接会è¯
└── UdpSession // UDP连接会è¯
```
### æ•°æ®æµå‘
**æŽ¥æ”¶æ•°æ®æµ**:
```
Socket接收 → ProcessEvent → OnPreReceive → Pipeline.Read → OnReceive → Received事件
```
**å‘逿•°æ®æµ**:
```
Send/SendMessage → Pipeline.Write → OnSend → Socketå‘é€
```
### 生命周期
```
[客户端连接] → [创建ISocketSession] → [Server_NewSession]
↓
[OnNewSession] → [CreateSession] → [AddSession]
↓
[NetSession.Start()] → [OnConnected]
↓
[æ•°æ®æ”¶å‘循环]
↓
[客户端æ–å¼€/è¶…æ—¶] → [Close(reason)] → [OnDisconnected] → [Dispose]
```
---
## 快速入门
### 最简å•çš„ Echo æœåС噍
```csharp
using NewLife.Net;
// 创建æœåС噍
var server = new NetServer
{
Port = 12345, // 监å¬ç«¯å£
Log = XTrace.Log, // å¯ç”¨æ—¥å¿—
};
// å¤„ç†æŽ¥æ”¶æ•°æ®
server.Received += (sender, e) =>
{
if (sender is INetSession session)
{
// Echo 回å¤
session.Send(e.Packet);
}
};
// å¯åЍæœåŠ¡
server.Start();
Console.WriteLine($"æœåС已å¯åŠ¨ï¼Œç›‘å¬ç«¯å£ï¼š{server.Port}");
Console.ReadLine();
// åœæ¢æœåŠ¡
server.Stop("Manual");
```
### 创建客户端连接
```csharp
using NewLife.Net;
// 创建TCP客户端
var uri = new NetUri("tcp://127.0.0.1:12345");
var client = uri.CreateRemote();
client.Log = XTrace.Log;
// 打开连接
client.Open();
// å‘逿•°æ®
client.Send("Hello Server");
// 接收数æ®
var pk = client.Receive();
Console.WriteLine($"收到:{pk.ToStr()}");
// å…³é—连接
client.Close("Done");
```
### 使用 UDP åè®®
```csharp
// æœåŠ¡ç«¯
var server = new NetServer
{
Port = 12345,
ProtocolType = NetType.Udp, // 指定UDP
};
server.Start();
// 客户端
var uri = new NetUri("udp://127.0.0.1:12345");
var client = uri.CreateRemote();
client.Open();
client.Send("Hello UDP");
```
---
## æ ¸å¿ƒç»„ä»¶
### NetServer - 网络æœåС噍
NetServer 是网络æœåŠ¡å™¨çš„ä¸»å…¥å£ï¼Œç®¡ç†å¤šä¸ªåº•层 Socket æœåŠ¡å™¨ã€‚
#### 基本é…ç½®
```csharp
var server = new NetServer
{
// 基本é…ç½®
Name = "MyServer", // æœåŠ¡å(默认类å去掉ServeråŽç¼€ï¼‰
Port = 12345, // 监å¬ç«¯å£ï¼Œ0ä¸ºéšæœºç«¯å£
ProtocolType = NetType.Tcp, // å议类型:Tcp/Udp/Unknown(åŒæ—¶ç›‘å¬)
AddressFamily = AddressFamily.InterNetwork, // åœ°å€æ—:IPv4/IPv6/Unspecified(åŒæ—¶)
// 会è¯é…ç½®
SessionTimeout = 1200, // 会è¯è¶…时(秒),默认20分钟
UseSession = true, // 是å¦ä½¿ç”¨ä¼šè¯é›†åˆ
// SSLé…ç½®
SslProtocol = SslProtocols.Tls12, // SSLå议版本
Certificate = cert, // X509è¯ä¹¦
// 日志é…ç½®
Log = XTrace.Log, // æœåŠ¡å™¨æ—¥å¿—
SocketLog = XTrace.Log, // Socket日志
SessionLog = XTrace.Log, // ä¼šè¯æ—¥å¿—
LogSend = true, // 记录å‘逿—¥å¿—
LogReceive = true, // 记录接收日志
// 性能é…ç½®
StatPeriod = 600, // 统计周期(秒),0ç¦ç”¨
ReuseAddress = true, // 地å€é‡ç”¨
// 追踪é…ç½®
Tracer = tracer, // APM追踪器
SocketTracer = tracer, // Socket层追踪器
// ä¾èµ–注入
ServiceProvider = serviceProvider, // æœåŠ¡æä¾›è€…
};
```
#### å议类型é…ç½®
```csharp
// ä»…ç›‘å¬ TCP
server.ProtocolType = NetType.Tcp;
// ä»…ç›‘å¬ UDP
server.ProtocolType = NetType.Udp;
// åŒæ—¶ç›‘å¬ TCP å’Œ UDP(默认)
server.ProtocolType = NetType.Unknown;
// HTTP/WebSocket(自动å¯ç”¨HTTPè§£æžï¼‰
server.ProtocolType = NetType.Http;
```
#### åœ°å€æ—é…ç½®
```csharp
// ä»… IPv4
server.AddressFamily = AddressFamily.InterNetwork;
// ä»… IPv6
server.AddressFamily = AddressFamily.InterNetworkV6;
// åŒæ—¶ç›‘å¬ IPv4 å’Œ IPv6(默认)
server.AddressFamily = AddressFamily.Unspecified;
```
#### 事件处ç†
```csharp
// 新会è¯äº‹ä»¶
server.NewSession += (sender, e) =>
{
var session = e.Session;
Console.WriteLine($"新连接:{session.Remote}");
};
// æ•°æ®æŽ¥æ”¶äº‹ä»¶
server.Received += (sender, e) =>
{
if (sender is INetSession session)
{
Console.WriteLine($"收到[{session.Remote}]:{e.Packet?.ToStr()}");
// 处ç†è§£ç åŽçš„æ¶ˆæ¯
if (e.Message != null)
{
// å¤„ç†æ¶ˆæ¯å¯¹è±¡
}
}
};
// 错误事件
server.Error += (sender, e) =>
{
Console.WriteLine($"错误:{e.Exception.Message}");
};
```
#### 会è¯ç®¡ç†
```csharp
// èŽ·å–æŒ‡å®šä¼šè¯
var session = server.GetSession(sessionId);
// é历所有会è¯
foreach (var item in server.Sessions)
{
Console.WriteLine($"ä¼šè¯ {item.Key}: {item.Value.Remote}");
}
// 当å‰ä¼šè¯æ•°å’Œæœ€é«˜ä¼šè¯æ•°
Console.WriteLine($"在线: {server.SessionCount}/{server.MaxSessionCount}");
```
#### æ‰‹åŠ¨æ·»åŠ æœåС噍
```csharp
// æ–¹å¼1:使用 AddServer
server.AddServer(IPAddress.Any, 8080, NetType.Tcp);
server.AddServer(IPAddress.Any, 8081, NetType.Udp);
// æ–¹å¼2:使用 AttachServer
var tcpServer = new TcpServer { Port = 8080 };
server.AttachServer(tcpServer);
// æ–¹å¼3:é‡è½½ EnsureCreateServer
public class MyServer : NetServer
{
public override void EnsureCreateServer()
{
if (Servers.Count > 0) return;
var tcp = new TcpServer { Port = Port };
AttachServer(tcp);
}
}
```
### NetSession - 网络会è¯
æ¯ä¸ªè¿žæŽ¥å¯¹åº”一个会è¯ï¼Œç”¨äºŽå¤„ç†è¯¥è¿žæŽ¥çš„业务逻辑。
#### 自定义会è¯
```csharp
public class MySession : NetSession
{
/// <summary>用户ID</summary>
public Int32 UserId { get; set; }
/// <summary>连接时间</summary>
public DateTime ConnectTime { get; set; }
/// <summary>连接建立</summary>
protected override void OnConnected()
{
base.OnConnected();
ConnectTime = DateTime.Now;
WriteLog("客户端已连接");
// å‘逿¬¢è¿Žæ¶ˆæ¯
Send("Welcome!");
}
/// <summary>连接æ–å¼€</summary>
protected override void OnDisconnected(String reason)
{
base.OnDisconnected(reason);
WriteLog($"客户端已æ–开:{reason}");
}
/// <summary>收到数æ®</summary>
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
// 原始数æ®åŒ…
var pk = e.Packet;
// ç»è¿‡ç®¡é“è§£ç åŽçš„æ¶ˆæ¯å¯¹è±¡
var msg = e.Message;
// 业务处ç†
ProcessData(pk, msg);
}
/// <summary>å‘生错误</summary>
protected override void OnError(Object? sender, ExceptionEventArgs e)
{
base.OnError(sender, e);
WriteError("å‘生错误:{0}", e.Exception.Message);
}
private void ProcessData(IPacket? pk, Object? msg)
{
// 业务逻辑
}
}
// 使用泛型æœåС噍
var server = new NetServer<MySession>
{
Port = 12345,
};
server.Start();
// èŽ·å–æŒ‡å®šä¼šè¯ï¼ˆå¼ºç±»åž‹ï¼‰
var session = server.GetSession(sessionId);
if (session != null)
{
session.UserId = 123;
session.Send("Hello");
}
```
#### å‘逿•°æ®
```csharp
public class MySession : NetSession
{
public void SendData()
{
// å‘é€å—节数组
Send(new Byte[] { 0x01, 0x02, 0x03 });
Send(data, offset, count);
// å‘é€å—符串(默认UTF-8)
Send("Hello World");
Send("ä½ å¥½", Encoding.UTF8);
// å‘逿•°æ®åŒ…
var packet = new ArrayPacket(data);
Send(packet);
// å‘逿µ
using var stream = File.OpenRead("data.bin");
Send(stream);
// å‘é€Span(高性能,零拷è´ï¼‰
ReadOnlySpan<Byte> span = stackalloc Byte[100];
Send(span);
// 通过管é“å‘逿¶ˆæ¯ï¼ˆä¼šç»è¿‡ç¼–ç 器)
var bytes = SendMessage(new MyRequest { Cmd = "ping" });
// å‘é€å“应消æ¯ï¼ˆå…³è”请求上下文)
SendReply(new MyResponse { Result = "OK" }, e);
}
// 异æ¥å‘é€å¹¶ç‰å¾…å“应
public async Task<MyResponse> QueryAsync()
{
var response = await SendMessageAsync(new MyRequest { Cmd = "query" });
return response as MyResponse;
}
// 带超时的异æ¥è¯·æ±‚
public async Task<MyResponse> QueryWithTimeoutAsync()
{
using var cts = new CancellationTokenSource(5000);
var response = await SendMessageAsync(request, cts.Token);
return response as MyResponse;
}
}
```
#### ä¼šè¯æ•°æ®å˜å‚¨
```csharp
public class MySession : NetSession
{
protected override void OnConnected()
{
base.OnConnected();
// 使用索引器å˜å‚¨æ•°æ®ï¼ˆä¸Žåº•层Socket会è¯å…±äº«ï¼‰
this["userId"] = 12345;
this["loginTime"] = DateTime.Now;
// 使用 Items å—å…¸
Items["userData"] = new UserData();
}
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
// 读å–ä¼šè¯æ•°æ®
var userId = (Int32)this["userId"];
var userData = Items["userData"] as UserData;
}
}
```
#### 泛型会è¯ï¼ˆå¼ºç±»åž‹Host访问)
```csharp
// 自定义æœåС噍
public class GameServer : NetServer<GameSession>
{
public GameConfig Config { get; set; }
public IDatabase Database { get; set; }
}
// 自定义会è¯
public class GameSession : NetSession<GameServer>
{
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
// 直接访问自定义æœåŠ¡å™¨å±žæ€§ï¼ˆå¼ºç±»åž‹ï¼‰
var config = Host.Config;
var db = Host.Database;
}
}
```
### Pipeline - 消æ¯ç®¡é“
管é“用于å议编解ç ,支æŒé“¾å¼å¤„ç†ã€‚
#### æ ‡å‡†ç¼–è§£ç 器
```csharp
using NewLife.Net.Handlers;
var server = new NetServer { Port = 12345 };
// æ·»åŠ æ ‡å‡†ç¼–è§£ç 器(4å—节头部+æ•°æ®ï¼‰
server.Add<StandardCodec>();
// æ·»åŠ JSONç¼–è§£ç 器
server.Add<JsonCodec>();
// 接收解ç åŽçš„æ¶ˆæ¯
server.Received += (s, e) =>
{
// e.Message 是解ç åŽçš„æ¶ˆæ¯å¯¹è±¡
if (e.Message is DefaultMessage msg)
{
Console.WriteLine($"æ ‡å¿—ï¼š{msg.Flag}");
Console.WriteLine($"åºåˆ—å·ï¼š{msg.Sequence}");
Console.WriteLine($"负载:{msg.Payload?.ToStr()}");
}
};
server.Start();
```
#### 自定义处ç†å™¨
```csharp
public class MyHandler : Handler
{
public override Boolean Read(IHandlerContext context, Object message)
{
// è§£ç 处ç†ï¼ˆæŽ¥æ”¶æ•°æ®æ—¶ï¼‰
if (message is IPacket pk)
{
var myMsg = ParseMessage(pk);
return context.FireRead(myMsg);
}
return base.Read(context, message);
}
public override Boolean Write(IHandlerContext context, Object message)
{
// ç¼–ç 处ç†ï¼ˆå‘逿•°æ®æ—¶ï¼‰
if (message is MyMessage msg)
{
var pk = EncodeMessage(msg);
return context.FireWrite(pk);
}
return base.Write(context, message);
}
}
// 注册处ç†å™¨
server.Add(new MyHandler());
```
### INetHandler - 网络处ç†å™¨
用于会è¯çº§åˆ«çš„æ•°æ®é¢„处ç†ã€‚
```csharp
public class MyServer : NetServer<MySession>
{
// 为æ¯ä¸ªä¼šè¯åˆ›å»ºå¤„ç†å™¨
public override INetHandler? CreateHandler(INetSession session)
{
return new MyNetHandler();
}
}
public class MyNetHandler : INetHandler
{
public INetSession? Session { get; set; }
public void Init(INetSession session)
{
Session = session;
}
public void Process(ReceivedEventArgs e)
{
// é¢„å¤„ç†æ•°æ®ï¼ˆåœ¨OnReceive之å‰ï¼‰
// å¯ä»¥ä¿®æ”¹ e.Packet 或 e.Message
// å¯ä»¥è®¾ç½® e.Packet = null æ¥é˜»æ¢åŽç»å¤„ç†
}
public void Dispose()
{
// 清ç†èµ„æº
}
}
```
---
## 高级特性
### SSL/TLS åŠ å¯†
```csharp
// æœåŠ¡ç«¯SSL
var cert = new X509Certificate2("server.pfx", "password");
var server = new NetServer
{
Port = 443,
ProtocolType = NetType.Tcp,
SslProtocol = SslProtocols.Tls12 | SslProtocols.Tls13,
Certificate = cert,
};
server.Start();
// 客户端SSL
var client = new TcpSession
{
Remote = new NetUri("tcp://127.0.0.1:443"),
SslProtocol = SslProtocols.Tls12,
// å¯é€‰ï¼šå®¢æˆ·ç«¯è¯ä¹¦
Certificate = clientCert,
};
client.Open();
```
### ç¾¤å‘æ¶ˆæ¯
```csharp
// ç¾¤å‘æ•°æ®åŒ…给所有客户端
await server.SendAllAsync(new ArrayPacket(data));
// 带æ¡ä»¶ç¾¤å‘(过滤器)
await server.SendAllAsync(data, session =>
session["RoomId"]?.ToString() == "123");
// 群å‘ç®¡é“æ¶ˆæ¯ï¼ˆä¼šç»è¿‡ç¼–ç 器)
server.SendAllMessage(new BroadcastMessage { Content = "Hello" });
// 群å‘并过滤
server.SendAllMessage(message, session => session.ID > 100);
// 排除自己
server.SendAllMessage(message, session => session.ID != mySession.ID);
```
### 消æ¯è¯·æ±‚å“应
```csharp
// 客户端å‘é€è¯·æ±‚å¹¶ç‰å¾…å“应
var client = new TcpSession { Remote = uri };
client.Add<StandardCodec>();
client.Open();
// 异æ¥å‘逿¶ˆæ¯å¹¶ç‰å¾…å“应
var response = await client.SendMessageAsync(request);
// 带超时的请求
using var cts = new CancellationTokenSource(5000);
var response = await client.SendMessageAsync(request, cts.Token);
```
### ä¾èµ–注入
```csharp
// é…ç½®æœåŠ¡
services.AddSingleton<IMyService, MyService>();
services.AddScoped<IScopedService, ScopedService>();
var server = new NetServer<MySession>
{
Port = 12345,
ServiceProvider = serviceProvider,
};
// 在会è¯ä¸ä½¿ç”¨ï¼ˆè‡ªåŠ¨åˆ›å»ºScope)
public class MySession : NetSession
{
protected override void OnConnected()
{
base.OnConnected();
// ServiceProvider 已自动创建 Scope
var service = ServiceProvider?.GetService<IMyService>();
var scoped = ServiceProvider?.GetService<IScopedService>();
}
// 内置æœåŠ¡å¿«é€ŸèŽ·å–
public override Object GetService(Type serviceType)
{
if (serviceType == typeof(INetSession)) return this;
if (serviceType == typeof(NetServer)) return (this as INetSession).Host;
if (serviceType == typeof(ISocketSession)) return Session;
if (serviceType == typeof(ISocketServer)) return Server;
return base.GetService(serviceType);
}
}
```
### APM 性能追踪
```csharp
var server = new NetServer
{
Port = 12345,
Tracer = tracer, // 应用层追踪
SocketTracer = tracer, // Socket层追踪
};
// 追踪的æ“作包括:
// - net:{Name}:Connect 连接事件
// - net:{Name}:Receive 接收数æ®
// - net:{Name}:Send å‘逿•°æ®
// - net:{Name}:Disconnect æ–开连接
```
---
## 最佳实践
### 1. 资æºç®¡ç†
```csharp
// 使用usingç¡®ä¿èµ„æºé‡Šæ”¾
using var server = new NetServer { Port = 12345 };
server.Start();
// 或者在finallyä¸åœæ¢
var server = new NetServer { Port = 12345 };
try
{
server.Start();
// ...
}
finally
{
server.Stop("Shutdown");
server.Dispose();
}
```
### 2. 异常处ç†
```csharp
// 会è¯çº§å¼‚常处ç†
public class MySession : NetSession
{
protected override void OnReceive(ReceivedEventArgs e)
{
try
{
base.OnReceive(e);
ProcessData(e.Packet);
}
catch (Exception ex)
{
WriteError("å¤„ç†æ•°æ®å¼‚常:{0}", ex.Message);
// ä¸è¦åœ¨è¿™é‡Œå…³é—连接,让上层决定
}
}
}
// æœåŠ¡å™¨çº§å¼‚å¸¸å¤„ç†
server.Error += (s, e) =>
{
XTrace.WriteException(e.Exception);
};
```
### 3. 日志é…ç½®
```csharp
// 生产环境é…ç½®
var server = new NetServer
{
Log = XTrace.Log, // æœåŠ¡å™¨æ—¥å¿—
SessionLog = null, // å…³é—ä¼šè¯æ—¥å¿—
SocketLog = null, // å…³é—Socket日志
LogSend = false, // å…³é—å‘逿—¥å¿—
LogReceive = false, // å…³é—æŽ¥æ”¶æ—¥å¿—
StatPeriod = 600, // 10分钟输出一次统计
};
// 调试环境é…ç½®
var server = new NetServer
{
Log = XTrace.Log,
SocketLog = XTrace.Log,
SessionLog = XTrace.Log,
LogSend = true,
LogReceive = true,
StatPeriod = 60, // 1分钟输出统计
};
```
### 4. 会è¯çжæ€ç®¡ç†
```csharp
public class GameSession : NetSession
{
public Player Player { get; set; }
protected override void OnConnected()
{
base.OnConnected();
// åˆå§‹åŒ–玩家对象
Player = new Player();
}
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
// 使用Itemså˜å‚¨ä¸´æ—¶æ•°æ®
this["LastActiveTime"] = DateTime.Now;
// 使用强类型属性
Player?.HandlePacket(e.Packet);
}
protected override void OnDisconnected(String reason)
{
base.OnDisconnected(reason);
// 清ç†çŽ©å®¶æ•°æ®
Player?.SaveAndCleanup();
Player = null;
}
}
```
---
## 性能优化
### 1. 会è¯é›†åˆä¼˜åŒ–
```csharp
// 高并å‘场景下如ä¸éœ€è¦é历会è¯ï¼Œå¯ç¦ç”¨ä¼šè¯é›†åˆ
server.UseSession = false;
// 会è¯é›†åˆä½¿ç”¨ ConcurrentDictionary,é历时直接é历 Values
foreach (var session in server.Sessions.Values)
{
// é¿å… KeyValuePair çš„é¢å¤–开销
}
```
### 2. 群å‘优化
```csharp
// 群å‘å·²ä¼˜åŒ–ä¸ºåŒæ¥å‘é€ï¼Œé¿å… Task.Run 开销
// 直接é历 _Sessions.Values,å‡å°‘å—å…¸æ“作开销
await server.SendAllAsync(data);
// 如果需è¦å¹¶è¡Œå‘é€ï¼Œè‡ªè¡Œå®žçް
var tasks = server.Sessions.Values
.Where(predicate)
.Select(s => Task.Run(() => s.Send(data)));
await Task.WhenAll(tasks);
```
### 3. åŽŸåæ“ä½œ
```csharp
// SessionCount å’Œ MaxSessionCount ä½¿ç”¨åŽŸåæ“ä½œæ›´æ–°
// é¿å…é”竞争,æé«˜å¹¶å‘性能
var count = server.SessionCount; // 当å‰ä¼šè¯æ•°
var max = server.MaxSessionCount; // åŽ†å²æœ€é«˜ä¼šè¯æ•°
```
### 4. 追踪数æ®ä¼˜åŒ–
```csharp
// Send 方法追踪数æ®é™åˆ¶é•¿åº¦ï¼Œé¿å…大数æ®åŒ…å½±å“追踪性能
// å—符串和å—节数组最多记录64å—节
public virtual INetSession Send(String msg, Encoding? encoding = null)
{
// 追踪时åªè®°å½•å‰64å—符
using var span = host?.Tracer?.NewSpan($"net:{host.Name}:Send",
msg.Length > 64 ? msg[..64] : msg, ...);
...
}
```
### 5. 防é‡å…¥ä¿æŠ¤
```csharp
// Start å’Œ Close 方法都有防é‡å…¥ä¿æŠ¤
// 使用 Interlocked.CompareExchange ç¡®ä¿åªæ‰§è¡Œä¸€æ¬¡
public virtual void Start()
{
if (Interlocked.CompareExchange(ref _running, 1, 0) != 0) return;
...
}
public void Close(String reason)
{
if (Interlocked.CompareExchange(ref _running, 0, 1) != 1) return;
...
}
```
### 6. TCP 性能é…ç½®
```csharp
// 访问底层TcpServer进行é…ç½®
if (server.Server is TcpServer tcp)
{
tcp.NoDelay = true; // ç¦ç”¨Nagle算法(低延迟)
tcp.KeepAliveInterval = 60; // KeepAliveé—´éš”
}
```
---
## 常è§é—®é¢˜
### Q: 如何处ç†ç²˜åŒ…/拆包?
A: 使用 StandardCodec 或自定义å议处ç†å™¨ã€‚StandardCodec 采用 4 å—èŠ‚å¤´éƒ¨æ ‡è¯†æ•°æ®é•¿åº¦ã€‚
```csharp
server.Add<StandardCodec>();
```
### Q: 如何实现心跳检测?
A: 设置会è¯è¶…时时间,客户端定期å‘é€å¿ƒè·³åŒ…。
```csharp
server.SessionTimeout = 120; // 2åˆ†é’Ÿæ— æ•°æ®åˆ™æ–å¼€
// 客户端定时å‘é€å¿ƒè·³
timer.Elapsed += (s, e) => client.Send("ping");
```
### Q: 如何获å–客户端真实IP?
A: 通过会è¯çš„ Remote 属性获å–。
```csharp
server.NewSession += (s, e) =>
{
var ip = e.Session.Remote.Address;
var port = e.Session.Remote.Port;
Console.WriteLine($"客户端:{ip}:{port}");
};
```
### Q: 如何é™åˆ¶æœ€å¤§è¿žæŽ¥æ•°ï¼Ÿ
A: 在 OnNewSession 䏿£€æŸ¥å¹¶æ‹’ç»ã€‚
```csharp
public class MyServer : NetServer
{
public Int32 MaxConnections { get; set; } = 1000;
protected override INetSession OnNewSession(ISocketSession session)
{
if (SessionCount >= MaxConnections)
{
WriteLog("连接数超é™ï¼Œæ‹’ç»è¿žæŽ¥ï¼š{0}", session.Remote);
session.Dispose();
return null;
}
return base.OnNewSession(session);
}
}
```
### Q: æœåС噍é‡å¯æ—¶ç«¯å£è¢«å 用?
A: å¯ç”¨åœ°å€é‡ç”¨ã€‚
```csharp
server.ReuseAddress = true;
```
### Q: 如何å‘逿–‡ä»¶ï¼Ÿ
A: 使用æµå‘逿ˆ–扩展方法。
```csharp
// 简å•å‘逿µ
using var stream = File.OpenRead("data.bin");
session.Send(stream);
// 分包å‘é€ï¼ˆé…åˆStandardCodec)
client.SendFile("data.bin");
```
### Q: UDP 如何区分客户端?
A: UDP å议下,æ¯ä¸ªä¸åŒçš„远程地å€ä¼šåˆ›å»ºç‹¬ç«‹çš„会è¯ã€‚
```csharp
server.Received += (s, e) =>
{
var session = s as INetSession;
Console.WriteLine($"æ¥è‡ª {session.Remote} 的数æ®");
};
```
### Q: å¦‚ä½•å®žçŽ°å¹¿æ’æˆ¿é—´ï¼Ÿ
A: ä½¿ç”¨ä¼šè¯æ•°æ®æ ‡è®°æˆ¿é—´ï¼Œç¾¤å‘时过滤。
```csharp
// åŠ å…¥æˆ¿é—´
session["RoomId"] = "room1";
// 房间广æ’
server.SendAllMessage(message, s => s["RoomId"]?.ToString() == "room1");
```
---
## 附录
### æ ‡å‡†ç½‘ç»œå°åŒ…åè®®
æ–°ç”Ÿå‘½å›¢é˜Ÿæ ‡å‡†ç½‘ç»œå°åŒ…å议(DefaultMessage):
```
| 1 Flag | 1 Sequence | 2 Length | N Payload |
```
- **Flag** (1å—节)ï¼šæ ‡è¯†ä½ï¼Œå¯ç”¨èŒƒå›´0~63ï¼Œæ ‡è¯†æ¶ˆæ¯ç±»åž‹/åŠ å¯†/压缩ç‰
- **Sequence** (1å—节):åºåˆ—å·ï¼Œç”¨äºŽè¯·æ±‚å“应é…对
- **Length** (2å—节):数æ®é•¿åº¦ï¼Œæœ€å¤§64KB
- **Payload** (Nå—节):负载数æ®
### 关键类型速查
| 类型 | 说明 |
|------|------|
| `NetServer` | 网络æœåŠ¡å™¨ï¼Œç®¡ç†å¤šä¸ªSocketæœåŠ¡å™¨å’Œä¼šè¯ |
| `NetServer<TSession>` | 泛型网络æœåŠ¡å™¨ï¼Œè‡ªåŠ¨åˆ›å»ºæŒ‡å®šç±»åž‹ä¼šè¯ |
| `NetSession` | 网络会è¯åŸºç±»ï¼Œå¤„ç†å•个连接的业务逻辑 |
| `NetSession<TServer>` | 泛型网络会è¯ï¼Œå¼ºç±»åž‹è®¿é—®Host |
| `INetSession` | ç½‘ç»œä¼šè¯æŽ¥å£ |
| `INetHandler` | 网络处ç†å™¨æŽ¥å£ï¼Œä¼šè¯çº§æ•°æ®é¢„å¤„ç† |
| `IPipeline` | 消æ¯ç®¡é“æŽ¥å£ |
| `ISocketServer` | SocketæœåŠ¡å™¨æŽ¥å£ |
| `ISocketSession` | Socketä¼šè¯æŽ¥å£ |
| `TcpServer` | TCPæœåС噍 |
| `UdpServer` | UDPæœåС噍 |
| `TcpSession` | TCP客户端/ä¼šè¯ |
### 相关链接
- GitHub: https://github.com/NewLifeX/X
- Gitee: https://gitee.com/NewLifeX/X
- 文档: https://newlifex.com
---
*æœ¬æ–‡æ¡£æœ€åŽæ›´æ–°ï¼š2025å¹´7月*
|