---
id: createtcpclient
title: 创建TcpClient
---


## 一、说明

TcpClient是Tcp系客户端基类，他直接参与tcp的连接、发送、接收、处理、断开等，他的业务与服务器的**SocketClient**是一一对应的。

## 二、特点

- 简单易用。
- IOCP多线程。
- 内存池支持
- 高性能
- 适配器预处理，一键式解决**分包**、**粘包**、对象解析(如HTTP，Json)等。
- 超简单的同步发送、异步发送、接收等操作。
- 基于委托、插件驱动，让每一步都能执行AOP。

## 三、产品应用场景

- 所有Tcp基础使用场景：可跨平台、跨语言使用。
- 自定义协议解析场景：可解析任意数据格式的TCP数据报文。

## 四、可配置项

<details>
<summary>可配置项</summary>
<div>

#### SetBufferLength

发送、接收缓存容量（单位：byte），默认1024×64。设置建议：

1. 如果数据包较小，建议10k左右的值。更加节约内存。
2. 如果数据包较大，例如文件传输等，建议64k，甚至更大的值。
3. 该值虽然无上限，但是一般不要超过1Mb，不然不仅没意义，还很浪费

#### SetMaxPackageSize

数据包最大值（单位：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，支持域名。支持类型：
1. 使用IP&Port，传入形如：127.0.0.1:7789的字符串即可。
2. 使用域名，必须包含协议类型，形如：http://baidu.com或者https://baidu.com:80

#### SetClientSslOption
客户端Ssl配置，为Null时则不启用。
注意，当RemoteIPHost使用https、wss的域名时，该配置会使用系统默认配置生效。

#### SetKeepAliveValue
为Socket设置的属性。
注意：该配置仅在window平台生效。

#### SetBindIPHost
绑定端口。
- 在UdpSessionBase中表示本地监听地址
- 在TcpClient中表示固定客户端端口号。

#### UseDelaySender
使用延迟发送。众所周知，tcp数据报文为了发送效率，会默认启用延迟算法。但是这种设置，只能一定程度的缓解小数据发送效率低的问题，因为它为了保证多线程发送的有序性，在send函数中设置了线程同步，所以说，每调用一次send，实际上都是巨大的性能消耗（此处用iocp发送亦然）。所以，要解决该问题， 最终还是要将小数据，组合成大数据，这样才能更高效率的发送。所以，DelaySender正是负责此类工作的。

使用DelaySender，会一定程度的降低发送的及时性，但是降低程度并不高，简单来说：

如果一个包大于512kb，则不会延迟，直接发送。
如果发送第一个包，与第二个包的时间间隔小于一个线程池线程调度的时间（这个时间极短，一般来说会在10微秒左右），则会将这两个包压缩为一个包发送。

#### UseNoDelay
设置Socket的NoDelay属性，默认false。

#### UseBroadcast
该值指定可以发送或接收广播数据包。

</div>
</details>

## 五、支持插件

支持**ITcpPlugin**接口，或者继承自**TcpPluginBase**类，重写相应方法即可。

|  插件方法| 功能 |
| --- | --- |
| OnConnecting | 在Socket完成初始化，但是并未连接时触发。 |
| OnConnected | 在Socket完成连接，且成功后触发 |
| OnDisconnecting | 当客户端主动调用Close时触发 |
| OnDisconnected | 当客户端断开连接后触发 |
| OnReceivingData | 在收到原始数据时触发，所有的数据均在ByteBlock里面。 |
| OnReceivedData | 在收到适配器数据时触发，根据适配器类型，数据可能在ByteBlock或者IRequestInfo里面。 |
| OnSendingData | 当即将发送数据时，调用该方法在适配器之后，接下来即会发送数据。 |

## 六、创建TcpClient

简单的处理逻辑可通过**Connecting**、**Connected**、**Received**等委托直接实现。

代码如下：

```csharp
TcpClient tcpClient = new TcpClient();
tcpClient.Connected += (client, e) => { };//成功连接到服务器
tcpClient.Disconnected += (client, e) => { };//从服务器断开连接，当连接不成功时不会触发。
tcpClient.Received += (client, byteBlock, requestInfo) =>
{
    //从服务器收到信息
    string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
    Console.WriteLine($"接收到信息：{mes}");
};

//声明配置
TouchSocketConfig config = new TouchSocketConfig();
config.SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
    .UsePlugin();

//载入配置
tcpClient.Setup(config);
tcpClient.Connect();
tcpClient.Send("RRQM");
```

## 七、接收数据

在TcpClient中，接收数据的方式有很多种。多种方式可以组合使用。

### 7.1 Received委托处理

当使用TcpClient创建客户端时，内部已经定义好了一个外置委托Received，可以通过该委托直接接收数据。

```csharp
TcpClient tcpClient = new TcpClient();
tcpClient.Received += (client, byteBlock, requestInfo) =>
{
    //从服务器收到信息
    string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
    Console.WriteLine($"接收到信息：{mes}");
};

//声明配置
TouchSocketConfig config = new TouchSocketConfig();
config.SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
    .UsePlugin();

//载入配置
tcpClient.Setup(config);
tcpClient.Connect();
```

### 7.2 插件处理推荐

按照TouchSocket的设计理念，使用插件处理数据，是一项非常简单，且高度解耦的方式。步骤如下：

1. 服务器配置启用插件（UsePlugin）
2. 新建插件类
3. 添加插件

代码如下：

（1）声明插件

```csharp
public class MyPlugin : TcpPluginBase<TcpClient>
{
    public MyPlugin()
    {
        this.Order = 0;//此值表示插件的执行顺序，当多个插件并存时，该值越大，越在前执行。
    }
   
    protected override void OnReceivedData(TcpClient client, ReceivedDataEventArgs e)
    {
        //这里处理数据接收
        //根据适配器类型，e.ByteBlock与e.RequestInfo会呈现不同的值，具体看文档=》适配器部分。
        ByteBlock byteBlock = e.ByteBlock;
        IRequestInfo requestInfo = e.RequestInfo;

        //e.Handled = true;//表示该数据已经被本插件处理，无需再投递到其他插件。
        base.OnReceivedData(client, e);
    }
}
```

（2）创建使用插件处理的客户端

```csharp
TcpClient client = new TcpClient();
client.Setup(new TouchSocketConfig()
    .SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
    .UsePlugin()
    .ConfigureContainer(a=>
    {
        a.AddConsoleLogger();
    })
    .ConfigurePlugins(a => 
    {
        a.Add<MyPlugin>();
    }))
    .Connect();
```

## 八、发送数据

【同步发送】

TcpClient已经内置了三种同步发送方法，直接调用就可以发送，但需要注意的是，通过该方法发送的数据，会经过**适配器**，如果想要直接发送，请使用**DefaultSend**。如果发送失败，则会立即抛出异常。

```csharp
public virtual void Send(byte[] buffer);
public virtual void Send(ByteBlock byteBlock);
public virtual void Send(byte[] buffer, int offset, int length);
```

【异步发送】

TcpClient已经内置了三种异步发送方法，直接调用就可以发送。如果发送失败，await就会触发异常。

```csharp
public virtual Task SendAsync(byte[] buffer);
public virtual Task SendAsync(byte[] buffer, int offset, int length);
```

