linux tcp fast open,.NET Core 使用 TCP Fast Open (Linux & Windows)

TFO 已经不是个新技术了,哪怕是计算机网络方面最守旧的 Windows 如今也支持了 TFO(虽然只支持客户端,而且疑似不符合规范并且很多 bug)。最近正好做项目时用到了 TFO,因此总结一下 .NET Core 使用 TFO 的方法。

服务端

目前只有 Linux 对 TFO 服务端支持较好,因此我们暂时也只用考虑 Linux。

服务端对应用程序而言支持起来也是最简单的,只需要调用一次setsockopt就可以启用。

可以写一个扩展方法

const int TCP_FASTOPEN_LINUX = 23;

[DllImport("libc.so.6", SetLastError = true)]

static extern unsafe int setsockopt(int sockfd, int level, int optname, void* optval, int optlen);

public static unsafe void EnableLinuxFastOpenServer(this Socket socket)

{

int qlen = 5;

int result = setsockopt(

(int)socket.Handle,

6 /* SOL_TCP */,

TCP_FASTOPEN_LINUX,

&qlen, sizeof(int));

if (result == -1)

{

throw new SocketException(Marshal.GetLastWin32Error());

}

}

对 Socket 调用即可,以 TcpListener 为例

var listener = new TcpListener(endpoint);

listener.Server.EnableLinuxFastOpenServer();

listener.Start();

调用前需要检查 /proc/sys/net/ipv4/tcp_fastopen 的值是否包含 0x2 这个flag。如果不包含则会抛异常。

客户端

Windows

Windows 的 TCP Fast Open 客户端实现是设置 Socket option 之后,调用 Windows Server 2003 / Windows Vista 加入的 ConnectEx,在 lpSendBuffer 中送入初始 buffer。

.NET 并没有任何文档说明如何调用 ConnectEx,不过阅读 CoreFX 和 .NET Framework Reference Source 可以发现, Socket.ConnectAsync(SocketAsyncEventArgs)使用了 ConnectEx,并且将传入的SocketAsyncEventArgs的buffer传递给了ConnectEx。

这样一来我们可以编写一个简单的async扩展方法,实现 Windows 上的 TFO。

const int TCP_FASTOPEN_WINDOWS = 15;

public static void EnableWindowsFastOpenClient(this Socket socket)

{

socket.SetSocketOption(

SocketOptionLevel.Tcp,

(SocketOptionName)TCP_FASTOPEN_WINDOWS,

1);

}

public static Task ConnectAsync(this Socket socket, EndPoint remoteEndPoint, ArraySegment initialData)

{

var tcs = new TaskCompletionSource();

var eventArgs = new SocketAsyncEventArgs();

eventArgs.Completed += (s, e) =>

{

if (e.SocketError == SocketError.Success)

{

tcs.TrySetResult(null);

}

else

{

tcs.TrySetException(new SocketException((int)e.SocketError));

}

};

eventArgs.RemoteEndPoint = remoteEndPoint;

eventArgs.UserToken = tcs;

eventArgs.SetBuffer(initialData.Array, initialData.Offset, initialData.Count);

if (socket.ConnectAsync(eventArgs))

{

return tcs.Task;

}

else

{

return Task.CompletedTask;

}

}

先对Socket调用EnableWindowsFastOpenClient,再调用ConnectAsync即可。如果使用的是低版本的Windows,或用户关闭了 TFO,则EnableWindowsFastOpenClient会抛出异常。

Linux

Linux 有两种 TFO API,一种是使用 sendto+MSG_FASTOPEN 建立连接,支持 3.7+的内核;另一种是使用 TCP_FASTOPEN_CONNECT,支持 4.11+的内核。

.NET 的 Socket 在 Linux 上是一层比较厚的封装,因为参数都是原封不动地从 Winsock 复制站贴,在 Linux 上需要进行转换,自然也不支持调用 sendto 传入 MSG_FASTOPEN;而直接把 fd 拿出来调用原生 sendto 也很麻烦,会破坏一些 managed Socket 的状态,比如私有的 _connected 等,为后续使用带来麻烦。类似的问题还有 macOS 的 TFO,需要使用 connectx() 建立连接,只能等 .NET Core 支持。

