文档:修复个别内容和实际版本不一致的问题

This commit is contained in:
若汝棋茗
2024-11-23 11:32:54 +08:00
parent dcc09bab72
commit 013c61119d
17 changed files with 491 additions and 111 deletions

View File

@@ -122,11 +122,11 @@ Memory<byte> memory = byteBlock.TotalMemory;
`ByteBlock`的工作模式,就像是你向商店(`BytePool`)租借了一个杯子(`ByteBlock``TotalMemory`就是这个杯子本身。
于我们而言,只在乎杯中实际有多少水(`Span`、`Memory`),所以`ByteBlock.Len`就是水位线。所以无论何时,我们都不可以喝超过水位线的水。因为超过水位线的水,可能是上个租客留下的口水。
于我们而言,只在乎杯中实际有多少水(`Span`、`Memory`),所以`byteBlock.Length`就是水位线。所以无论何时,我们都不可以喝超过水位线的水。因为超过水位线的水,可能是上个租客留下的口水。
同时喝水的时候还可以接力喝。比如先让张三方法A喝1口那么我们可以把已喝的水做个标记`Position`赋值为1然后把杯子`ByteBlock`传递给李四方法B那么这时候李四就只能按张三喝过的水继续喝当然如果李四不嫌弃的话也是可以从头再喝的。
最后就是杯子的归还(`Dispose`),以及杯中水的处理。在杯子未归还至前,杯子的所有属性(例如:`Len`、`Position`、`Span`、`Memory`)都代表的是当前的状态。而当杯子被归还时,这些属性将变得没有意义,尤其是`Memory`。因为`Memory`可以被其他对象引用,但是杯子归还后,`Memory`指向的内存块可能已经是新的申请人,这杯中的水,终究是变成了“前人的口水”。
最后就是杯子的归还(`Dispose`),以及杯中水的处理。在杯子未归还至前,杯子的所有属性(例如:`Length`、`Position`、`Span`、`Memory`)都代表的是当前的状态。而当杯子被归还时,这些属性将变得没有意义,尤其是`Memory`。因为`Memory`可以被其他对象引用,但是杯子归还后,`Memory`指向的内存块可能已经是新的申请人,这杯中的水,终究是变成了“前人的口水”。
但是如果我们确实需要保存杯中水的话,可以使用`ToArray()`方法。其返回值是一个数组,它可以以新引用的方式保存,且与内存池没有任何关系了,所有的生命周期归`GC`管理。
@@ -144,7 +144,7 @@ using (var byteBlock = new ByteBlock())
byteBlock.SeekToStart();//将游标重置
var buffer = new byte[byteBlock.Len];//定义一个数组容器
var buffer = new byte[byteBlock.Length];//定义一个数组容器
var r = byteBlock.Read(buffer);//读取数据到容器并返回读取的长度r
}
```

View File

@@ -49,7 +49,7 @@ for (int bufferLength = 1; bufferLength < 1024 * 10; bufferLength += 1024)
, bufferLength, (byteBlock, requestInfo) =>
{
//此处就是接收如果是自定义适配器可以将requestInfo强制转换为实际对象然后判断数据的确定性
if (byteBlock.Len!=5||(!byteBlock.ToArray().SequenceEqual(data)))
if (byteBlock.Length!=5||(!byteBlock.ToArray().SequenceEqual(data)))
{
isSuccess = false;
}

View File

@@ -77,8 +77,8 @@ Dmtp像http和websocket一样也是封装的应用层协议。它可以基于
### 4.1 连接注意事项
Dmtp拥有独立的连接机制在连接前后会依次触发`IDmtpHandshakingPlugin`与`IDmtpHandshakedPlugin`插件。当完成连接时,其`IDmtpActor.IsHandshaked`方为`true`,此后才可以进行后续操作。
Dmtp拥有独立的连接机制在连接前后会依次触发`IDmtpHandshakingPlugin`与`IDmtpHandshakedPlugin`插件。当完成连接时,其`IDmtpActor.Online`方为`true`,此后才可以进行后续操作。
例如Dmtp-Tcp组件这是基于Tcp协议的Dmtp因为它是继承实现所以会拥有`Online`和`IsHandshaked`两个属性。其中Online仅仅表示已经建立Tcp连接IsHandshaked才表示Dmtp是否完成握手。
例如Dmtp-Tcp组件这是基于Tcp协议的Dmtp因为它是继承实现所以会拥有`Online`和`Online`两个属性。其中Online仅仅表示已经建立Tcp连接IsHandshaked才表示Dmtp是否完成握手。
所以,在一些情况下,可能可能需要判断`IsHandshaked`完成,才能进行后续操作。
所以,在一些情况下,可能可能需要判断`Online`完成,才能进行后续操作。

View File

@@ -622,8 +622,8 @@ internal sealed class DefaultSerializationSelector : ISerializationSelector
return SerializeConvert.XmlDeserializeFromBytes(byteBlock.ReadBytesPackage(), parameterType);
case (SerializationType)4:
{
var len = byteBlock.ReadInt32();
var span = byteBlock.ReadToSpan(len);
var Length = byteBlock.ReadInt32();
var span = byteBlock.ReadToSpan(Length);
return MemoryPackSerializer.Deserialize(parameterType, span);
}
default:
@@ -876,7 +876,7 @@ Task task = Task.Run(() =>//这里必须用异步
{
foreach (var currentByteBlock in channel)
{
size += currentByteBlock.Len;//此处可以处理传递来的流数据
size += currentByteBlock.Length;//此处可以处理传递来的流数据
}
status = channel.Status;//最后状态
}
@@ -910,7 +910,7 @@ public int RpcPushChannel(ICallContext callContext, int channelID)
{
foreach (var item in channel)
{
size += item.Len;//此处处理流数据
size += item.Length;//此处处理流数据
}
}
}

View File

@@ -19,7 +19,7 @@ title: 文件流池
使用完成后,可以随时释放。
```csharp showLineNumbers
int len = 0;
int Length = 0;
byte[] buffer = new byte[1024 * 1024];
using (var reader = FilePool.GetReader(path))
@@ -31,11 +31,11 @@ using (var reader = FilePool.GetReader(path))
{
break;
}
len += r;
Length += r;
}
}
Console.WriteLine(len);
Console.WriteLine(Length);
```
## 三、使用写

View File

@@ -3,6 +3,8 @@ id: httpclient
title: 创建HttpClient
---
import CardLink from "@site/src/components/CardLink.js";
### 定义
命名空间TouchSocket.Http <br/>
@@ -172,4 +174,24 @@ using (var stream=File.OpenRead("TouchSocket.dll"))
:::
[本文示例Demo](https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Http)
## 六、传输文件
### 6.1 下载文件
```csharp {4} showLineNumbers
//直接发起一个Get请求然后写入到流中。
using (var stream=File.Create("1.txt"))
{
await client.GetFileAsync("/WeatherForecast",stream);
}
```
### 6.2 上传文件
```csharp showLineNumbers
await client.UploadFileAsync("/upfile", new FileInfo("filePath"));
```
## 七、本文示例Demo
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Http/HttpClientConsoleApp"/>

View File

@@ -5,6 +5,7 @@ title: 创建HttpService
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import CardLink from "@site/src/components/CardLink.js";
### 定义
@@ -471,5 +472,7 @@ Https服务器和http服务器几乎一样只不过增加了一个Ssl的
})
```
## 十、本文示例Demo
[本文示例Demo](https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Http)
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Http/HttpServiceConsoleApp"/>
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Http/HttpServiceForCorsConsoleApp"/>

