...
 
Commits (9)
    https://gitcode.net/qq_40374647/RRQMSocket/-/commit/b8c779166cd9faf038c1ed17e4b9a77b49bdc0dd 修复示例demo 2023-05-29T19:57:32+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/46459aceaa18e35acc4b5ecd21e01f34d69ac02b 更新demo 2023-05-31T13:08:58+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/5066469901ec6a3720fb4b7cc1f5cc7038e9d676 添加cookie示例 2023-06-21T10:01:55+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/fba8ba0a9e149e08ce3be008572a5770879ac8bd 更新tcp服务器的创建 2023-06-23T23:42:35+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/543bdc0337a431832b270e2d1f4a2f3249f9be8f 增加动态添加、移除监听配置 文档 2023-06-23T23:59:03+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/60a762165aeb2c80493f909f51db6c8da2a23b33 更新waitingclient 2023-06-24T22:24:45+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/d070bc4acc7512c176163a369f2e2b4c467f9857 更新resetid文档 2023-06-24T22:30:31+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/b985b31891e52e0b689683873ef44d1a685d7e00 更新文档 2023-06-24T23:59:25+08:00 若汝棋茗 505554090@qq.com https://gitcode.net/qq_40374647/RRQMSocket/-/commit/5e8cc4e4bcde35d3b93dc9a6bd09c71e14ee9a6f 更新demo 2023-07-04T16:33:39+08:00 若汝棋茗 505554090@qq.com