相比之下 TCP_FASTOPEN_CONNECT 则是对 Socket 调用者而言完全透明的,它的原理是调用 connect() 时不立刻发送 SYN,而是在第一次调用 send 时发送 SYN (TFO=C) 和 data,此时才真正建立连接。

因此需要做的事情跟 Server 端一样,使用以下扩展方法完成

const int TCP_FASTOPEN_CONNECT = 30;

public static unsafe void EnableLinuxFastOpenConnect(this Socket socket)

{

int val = 1;

int result = setsockopt(

(int)socket.Handle,

6 /*SOL_TCP*/,

TCP_FASTOPEN_CONNECT,

&val, sizeof(int));

if (result == -1)

{

throw new SocketException(Marshal.GetLastWin32Error());

}

}

使用EnableLinuxFastOpenConnect()之后照常使用 Socket API 即可。需要注意的是 Connect 之后一定要保证调用 SendAsync 至少一次,否则连接不会真正建立,此时调用 ReceiveAsync 是不会返回的。

调用 EnableLinuxFastOpenConnect() 之前需要检查 /proc/sys/net/ipv4/tcp_fastopen 的值是否包含 0x1 flag;除此之外还需要检查内核版本是否符合要求(>4.11),可以通过Environment.OSVersion.Version > new Version(4, 11, 0, 0) 判断。

.net 稳定 高效 易用 可同步 TCP 通信框架 使用平台: WinXP,WIN7,WIN8,WINCE,WINPHONE。 使用.net 2.0 框架。 主要功能介绍: 1、可以代替 Oracle,Mysql客户端 在不安装Oracle,MySql客户端的情况下访问, 对数据库进行间接访问(需开始框架的服务器端)。 2、可以使本来没有网经功能的Sqlite具有网络访问的能力。(也是需要开启服务器端) 以上两点可以兼容现有代码生成器时,客户端代码仅需要特别小的改动就可以。 3、基本功能。可以实现聊天,传文件,图片。 4、使用长连接,有断线自动连接功能,心跳包。 5、使用自定义数据包协议,自建Session机制加强数据连接安全。 6、框架稳定,支持高并发。 7、简单的事件处理机制。使用更加简单。 8、支持同步处理,使程序的开发更架简单,不需要另行回调处理。 下载地址: 使用方式: 首选需要 引用 DataUtils.v1.1.dll。DataUtils 内包含客户端与服务器端 处理类。 1、服务器端 代码示例。 设置服务器端默认端口 ,不设置端口会使用默认端口 TcpSettings.DefultPort = 8511; 既可以使用静态默认对象,也可以创建服务器端对象。 SocketListener server= new SocketListener(); 对象创建后 注册一些事件,以接收客户端发送的信息。 SocketListener.Server.RegeditSession += new Feng.Net.Tcp.SocketListener.RegeditSessionEventHandler(server_RegeditSession); RegeditSession 事件用于是否允许客户端连接此服务器。可以使用用户名,密码的核对方式。 SocketListener.Server.DataReceive += new SocketListener.DataReceiveEventHandler(server_DataReceive); DataReceive 在这个事件里处理接收到的数据。 事件注册完成就可以打开监听 SocketListener.Server.StartListening(); 2、客户端 代码示例 设置服务器的IP地址 TcpSettings.DeafultIPAddress = "192.168.1.3"; TcpSettings.DefultPort = 8511;//不设置端口会使用默认端口。 这样就可以使用默认的静态客户端了。 也可以自己创建对象。 客户端创建后需要在Connected事件注册用户,以限制某些用户是否可以使此链接。用户来源可以是数据库等。 void client_Connected(object sender, SocketClient sh) { Client.RegeditSession("aaa", "bbb"); } 发送文字消息给其他用户 SocketClient.Client.SendToOtherUser(string user, string text); //USER代表发达的目白用户,text表示为发送的内容。 发送图片,音频,视屏可以使用 SocketClient..SendToOtherUser(string user, byte[] data)////USER代表发达的目白用户,data表示为发送的内容。 data数据中数据有多种类型时可以使用 using (Feng.IO.BufferWriter bw = new Feng.IO.BufferWriter()) { bw.WriteBitmap(new Bitmap(100, 100)); bw.Write(text);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值