View File

@@ -8,9 +8,29 @@ title: 静态页面插件
命名空间TouchSocket.Http <br/>
程序集:[TouchSocket.Http.dll](https://www.nuget.org/packages/TouchSocket.Http)
## 静态网页托管插件仅服务器支持
## 一、说明
**HttpStaticPagePlugin**静态网页托管插件是用于Http的内容响应
静态页面功能是指`Web`服务器通过`HTTP`(超文本传输协议)或`HTTPS`安全的超文本传输协议向客户端通常是浏览器提供预定义的内容。这些内容通常包括HTML文件、CSS样式表、JavaScript脚本以及图片等多媒体资源。与动态页面不同静态页面在服务器端不会进行任何处理或计算它们是以文件形式存储在服务器上的并且当用户请求时服务器直接将这些文件发送给客户端
## 二、静态页面的特点
1. **加载速度快**:由于服务器不需要执行任何脚本来生成页面内容,因此响应速度通常更快。
2. **易于维护**:对于内容不经常变化的网站,使用静态页面可以减少维护成本,因为无需担心后端逻辑的更新和数据库管理等问题。
3. **成本效益**:对于访问量较小的站点,使用静态页面可以节省服务器资源和带宽,降低运营成本。
## 三、静态页面的使用场景
- **个人博客**:如果博主主要发布文字内容,且更新频率不高,那么采用静态页面构建个人博客是一个不错的选择。
- **企业介绍**:对于那些只需要展示公司信息、联系方式等基本内容的企业网站来说,静态页面能够满足需求同时保持简洁高效。
- **项目展示**:艺术家、设计师等可以通过静态页面来创建个人作品集,以直观地向潜在客户展示自己的能力和风格。
总之,静态页面因其简单、快速和安全的特性,在特定的应用场景下具有明显优势。然而,对于需要频繁更新内容或实现复杂交互功能的网站,则可能更适合选择动态页面技术。
## 四、使用
### 4.1 常规使用
在创建`HttpService`实例后,只需要使用`UseHttpStaticPage`插件,然后指定根文件夹路径即可。
```csharp showLineNumbers
var service = new HttpService();
@@ -30,3 +50,67 @@ await service.StartAsync();
Console.WriteLine("Http服务器已启动");
```
:::tip 提示
`UseHttpStaticPage`插件可以多次添加,也可以添加多个文件夹。
:::
### 4.2 请求资源定向
在使用`UseHttpStaticPage`插件时,可以指定请求资源定向。例如,当请求的`URL`为`/api`时,将重定向到`/api/index.html`。
```csharp {4-7} showLineNumbers
a.UseHttpStaticPage()
.SetNavigateAction(request =>
{
if (request.RelativeURL.EndsWith("/"))
{
return $"{request.RelativeURL}/index.html";
}
//此处可以设置重定向
return request.RelativeURL;
})
.AddFolder("api/");
```
:::info 信息
默认情况下,会将`/`、`/index`都定向到`/index.html`。
:::
### 4.3 响应配置
可以通过配置响应头,例如添加自定义头。
```csharp {2-5} showLineNumbers
a.UseHttpStaticPage()
.SetResponseAction(response =>
{
//可以设置响应头
})
.AddFolder("api/");
```
### 4.4 配置ContentType
默认情况下,会将常见的文件后缀名映射到对应的`ContentType`。例如,`.html`文件会被映射为`text/html`。详情请见[ContentTypeMapper](https://gitee.com/RRQM_Home/TouchSocket/blob/master/src/TouchSocket.Http/StaticPage/FileExtensionContentTypeProvider.cs)。
但是,也可以通过配置`ContentTypeMapper`来覆盖默认的映射关系。
```csharp {2-5} showLineNumbers
a.UseHttpStaticPage()
.SetContentTypeProvider(mapper =>
{
mapper.Add(".txt", "text/plain");
})
.AddFolder("api/");
```
:::info 信息
在一些浏览器中,可能会出现编码错误的问题,可以通过设置`ContentTypeMapper`来修正。例如,将`.txt`文件映射为`text/plain; charset=UTF-8`,这样就不会出现乱码了。
:::

View File

@@ -38,7 +38,7 @@ adapter.ReceivedCallBack = (byteBlock, requestInfo) =>
{
//此处会回调接收的最终触发例如此处使用的固定包头会解析4+n的数据为n。
if (byteBlock.Len == 4)
if (byteBlock.Length == 4)
{
receivedCallBack = true;
}

View File

@@ -3,6 +3,8 @@ id: ipackage
title: 包序列化模式
---
import CardLink from "@site/src/components/CardLink.js";
### 定义
命名空间TouchSocket.Core <br/>
@@ -11,7 +13,7 @@ title: 包序列化模式
## 一、说明
包序列化模式是为了解决**极限序列化**的问题。常规序列化的瓶颈主要是反射、表达式树、创建对象等几个方面这几个问题在运行时阶段都没有一个好的解决方案。目前在net6以后微软大力支持源生成这使得这类问题得到了很大程度的解决。但是对于老项目或者无法使用net6和vs2022以上的项目是无法使用的。所以,这时候包序列化模式就显得非常需要了。
包序列化模式是为了解决**极限序列化**的问题。常规序列化的瓶颈,主要是反射、表达式树、创建对象等几个方面,这几个问题在运行时阶段,都没有一个好的解决方案。目前在`net6`以后,微软大力支持源生成,这使得这类问题得到了很大程度的解决。所以,这时候包序列化模式就显得非常需要了。
## 二、特点
@@ -23,9 +25,8 @@ title: 包序列化模式
### 2.2 缺点
1. 在源生成无法使用时,手动编写代码比较麻烦
2. 不支持跨语言
3. 类型版本兼容性比较差,简单来说就是高版本只能新增属性,不能删除属性,不能修改属性类型(如果类型长度一致,则可以修改类型,例如:`int` -> `float`)。
1. 不支持跨语言
2. 类型版本兼容性比较差,简单来说就是高版本只能新增属性,不能删除属性,不能修改属性类型(如果类型长度一致,则可以修改类型,例如:`int` -> `float`
## 三、使用
@@ -33,7 +34,7 @@ title: 包序列化模式
例如:
下列类型MyClass有一个Int类属性和一个string类属性。
下列类型`MyClass`,有一个`int`类属性和一个`string`类属性。
```csharp showLineNumbers
public class MyClass
@@ -43,9 +44,9 @@ public class MyClass
}
```
我们可以使用包序列化模式将MyClass序列化成二进制流或者反序列化成MyClass。
我们可以使用包序列化模式,将`MyClass`序列化成二进制流,或者反序列化成`MyClass`
那么首先需要实现IPackage接口或者继承PackageBase然后依次将属性写入到ByteBlock中或者从ByteBlock中读取属性。
那么首先需要实现`IPackage`接口(或者继承`PackageBase`),然后依次将属性写入到`ByteBlock`中,或者从`ByteBlock`中读取属性。
```csharp {9-10,16-17} showLineNumbers
public class MyClass:PackageBase
@@ -71,9 +72,9 @@ public class MyClass:PackageBase
### 3.2 数组(列表)类型
对于数组、列表等类型需要先判断是否为null然后再写入有效值。
对于数组、列表等类型,需要先判断是否为`null`,然后再写入有效值。
如果有效值是自定义类型则也需要实现IPackage接口然后依次写入。
如果有效值是自定义类型,则也需要实现`IPackage`接口,然后依次写入。
```csharp {5,22} showLineNumbers
public class MyArrayClass : PackageBase
@@ -119,7 +120,7 @@ public class MyArrayClass : PackageBase
### 3.3 字典类型
字典类型基本上和数组类似也是先判断是否为null然后再写入有效值。
字典类型基本上和数组类似,也是先判断是否为`null`,然后再写入有效值。
```csharp {5,23} showLineNumbers
public class MyDictionaryClass : PackageBase
@@ -172,7 +173,7 @@ public class MyDictionaryClass : PackageBase
### 4.1 使用内存块
使用内存块使用ByteBlock类。
使用内存块,使用`ByteBlock`类。
```csharp {12,20} showLineNumbers
//声明内存大小。
@@ -201,7 +202,7 @@ using (var byteBlock = new ByteBlock(1024 * 64))
### 4.2 使用值类型内存块
使用值类型内存块使用ValueByteBlock类。
使用值类型内存块,使用`ValueByteBlock`类。
```csharp {15,23} showLineNumbers
//声明内存大小。
@@ -238,10 +239,12 @@ finally
## 五、使用源生成
如果源生成可用一般指vs2019最新版和vs2022Rider使用源代码生成方式可以实现**自动**的打包和解包。
如果源生成可用(一般指`vs2019`最新版和`vs2022``Rider`),使用源代码生成方式,可以实现**自动**的打包和解包。
例如上述类型,我们只需要使用`GeneratorPackage`特性标记即可。
### 5.1 生成特性
```csharp {5} showLineNumbers
/// <summary>
/// 使用源生成包序列化。
@@ -357,13 +360,165 @@ namespace PackageConsoleApp
:::
## 五、性能评测
## 六、源生成Member配置
### 6.1 成员可见性
默认情况下使用源代码生成方式时只会将公共成员公共属性、private set属性和公共字段进行打包和解包。如果需要对某些成员进行过滤或者对私有成员进行强制可以使用`PackageMember`特性进行配置。
【忽略成员】
```csharp {4} showLineNumbers
[GeneratorPackage]
internal partial class MyGeneratorPackage : PackageBase
{
[PackageMember(Behavior = PackageBehavior.Ignore)]
public int P1 { get; set; }
...
}
```
【强制成员】
```csharp {5} showLineNumbers
[GeneratorPackage]
internal partial class MyGeneratorPackage : PackageBase
{
...
[PackageMember(Behavior = PackageBehavior.Include)]
private int P8;
}
```
### 6.2 成员顺序
`Package`的工作逻辑是读取成员,然后按照成员名**顺序**进行打包。
但是有时候希望可以自定义顺序,目的是为了更好的兼容性。
则可以先使用`PackageMember`特性对成员进行排序。
```csharp {4,7,10} showLineNumbers
[GeneratorPackage]
internal partial class MyGeneratorIndexPackage : PackageBase
{
[PackageMember(Index = 2)]
public int P1 { get; private set; }
[PackageMember(Index = 0)]
public string P2 { get; set; }
[PackageMember(Index = 1)]
public char P3 { get; set; }
}
```
<details>
<summary>源生成的代码</summary>
<div>
```csharp showLineNumbers
/*
此代码由SourceGenerator工具直接生成非必要请不要修改此处代码
*/
#pragma warning disable
using System;
using System.Diagnostics;
using TouchSocket.Core;
using System.Threading.Tasks;
namespace PackageConsoleApp
{
partial class MyGeneratorIndexPackage
{
public override void Package<TByteBlock>(ref TByteBlock byteBlock)
{
byteBlock.WriteString(P2);
byteBlock.WriteChar(P3);
byteBlock.WriteInt32(P1);
}
public override void Unpackage<TByteBlock>(ref TByteBlock byteBlock)
{
P2 = byteBlock.ReadString();
P3 = byteBlock.ReadChar();
P1 = byteBlock.ReadInt32();
}
}
}
```
</div>
</details>
### 6.3 自定义类型转换
`Package`在源生成打包时,是支持自定义类型的,但是要求是自定义的类型也必须实现`IPackage`接口。
但是,有时候,有些成员类型是已存在的第三方类型,所以就需要使用自定义转换器来实现。
例如:对于`Rectangle`类型,这是一个在`System.Drawing`中记录矩形的数据类型。
```csharp {4} showLineNumbers
[GeneratorPackage]
internal partial class MyGeneratorConvertPackage : PackageBase
{
public Rectangle P1 { get; set; }
}
```
默认情况下,是无法使用源生成打包的。
这时候就需要自定转换器。
首先,新建一个类,继承`FastBinaryConverter<T>`,指定泛型为`Rectangle`,然后实现`Read`和`Write`方法。
```csharp showLineNumbers
class RectangleConverter : FastBinaryConverter<Rectangle>
{
protected override Rectangle Read<TByteBlock>(ref TByteBlock byteBlock, Type type)
{
var rectangle = new Rectangle(byteBlock.ReadInt32(), byteBlock.ReadInt32(), byteBlock.ReadInt32(), byteBlock.ReadInt32());
return rectangle;
}
protected override void Write<TByteBlock>(ref TByteBlock byteBlock, in Rectangle obj)
{
byteBlock.WriteInt32(obj.X);
byteBlock.WriteInt32(obj.Y);
byteBlock.WriteInt32(obj.Width);
byteBlock.WriteInt32(obj.Height);
}
}
```
:::info 信息
在实现时,不需要考虑对象为`null`的情况,无论是类还是结构体,都会自动判断。所以,触发到转换器时,一定是不为`null`。
:::
然后,在成员中,添加`[PackageMember(Converter =typeof(RectangleConverter))]`即可。
```csharp {4} showLineNumbers
[GeneratorPackage]
internal partial class MyGeneratorConvertPackage : PackageBase
{
[PackageMember(Converter =typeof(RectangleConverter))]
public Rectangle P1 { get; set; }
}
```
## 七、性能评测
基准测试表明:
包序列化模式比MemoryPack快30%。
比json方式快了20倍多。
比微软的json快了近10倍。
包序列化模式比`MemoryPack`快30%。
`json`方式快了20倍多。
比微软的`json`快了近10倍。
比微软的二进制快了近100倍。
```csharp showLineNumbers
@@ -377,3 +532,7 @@ namespace PackageConsoleApp
| FastBinarySerialize | .NET 6.0 | .NET 6.0 | 7.531 ms | 0.0194 ms | 0.0162 ms | 4.57 | 0.03 | 578.1250 | - | 8.7 MB | 1.14 |
| SystemBinarySerialize | .NET 6.0 | .NET 6.0 | 253.637 ms | 1.5709 ms | 1.3118 ms | 153.95 | 1.04 | 28000.0000 | 1000.0000 | 420.51 MB | 55.11 |
```
## 八、本文示例Demo
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Core/PackageConsoleApp"/>

View File

@@ -3,6 +3,8 @@ id: packageadapter
title: 内置包适配器
---
import CardLink from "@site/src/components/CardLink.js";
### 定义
命名空间TouchSocket.Core <br/>
@@ -19,6 +21,7 @@ title: 内置包适配器
| FixedSizePackageAdapter | 固定长度数据处理适配器 |固定长度数据处理适配器是将发送的数据通过分割、填补的操作,以达到每次发送、接收的数据都是固定的长度来处理粘包、分包问题。这种方案一般适用于机械臂,机器人控制等场景。 |
| TerminatorPackageAdapter | 终止因子数据处理适配器 |终止因子数据处理适配器是通过**特殊字符或数值**的方式,来达到处理粘包、分包的目的。可随意设置分割因子的值,以及编码方式。不仅如此,还有异常数据设置,在达到设定值时,如果还没有发现分割因子,则抛弃数据。其稳定性仅次于固定包头,且使用场景也比较广泛。|
| PeriodPackageAdapter | 周期数据处理适配器 |周期数据处理适配器是通过**时间周期**的方式,来处理分包的目的(不包括粘包)。可处理任意数据。但是这也只是一定程度的处理。|
| JsonPackageAdapter | Json格式数据处理适配器 |Json格式数据处理适配器是一个非常不错的解决**纯Json字符串**粘、分包的方案它能将符合Json标准的数据准确地分割出来。并且能把其中的杂质数据一起提取出来。|
## 二、特点
@@ -48,6 +51,13 @@ title: 内置包适配器
2. 只能解决分包问题,无法解决粘包问题。
3. 处理效率会有一定延迟。
### 2.5 Json格式数据处理适配器
1. 能够处理任意标准Json数据。
2. 能够提取出信息中的杂质数据。
3. 支持单个Object数据、或者Array数据。
4. 支持类型嵌套格式。
## 三、算法解释
### 3.1 固定包头算法
@@ -68,6 +78,11 @@ title: 内置包适配器
周期数据处理适配器,就是通过判断收到数据的时间间隔,将极短时间内收到的数据进行合并。能够一定程度的解决分包问题。
### 3.5 Json格式数据处理算法
Json格式数据处理算法就是对接收的字符串进行大括号和中括号的计数当成对的括号组合来确定一个完整的json数据。
## 四、使用
### 4.1 使用固定包头适配器
@@ -344,6 +359,73 @@ private static async Task<TcpService> CreateService()
:::
### 4.5 使用Json格式数据处理适配器
客户端与服务器均适用。下列以服务器为例。
步骤
1. TouchSocketConfig配置中设置同时指定数据的长度。
2. 通过Received事件、方法、插件中的IRequestInfo强制转为JsonPackage然后读取数据。
```csharp {7,31} showLineNumbers
private static async Task<TcpClient> CreateClient()
{
var client = new TcpClient();
//载入配置
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.SetTcpDataHandlingAdapter(()=>new JsonPackageAdapter(Encoding.UTF8))
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个日志注入
}));
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
client.Logger.Info("客户端成功连接");
return client;
}
private static async Task<TcpService> CreateService()
{
var service = new TcpService();
service.Received = (client, e) =>
{
//从客户端收到信息
var mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
client.Logger.Info($"已从{client.Id}接收到信息:{mes}");
return Task.CompletedTask;
};
await service.SetupAsync(new TouchSocketConfig()//载入配置
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
.SetTcpDataHandlingAdapter(()=>new JsonPackageAdapter(Encoding.UTF8))
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个控制台日志注入注意在maui中控制台日志不可用
})
.ConfigurePlugins(a =>
{
//a.Add();//此处可以添加插件
}));
await service.StartAsync();//启动
service.Logger.Info("服务器已启动");
return service;
}
```
:::tip 提示
Json格式数据处理适配器不对发送的数据做处理仅仅对接收到的数据做处理。
:::
:::tip 提示
该适配器,客户端与服务器均适用。
:::
## 五、可设置参数
| 属性 | 描述 |默认值 |
@@ -355,4 +437,6 @@ private static async Task<TcpService> CreateService()
| CacheTimeout | 缓存超时时间。 |1秒|
| UpdateCacheTimeWhenRev | 是否在收到数据时即刷新缓存时间。当设为true时将弱化CacheTimeout的作用只要一直有数据则缓存不会过期。当设为false时则在CacheTimeout的时效内。必须完成单个缓存的数据 |true|
[本文示例Demo](https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Adapter/PackageAdapterConsoleApp)
## 六、本文示例Demo
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Adapter/PackageAdapterConsoleApp"/>

View File

@@ -6,7 +6,7 @@ title: 入门指南
## 一、说明
**TouchSocketPro**系是基于`.Net`发布的程序集系列,所以它可以被用于对应`.Net`版本的`C#`、`F#`、`VB.net`等语言项目。
**TouchSocketPro** 系是基于`.Net`发布的程序集系列,所以它可以被用于对应`.Net`版本的`C#`、`F#`、`VB.net`等语言项目。
它支持您的项目是以下类型:
- 控制台

View File

@@ -399,7 +399,7 @@ await service.StartAsync();
:::danger 注意
当接收数据时ByteBlock与RequestInfo的值会根据适配器类型不同而不同。并且当数据存于ByteBlock时其实际的数据长度是ByteBlock.Length(Len)。而不是ByteBlock.Buffer.Length
当接收数据时ByteBlock与RequestInfo的值会根据适配器类型不同而不同。并且当数据存于ByteBlock时其实际的数据长度是ByteBlock.Length(Length)。而不是ByteBlock.Buffer.Length
:::

View File

@@ -62,7 +62,7 @@ config.UsePlugin()
.ConfigurePlugins(a =>
{
a.Add<TLVPlugin>()//使用插件相当于自动设置适配器并且主动回应Ping。
.SetLengthType(FixedHeaderType.Int);//设置支持的最大数据类型该值还受SetMaxPackageSize影响。
.SetLengthype(FixedHeaderType.Int);//设置支持的最大数据类型该值还受SetMaxPackageSize影响。
});
```

View File

@@ -4,6 +4,7 @@ title: 创建WebSocket客户端
---
import Tag from "@site/src/components/Tag.js";
import CardLink from "@site/src/components/CardLink.js";
### 定义
@@ -22,7 +23,7 @@ import Tag from "@site/src/components/Tag.js";
| IWebSocketHandshakedPlugin | 当成功握手响应之后 |
| IWebSocketReceivedPlugin | 当收到Websocket的数据报文 |
| IWebSocketClosingPlugin | 当收到关闭请求时,如果对方直接断开连接,此方法则不会触发。 |
| IWebSocketClosedPlugin | 当WebSocket连接断开时触发无论是否正常断开。但如果是断网等操作可能不会立即执行需要结合心跳操作和CheckClear插件来进行清理。 |
| IWebSocketClosedPlugin | 当WebSocket连接断开时触发无论是否正常断开。但如果是断网等操作可能不会立即执行需要结合心跳操作和`UseCheckClear`插件来进行清理。 |
## 三、创建客户端
@@ -71,17 +72,17 @@ Console.WriteLine("连接成功");
:::caution 注意
当使用域名连接时TargetHost为域名例如连接到IPHost("wss://baidu.com")时TargetHost应当填写baidu.com
当使用域名连接时,`TargetHost`为域名,例如连接到`IPHost("wss://baidu.com")`时,`TargetHost`应当填写:`baidu.com`
:::
## 四、连接服务器
WebSessionClient可以使用默认配置直接连接到服务器同时也支持使用多种方法定义连接。
`WebSocketClient`可以使用默认配置直接连接到服务器,同时也支持使用多种方法定义连接。
### 4.1 直接连接
使用url直接建立连接这一般是服务器也只是普通的ws服务器的情况下。
使用`url`直接建立连接,这一般是服务器也只是普通的`ws`服务器的情况下。
```csharp showLineNumbers
var client = new WebSocketClient();
@@ -98,7 +99,7 @@ client.Logger.Info("通过ws://127.0.0.1:7789/ws连接成功");
### 4.2 带Query参数连接
带Query参数连接实际上还是通过url直接连接。
`Query`参数连接,实际上还是通过`url`直接连接。
```csharp showLineNumbers
var client = new WebSocketClient();
@@ -116,7 +117,7 @@ client.Logger.Info("通过ws://127.0.0.1:7789/wsquery?token=123456连接成功")
### 4.3 使用特定Header连接
一般的当某些服务器安全级别较高时可能会定制特定的header用于验证连接。
一般的,当某些服务器安全级别较高时,可能会定制特定的`header`用于验证连接。
```csharp showLineNumbers
var client = new WebSocketClient();
@@ -141,13 +142,13 @@ client.Logger.Info("通过ws://127.0.0.1:7789/wsheader连接成功");
:::tip 提示
实际上OnWebSocketHandshaking就是插件委托也可以自己封装到插件使用。
实际上`OnWebSocketHandshaking`就是插件委托,也可以自己封装到插件使用。
:::
### 4.4 使用Post方式连接
WebSocket默认情况下是基于GET方式连接的,但是在一些更特殊的情况下,需要以POST,甚至其他方式连接,那么可以使用以下方式实现。
`WebSocket`默认情况下是基于`Get`方式连接的,但是在一些更特殊的情况下,需要以`Post`,甚至其他方式连接,那么可以使用以下方式实现。
```csharp showLineNumbers
using var client = new WebSocketClient();
@@ -172,40 +173,41 @@ client.Logger.Info("通过ws://127.0.0.1:7789/postws连接成功");
:::tip 提示
使用此方式时基本上就能完全定制请求连接了。比如一些Cookie等。
使用此方式时,基本上就能完全定制请求连接了。比如一些`Cookie`等。
:::
## 五、发送数据
因为客户端是从**HttpClientBase**派生,则可以直接使用**扩展方法**,进行发送
客户端定义了一些发送方法,方便开发者快速发送数据
### 5.1 发送文本类消息
```csharp showLineNumbers
client.SendAsync("Text");
await client.SendAsync("Text");
```
### 5.2 发送二进制消息
```csharp showLineNumbers
client.SendAsync(new byte[10]);
await client.SendAsync(new byte[10]);
```
### 5.3 直接发送自定义构建的数据帧
```csharp showLineNumbers
WSDataFrame frame=new WSDataFrame();
frame.Opcode= WSDataType.Text;
frame.FIN= true;
frame.RSV1= true;
frame.RSV2= true;
frame.RSV3= true;
frame.AppendText("I");
frame.AppendText("Love");
frame.AppendText("U");
client.SendAsync(frame);
using (var frame = new WSDataFrame())
{
frame.Opcode = WSDataType.Text;
frame.FIN = true;
frame.RSV1 = true;
frame.RSV2 = true;
frame.RSV3 = true;
frame.AppendText("I");
frame.AppendText("Love");
frame.AppendText("U");
await client.SendAsync(frame);
}
```
:::info 备注
@@ -218,26 +220,28 @@ client.SendAsync(frame);
### 6.1 订阅Received事件实现
```csharp showLineNumbers
client.Received = (c, e) =>
client.Received = async (c, e) =>
{
switch (e.DataFrame.Opcode)
{
switch (e.DataFrame.Opcode)
{
case WSDataType.Cont:
break;
case WSDataType.Text:
break;
case WSDataType.Binary:
break;
case WSDataType.Close:
break;
case WSDataType.Ping:
break;
case WSDataType.Pong:
break;
default:
break;
}
};
case WSDataType.Cont:
break;
case WSDataType.Text:
break;
case WSDataType.Binary:
break;
case WSDataType.Close:
break;
case WSDataType.Ping:
break;
case WSDataType.Pong:
break;
default:
break;
}
await e.InvokeNext();
};
```
### 6.2 使用插件实现 <Tag>推荐</Tag>
@@ -354,7 +358,7 @@ using (var client = GetClient())
:::info 信息
`ReadAsync`的方式是属于**同步不阻塞**的接收方式和当下Aspnetcore模式一样。他不会单独占用线程,只会阻塞当前`Task`。所以可以大量使用,不需要考虑性能问题。同时,`ReadAsync`的好处就是单线程访问上下文这样在处理ws分包时是非常方便的。
`ReadAsync`的方式是属于**同步不阻塞**的接收方式。他不会单独占用线程,只会阻塞当前`Task`。所以可以大量使用,不需要考虑性能问题。同时,`ReadAsync`的好处就是单线程访问上下文这样在处理ws分包时是非常方便的。
:::
@@ -612,15 +616,15 @@ public class MyWebSocketPlugin : PluginBase, IWebSocketReceivedPlugin
### 7.1 握手机制
`WebSocket`拥有独立的握手机制,直接获取`IsHandshaked`属性即可。
`WebSocket`拥有独立的握手机制,直接获取`Online`属性即可。
### 7.2 Ping机制
`WebSocket`有自己的`Ping`、`Pong`机制。所以直接调用已有方法即可。
```csharp showLineNumbers
client.Ping();
client.Pong();
await client.PingAsync();
await client.PongAsync();
```
:::tip 建议
@@ -631,21 +635,31 @@ client.Pong();
### 7.3 断线重连
`WebSocket`断线重连,可以直接使用[Tcp断线重连](./reconnection.mdx)插件。
`WebSocket`断线重连,可以直接使用插件。
```csharp showLineNumbers
.ConfigurePlugins(a =>
{
a.UseReconnection();
a.UseWebSocketReconnection();
})
```
## 八、关闭连接
关闭Websocket,应该发送关闭报文
在使用`WebSocket`时,如果想主动关闭连接,可以使用`CloseAsync`方法,同时可以携带一个关闭原因
默认关闭状态码为1000。意为正常关闭。
```csharp showLineNumbers
myWSClient.Close("close");
await webSocket.CloseAsync("关闭");
```
[本文示例Demo](https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/WebSocket/WebSocketConsoleApp)
如果你想使用其他状态码,可以参考如下代码。
```csharp showLineNumbers
await webSocket.CloseAsync(WebSocketCloseStatus.EndpointUnavailable,"关闭");//状态码为1001意为服务端不可用。
```
## 九、本文示例Demo
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/WebSocket/WebSocketConsoleApp"/>

View File

@@ -3,6 +3,8 @@ id: websocketservice
title: 创建WebSocket服务器
---
import CardLink from "@site/src/components/CardLink.js";
### 定义
命名空间TouchSocket.Http.WebSockets <br/>
@@ -838,17 +840,18 @@ await webSocket.SendAsync(new byte[10]);
### 6.5 直接发送自定义构建的数据帧
```csharp showLineNumbers
WSDataFrame frame=new WSDataFrame();
frame.Opcode= WSDataType.Text;
frame.FIN= true;
frame.RSV1= true;
frame.RSV2= true;
frame.RSV3= true;
frame.AppendText("I");
frame.AppendText("Love");
frame.AppendText("U");
await webSocket.SendAsync(frame);
using (var frame = new WSDataFrame())
{
frame.Opcode = WSDataType.Text;
frame.FIN = true;
frame.RSV1 = true;
frame.RSV2 = true;
frame.RSV3 = true;
frame.AppendText("I");
frame.AppendText("Love");
frame.AppendText("U");
await webSocket.SendAsync(frame);
}
```
:::info 备注
@@ -886,12 +889,23 @@ while (true)
}
```
### 6.9 关闭连接
## 七、关闭连接
在使用`WebSocket`时,如果想主动关闭连接,可以使用`CloseAsync`方法,同时可以携带一个关闭原因。
默认关闭状态码为1000。意为正常关闭。
```csharp showLineNumbers
await webSocket.CloseAsync("关闭");
```
如果你想使用其他状态码,可以参考如下代码。
```csharp showLineNumbers
await webSocket.CloseAsync(WebSocketCloseStatus.EndpointUnavailable,"关闭");//状态码为1001意为服务端不可用。
```
[本文示例Demo](https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/WebSocket/WebSocketConsoleApp)
## 八、本文示例Demo
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/WebSocket/WebSocketConsoleApp"/>

View File

@@ -15,9 +15,9 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.6.1",
"@docusaurus/preset-classic": "^3.6.1",
"@docusaurus/theme-mermaid": "^3.6.1",
"@docusaurus/core": "^3.6.2",
"@docusaurus/preset-classic": "^3.6.2",
"@docusaurus/theme-mermaid": "^3.6.2",
"@easyops-cn/docusaurus-search-local": "^0.40.1",
"@giscus/react": "^3.0.0",
"@mdx-js/react": "^3.0.0",
@@ -29,9 +29,9 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.6.1",
"@docusaurus/tsconfig": "^3.6.1",
"@docusaurus/types": "^3.6.1",
"@docusaurus/module-type-aliases": "^3.6.2",
"@docusaurus/tsconfig": "^3.6.2",
"@docusaurus/types": "^3.6.2",
"typescript": "~5.2.2"
},
"browserslist": {
@@ -54,4 +54,4 @@
"@docusaurus/core": "^3.2.1"
}
}
}
}