......@@ -27,7 +27,6 @@ namespace TouchRpcClientApp
//后续参数为调用参数。
bool result = client.Invoke<bool>("touchrpcserverapp.myrpcserver.login", InvokeOption.WaitInvoke, textBox1.Text, textBox2.Text);
MessageBox.Show(result.ToString());
client.SafeDispose();//client是长连接,可以复用,但在此处使用短连接。
}
......
......@@ -10,11 +10,11 @@ namespace RpcClassLibrary.ServerInterface
/// <summary>
/// 定义服务接口。
/// </summary>
[GeneratorRpcProxy(Prefix = "RpcClassLibrary",MethodFlags = MethodFlags.IncludeCallContext)]
public interface IUserServer:IRpcServer
[GeneratorRpcProxy(MethodFlags = MethodFlags.IncludeCallContext)]
public interface IUserServer : IRpcServer
{
[GeneratorRpcMethod]
[TouchRpc]
LoginResponse Login(ICallContext callContext,LoginRequest request);
[TouchRpc(MethodFlags = MethodFlags.IncludeCallContext)]
LoginResponse Login(ICallContext callContext, LoginRequest request);
}
}
......@@ -167,5 +167,11 @@ namespace WSClientApp
break;
}
}
protected override void OnHandshaking(WebSocketClient client, HttpContextEventArgs e)
{
e.Context.Request.Headers["cookie"] = "";
base.OnHandshaking(client, e);
}
}
}
\ No newline at end of file
......@@ -54,6 +54,13 @@ namespace WebSocketConsoleApp
case WSDataType.Text:
Console.WriteLine(e.DataFrame.ToText());
((HttpSocketClient)client).SendWithWS(e.DataFrame.ToText());
//using (ByteBlock byteBlock=new ByteBlock(1024*64))//估算一下你的数据大小
//{
// WSDataFrame dataFrame = new WSDataFrame();
// dataFrame.BuildResponse(byteBlock);
// ((HttpSocketClient)client).SendWithWS(byteBlock);
//}
break;
case WSDataType.Binary:
......
......@@ -3,6 +3,7 @@ id: createtcpclient
title: 创建TcpClient
---
import Tag from "@site/src/components/Tag.js";
## 一、说明
......@@ -41,56 +42,14 @@ TcpClient是Tcp系客户端基类,他直接参与tcp的连接、发送、接
数据包最大值(单位:byte),默认1024×1024×10。该值会在适当时间,直接作用DataHandlingAdapter.MaxPackageSize。
#### SetThreadCount
多线程数量。该值在Auto模式下指示线程池的最少线程数量和IO线程数量。
设置建议:
1. 异步处理接收数据,此时线程数量设置为内核线程左右的值即可。
2. 同步处理接收数据,此时应当考虑两个因素。该操作是否为耗时操作,如果是,则该值在允许范围内,应当设置更可能大的值。如果不是,则设置为内核线程左右的值即可。
#### SetGetDefaultNewID
配置初始ID的分配策略
#### SetListenIPHosts
监听IP和端口号组,可以一次性设置多个地址。
#### SetServerName
服务器标识名称,无实际使用意义。
#### SetBacklogProperty
Tcp半连接挂起连接队列的最大长度。默认为30
#### SetMaxCount
最大可连接数,默认为10000
#### SetReceiveType
接收类型。
- AUTO:自动接收模式。
- None:不投递IO接收申请,用户可通过GetStream,获取到流以后,自己处理接收。注意:连接端不会感知主动断开。
#### UsePlugin
是否启用插件。在启用时或许会带来一点点性能损耗,基本上不是千万数据交互根本不值一提。
#### SetServiceSslOption
Ssl配置,为Null时则不启用。
#### UseNoDelay
设置Socket的NoDelay属性,默认false。
#### UseDelaySender
使用延迟发送。众所周知,tcp数据报文为了发送效率,会默认启用**延迟算法**。但是这种设置,只能一定程度的缓解小数据发送效率低的问题,因为它为了保证多线程发送的有序性,在send函数中设置了线程同步,所以说,每调用一次send,实际上都是巨大的性能消耗(此处用iocp发送亦然)。所以,要解决该问题, 最终还是要将小数据,组合成大数据,这样才能更高效率的发送。所以,DelaySender正是负责此类工作的。
使用DelaySender,会一定程度的降低发送的及时性,但是降低程度并不高,简单来说:
1. 如果一个包大于512kb,则不会延迟,直接发送。
2. 如果发送第一个包,与第二个包的时间间隔小于一个线程池线程调度的时间(这个时间极短,一般来说会在10**微秒**左右),则会将这两个包压缩为一个包发送。
#### UseReuseAddress
启用端口复用。该配置可在服务器、或客户端在监听端口时,运行监听同一个端口。可以一定程度缓解端口来不及释放的问题。
#### SetRemoteIPHost
链接到的远程IPHost,支持域名。支持类型:
......@@ -121,54 +80,102 @@ Ssl配置,为Null时则不启用。
#### UseNoDelay
设置Socket的NoDelay属性,默认false。
#### UseBroadcast
该值指定可以发送或接收广播数据包。
</div>
</details>
## 五、支持插件
支持**ITcpPlugin**接口,或者继承自**TcpPluginBase**类,重写相应方法即可。
| 插件方法| 功能 |
| --- | --- |
| OnConnecting | 在Socket完成初始化,但是并未连接时触发。 |
| OnConnected | 在Socket完成连接,且成功后触发 |
| OnDisconnecting | 当客户端主动调用Close时触发 |
| OnDisconnected | 当客户端断开连接后触发 |
| OnReceivingData | 在收到原始数据时触发,所有的数据均在ByteBlock里面。 |
| OnReceivedData | 在收到适配器数据时触发,根据适配器类型,数据可能在ByteBlock或者IRequestInfo里面。 |
| OnSendingData | 当即将发送数据时,调用该方法在适配器之后,接下来即会发送数据。 |
| ITcpConnectingPlugin | 此时Socket实际上已经完成连接,但是并没有启动接收,然后触发。 |
| ITcpConnectedPlugin | 同意连接,且成功启动接收后触发 |
| ITcpDisconnectingPlugin | 当客户端主动调用Close时触发 |
| ITcpDisconnectedPlugin | 当客户端断开连接后触发 |
| ITcpReceivingPlugin | 在收到原始数据时触发,所有的数据均在ByteBlock里面。 |
| ITcpReceivedPlugin | 在收到适配器数据时触发,根据适配器类型,数据可能在ByteBlock或者IRequestInfo里面。 |
| ITcpSendingPlugin | 当即将发送数据时,调用该方法在适配器之后,接下来即会发送数据。 |
| IIdChangedPlugin | 当SocketClient的ID发生改变时触发。 |
## 六、创建TcpClient
#### 6.1 简单创建
简单的处理逻辑可通过**Connecting**、**Connected**、**Received**等委托直接实现。
代码如下:
```csharp
TcpClient tcpClient = new TcpClient();
tcpClient.Connected += (client, e) => { };//成功连接到服务器
tcpClient.Disconnected += (client, e) => { };//从服务器断开连接,当连接不成功时不会触发。
tcpClient.Received += (client, byteBlock, requestInfo) =>
tcpClient.Connecting = (client, e) => { };//即将连接到服务器,此时已经创建socket,但是还未建立tcp
tcpClient.Connected = (client, e) => { };//成功连接到服务器
tcpClient.Disconnecting = (client, e) => { };//即将从服务器断开连接。此处仅主动断开才有效。
tcpClient.Disconnected = (client, e) => { };//从服务器断开连接,当连接不成功时不会触发。
tcpClient.Received = (client, byteBlock, requestInfo) =>
{
//从服务器收到信息
//从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
Console.WriteLine($"接收到信息:{mes}");
tcpClient.Logger.Info($"客户端接收到信息:{mes}");
};
//声明配置
TouchSocketConfig config = new TouchSocketConfig();
config.SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
.UsePlugin();
//载入配置
tcpClient.Setup(config);
tcpClient.Connect();
tcpClient.Setup(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个日志注入
}));
tcpClient.Connect();//调用连接,当连接不成功时,会抛出异常。
//Result result = tcpClient.TryConnect();//或者可以调用TryConnect
//if (result.IsSuccess())
//{
//}
tcpClient.Logger.Info("客户端成功连接");
tcpClient.Send("RRQM");
```
#### 6.2 继承实现
一般继承实现的话,可以从TcpClientBase继承。
```csharp
class MyTcpClient: TcpClientBase
{
protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
{
//此处逻辑单线程处理。
//此处处理数据,功能相当于Received委托。
string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
Console.WriteLine($"已接收到信息:{mes}");
return true;//返回true,则表示该数据已被当前处理。不会再触发后续的插件等。
}
}
```
```csharp
MyTcpClient tcpClient = new MyTcpClient();
tcpClient.Connecting = (client, e) => { };//即将连接到服务器,此时已经创建socket,但是还未建立tcp
tcpClient.Connected = (client, e) => { };//成功连接到服务器
tcpClient.Disconnecting = (client, e) => { };//即将从服务器断开连接。此处仅主动断开才有效。
tcpClient.Disconnected = (client, e) => { };//从服务器断开连接,当连接不成功时不会触发。
//载入配置
tcpClient.Setup(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个日志注入
}));
tcpClient.Connect();//调用连接,当连接不成功时,会抛出异常。
```
## 七、接收数据
在TcpClient中,接收数据的方式有很多种。多种方式可以组合使用。
......@@ -196,27 +203,25 @@ tcpClient.Setup(config);
tcpClient.Connect();
```
### 7.2 插件处理推荐
### 7.2 插件处理 <Tag>推荐</Tag>
按照TouchSocket的设计理念,使用插件处理数据,是一项非常简单,且高度解耦的方式。步骤如下:
1. 服务器配置启用插件(UsePlugin)
2. 新建插件类
3. 添加插件
(1)声明插件
代码如下:
插件可以先继承`PluginBase`,然后再实现需要的功能插件接口,可以按需选择泛型或者非泛型实现。
(1)声明插件
如果已经有继承类,直接实现`IPlugin`接口即可。
```csharp
public class MyPlugin : TcpPluginBase<TcpClient>
public class MyPlugin : PluginBase,ITcpReceivedPlugin<TcpClient>
{
public MyPlugin()
{
this.Order = 0;//此值表示插件的执行顺序,当多个插件并存时,该值越大,越在前执行。
}
protected override void OnReceivedData(TcpClient client, ReceivedDataEventArgs e)
public void OnTcpReceived(TcpClient client, ReceivedDataEventArgs e)
{
//这里处理数据接收
//根据适配器类型,e.ByteBlock与e.RequestInfo会呈现不同的值,具体看文档=》适配器部分。
......@@ -224,7 +229,11 @@ public class MyPlugin : TcpPluginBase<TcpClient>
IRequestInfo requestInfo = e.RequestInfo;
//e.Handled = true;//表示该数据已经被本插件处理,无需再投递到其他插件。
base.OnReceivedData(client, e);
}
public Task OnTcpReceivedAsync(TcpClient client, ReceivedDataEventArgs e)
{
return Task.CompletedTask;
}
}
```
......@@ -234,13 +243,12 @@ public class MyPlugin : TcpPluginBase<TcpClient>
```csharp
TcpClient client = new TcpClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
.UsePlugin()
.ConfigureContainer(a=>
.SetRemoteIPHost("127.0.0.1:7789")
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
.ConfigurePlugins(a =>
{
a.Add<MyPlugin>();
}))
......@@ -268,3 +276,9 @@ public virtual Task SendAsync(byte[] buffer);
public virtual Task SendAsync(byte[] buffer, int offset, int length);
```
:::tip 提示
框架不仅内置了`Send`字节的发送,也扩展了字符串等常见数据的发送。而且还包括了`TrySend`等不会抛出异常的发送方法。
:::
此差异已折叠。
---
id: resetid
title: 服务器重置ID
title: 服务器重置id
---
## 一、说明
每个客户端在连接时,服务器都会为连接的客户端**新分配**一个唯一的ID。也就是说,在服务器中ID与SocketClient实例就是一一对应的。
每个客户端在连接时,服务器都会为连接的客户端**新分配**一个唯一的Id。也就是说,在服务器中Id与SocketClient实例就是一一对应的。
## 二、配置初始ID策略
## 二、配置初始Id策略
默认情况下服务器都会根据**历史连接数量**,为连接的客户端新分配ID。也就是说,第一个连接的,其ID就是1,以此类推。
默认情况下服务器都会根据**历史连接数量**,为连接的客户端新分配Id。也就是说,第一个连接的,其Id就是1,以此类推。
当然我们可以自由的定义ID策略,只需要在Config配置中,配置[SetGetDefaultNewID](../docs/createtcpservice.mdx#setgetdefaultnewid),自定义新ID来源即可。要求不和现连接的客户端ID重复。
当然我们可以自由的定义Id策略,只需要在Config配置中,配置[SetGetDefaultNewId](../docs/createtcpservice.mdx#setgetdefaultnewid),自定义新id来源即可。要求不和现连接的客户端id重复。
下列示例,就是使用Guid作为初始ID
下列示例,就是使用Guid作为初始Id
```csharp
.SetGetDefaultNewID(()=> { return new Guid().ToString(); })
var config = new TouchSocketConfig();
config.SetGetDefaultNewId(()=>Guid.NewGuid().ToString());
```
## 三、创建能代表连接的ID
## 三、创建能代表连接的Id
上述这种ID规范,是与连接信息没有任何关联的,这也就意味着,这种方式是无法关联SocketClient的。
上述这种Id规范,是与连接信息没有任何关联的,这也就意味着,这种方式是无法关联SocketClient的。
但往往,有时候,我们希望,SocketClient的ID,能一定程度的代表一些信息。例如:以客户端的IP和端口,作为唯一ID
但往往,有时候,我们希望,SocketClient的Id,能一定程度的代表一些信息。例如:以客户端的IP和端口,作为唯一id
那这时候,**服务器**可以订阅**Connecting**,然后,为新连接的SocketClient,设置与之有关联信息的ID
那这时候,**服务器**可以订阅**Connecting**,然后,为新连接的SocketClient,设置与之有关联信息的id
```csharp
m_service.Connecting = (client, e) =>
m_service.Connecting = (client, e) => //有客户端正在连接
{
e.ID = $"{client.IP}:{client.Port}";
};//有客户端正在连接
e.Id = $"{client.IP}:{client.Port}";
};
```
:::tip 提示
......@@ -40,23 +41,23 @@ m_service.Connecting = (client, e) =>
:::
## 四、即时修改ID
## 四、即时修改id
上述修改ID的方式,应该还不足以应对所有情况。有时候我们希望,在该连接完成,且经过某种验证之后再设置新的ID,那么我们可以通过**ResetID**的方法,来实现需求。
上述修改Id的方式,应该还不足以应对所有情况。有时候我们希望,在该连接完成,且经过某种验证之后再设置新的id,那么我们可以通过**ResetId**的方法,来实现需求。
### 4.1 通过Service直接修改
```csharp
service.ResetID("oldId","newId");
service.ResetId("oldId","newId");
```
### 4.2 通过SocketClient修改
```csharp
socketClient.ResetID("newId");
socketClient.ResetId("newId");
```
:::note 备注
上述的ID标识,仅仅是服务器(TcpService)和辅助客户端(SocketClient)之间的关联。与客户端(TcpClient)是没有任何关系的。
上述的Id标识,仅仅是服务器(TcpService)和辅助客户端(SocketClient)之间的关联。与客户端(TcpClient)是没有任何关系的。
:::
\ No newline at end of file
---
id: tcpcommonplugins
title: 常用插件
---
\ No newline at end of file
......@@ -18,6 +18,22 @@ import Tag from "@site/src/components/Tag.js";
:::
## v1.5
更新日期:未定
更新描述:大版本更新。
- &nbsp;<Tag>优化</Tag> FileLogger支持指定不同目录。
- &nbsp;<Tag>优化</Tag> IPHost支持从int、string直接隐式转换。
- &nbsp;<Tag>调整</Tag> TouchSocket所有“ID”属性,改名为“Id”。
- &nbsp;<Tag>调整</Tag> TouchSocket所有插件的执行顺序,移动至内部重写方法之后。
- &nbsp;<Tag>调整</Tag> TouchSocket所有`ResetID`改名为`ResetId`。
- &nbsp;<Tag>移除</Tag> UsePlugin的显式配置,当调用ConfigurePlugins时,会自动启用。
- &nbsp;<Tag>新增</Tag> ws协议的TouchRpc服务端,增加HttpContext上下文获取。
---
## v1.3
更新日期:2023.3.1
......
......@@ -17,20 +17,23 @@ title: 同步请求
### 2.1 以TcpClient为例
```csharp
TcpClient m_tcpClient = new TcpClient();
var config = new TouchSocketConfig();
config.SetRemoteIPHost(new IPHost("127.0.0.1:7789"));
//载入配置
m_tcpClient.Setup(config);
m_tcpClient.Connect();
TcpClient client = new TcpClient();
client.Connect("127.0.0.1:7789");
//调用GetWaitingClient获取到IWaitingClient的对象。
var waitClient = m_tcpClient.GetWaitingClient(new WaitingOptions()
var waitClient = client.GetWaitingClient(new WaitingOptions()
{
AdapterFilter = AdapterFilter.AllAdapter,//表示发送和接收的数据都会经过适配器
BreakTrigger = true,//表示当连接断开时,会立即触发
ThrowBreakException = true//表示当连接断开时,是否触发异常
},
response => //设置用于筛选的fun委托,当返回为true时,才会响应返回
{
if (response.Data.Length==1)
{
return true;
}
return false;
});
//然后使用SendThenReturn。
......@@ -54,6 +57,14 @@ service.Received = (client, byteBlock, requestInfo) =>
AdapterFilter = AdapterFilter.AllAdapter,//表示发送和接收的数据都会经过适配器
BreakTrigger = true,//表示当连接断开时,会立即触发
ThrowBreakException = true//表示当连接断开时,是否触发异常
},
response => //设置用于筛选的fun委托,当返回为true时,才会响应返回
{
if (response.Data.Length==1)
{
return true;
}
return false;
});
//然后使用SendThenReturn。
......
......@@ -31,7 +31,7 @@ export default function (props) {
},
移除: {
icon: "shanchu",
bgColor: "#666",
bgColor: "red",
},
答疑: {
icon: "dayi",
......