c# - TCP设置时间通常这么慢(1秒)吗?

标签 c# sockets tcp tcpclient

我的情况是我有一百个小的文本文件,我想加载,解析和存储在DLL中。 DLL的客户端是临时的(命令行程序),我希望不要在每次命令行调用时都重新加载数据。

所以,我想我会写一个Windows服务器来存储数据,并让客户端使用TCP查询服务器。但是,TCP性能确实很慢。我使用Stopwatch编写了以下代码来测量套接字设置时间。

    // time the TCP interaction to see where the time goes
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    // create and connect socket to remote host
    client = new TcpClient (hostname, hostport); // auto-connects to server
    Console.WriteLine ("Connected to {0}",hostname);

    // get a stream handle from the connected client
    netstream = client.GetStream();

    // send the command to the far end
    netstream.Write(sendbuf, 0, sendbuf.Length);
    Console.WriteLine ("Sent command to far end: '{0}'",cmd);
    stopwatch.Stop();
    sendTime = stopwatch.ElapsedMilliseconds;

令我惊讶的是,这些代码的执行花费了1,037毫秒(1秒)。我希望时间会短得多。这是在现代Windows 10本地主机上运行的客户端和服务器之间的正常套接字设置时间吗?

为了进行比较,我编写了一个循环,该循环加载了10个文件x每个文件100行,而该实验仅用了1毫秒。因此,从磁盘(SSD)读取数据的速度比使用服务器套接字的速度快1000倍。

我知道在我的场景中该做什么(每次调用都使用文件读取),但是我想知道是否有人可以确认套接字设置时间的这些时间安排。也许本地计算机有更快的进程间通信机制,可以与文件读取/解析相比。我真的不想相信File.ReadAllLines(filepath)是分布在数百个命令行客户端调用中的最快方法。

编辑-通过使用显式IPEndPoint地址避免DNS查找

按照下面的注释,我用IPEndpoint方法替换了“localhost”以建立连接。更改将1037毫秒减少到大约20毫秒,但是(1)TcpClient无法自动连接,并且(2)文本发送无法到达服务器。因此,原始方法和IPEndPoint方法之间必须有所不同。
// new IPEndPoint method
// fast at 20ms, but the server never sees the sent text
string serverIP = "127.0.0.1";
IPAddress address = IPAddress.Parse (serverIP);
IPEndPoint remoteEP = new IPEndPoint(address, hostport);
client = new TcpClient(remoteEP);
client.Connect (remoteEP);  // new; required w IPEndPoint method

// send text command to the far end
netstream = client.GetStream();
netstream.Write(sendbuf, 0, sendbuf.Length);
Console.WriteLine ("Sent command to far end: '{0}'",cmd);
stopwatch.Stop();
sendTime = stopwatch.ElapsedMilliseconds;
Console.WriteLine ($"Milliseconds for sending by TCP:  '{sendTime}'");

// unfortunately, the server never sees the sent text now

我不知道为什么将IPEndPoint用作TcpClient的输入参数,而TcpClient之前会自动连接,为什么需要显式连接。而且我也不知道为什么netstream.Write现在也失败了。网络上的示例始终将socket.Connectsocket.Send与IPEndPoints一起使用。

编辑#2-将IPEndPoint与套接字而不是流一起使用
// use sockets, not streams
// This code takes 3 seconds to send text to the server
// But at least this code works. The original code was faster at 1 second.       
string serverIP = "127.0.0.1";
IPAddress address = IPAddress.Parse(serverIP);
IPEndPoint remoteEP = new IPEndPoint(address, hostport);
socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream,
                 ProtocolType.Tcp);
socket.Connect (remoteEP);
socket.Send (sendbuf);

编辑#3-经过基于Evk的实验后,评论:

使用上面Evk提供的信息,我做了如下几个实验。使用了三个客户端和两个服务器。
Client 1: DNS returns only IPv4 using new TcpClient().
Client 2: DNS returns only Ipv6 using new TcpClient(AddressFamily.InternetworkV6)
Client 3: DNS returns IPv4 and IPv6 using new TcpClient(“localhost”,port)
Server 1: IPv4 new TcpListener(IPAddress.Loopback, port)
Server 2: IPv6 new TcpListener(IPAddress.IPv6Loopback, port)

从最差到最佳,这6对可能返回以下结果:

c4xs6-带有服务器2 ip6的客户端1 ip4 –主动拒绝了连接。

c6xs4-带有服务器1 ip4的客户端2 ip6 –连接被主动拒绝。

c46xs4-带有服务器1 ip4的客户端3(两者)始终延迟1000毫秒,因为客户端在超时之前尝试使用IPv6并尝试ip4一直有效。这是这篇文章中的原始代码。

C46xs6-带有服务器2 ip6的客户端3(两者)都重新启动后,在第一次尝试(21毫秒)和随后的间隔较近的尝试中速度很快。但是在等待一到三分钟后,下一次尝试是3000毫秒,紧接着间隔很近的后续尝试又快了20毫秒。

C4xs4 –与上述行为相同。重新启动后的首次尝试速度很快,后续尝试间隔很近。但是,在等待一两分钟后,下一次尝试为3000毫秒,然后进行快速(20毫秒)间隔很近的后续尝试。

C6xS6 –与上述行为相同。重新启动服务器后很快,但是在一两分钟后,延迟尝试(3000ms),然后是对间隔很近的尝试的快速响应(20ms)。

我的实验表明,随着时间的推移,没有始终如一的快速响应。连接闲置时,必须有某种延迟,超时或 sleep 行为。我使用netstream.Close; client.Close();关闭每次尝试的每个连接。 (是吗?)我不知道是在一两分钟的空闲无 Activity 时间之后导致延迟响应的原因。

一分钟或两分钟的空闲收听时间后,可能导致延迟的原因是什么吗?据说客户端已退出控制台程序,但已不在系统内存中。该服务器据说没有做任何新的事情,只是在监听另一个连接。

最佳答案

否,不希望在1秒内建立与localhost的连接。您遇到的问题不是DNS查找本身。本地主机的DNS查找不会花费时间(可能只有几毫秒),并且肯定不会花费1秒。下面,我假设您的TCP服务器仅绑定(bind)到IpV4回送(127.0.0.1),例如:

var server = new TcpListener(IPAddress.Loopback, port);

当您像这样初始化客户端时:
new TcpClient("localhost", port)

它查询DNS(不花时间),DNS返回2个IP地址:::1(IpV6本地主机)和127.0.0.1(IpV4本地主机)。不知道是否需要使用IpV4或IpV6地址。因此,它会尝试两者(优先选择IpV6)。您观察到的1秒钟延迟是它需要意识到与::1(IpV6 localhost)的连接失败的时间。

如果您像这样初始化客户端:
var client = new TcpClient();

与以下内容相同:
// InterNetwork means IpV4
var client = new TcpClient(AddressFamily.InterNetwork);

这两个版本都将客户端绑定(bind)到本地IpV4套接字。这意味着您以后再执行以下操作:
client.Connect("localhost", port);

客户端无需尝试使用IpV6本地主机地址,因为本地套接字是IpV4。这两个版本都将消除您观察到的1秒延迟。消除延迟的另一种方法是将服务器绑定(bind)到ipv6回送(绑定(bind)到IPAddress.IPv6Loopback)。

注意:
IPEndPoint remoteEP = new IPEndPoint(address, hostport);
client = new TcpClient(remoteEP);

只是错了。 TcpClient构造函数的此重载需要本地端点,而不是远程端点。在您的示例中,应该只是在客户端或服务器上引发异常(端口已在使用中),因为您正试图绑定(bind)到服务器和客户端上的相同ip和端口。如果您想直接连接而不进行DNS查找(无论如何,localhost都花费0的时间,但是当您连接到真实服务器时可能很重要),请执行以下操作:
IPEndPoint remoteEP = new IPEndPoint(address, hostport);
client = new TcpClient();
client.Connect(remoteEP);

关于c# - TCP设置时间通常这么慢(1秒)吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49341481/

相关文章:

c# - 您可以忽略特定版本的强命名引用吗?

C# 属性 : how to use custom set property without private field?

c# - 关闭最后一个连接后,如何通过套接字监听传入连接?

sockets - hping 发送 SYN : how not to send RST after receiving SYN/ACK?

c# - 通过 Task Scheduler 的 Entity Framework 连接作为 NTAuthority/Anonymous Logon 连接

c# - 在 WinForms 应用程序中显示 List<String> 的最快方法?

linux - 两个进程之间的通信是否只有一个Unix Domain Socket?

linux - JBoss 7 HTTPS 连接在手动运行时可以工作,但作为 Linux 服务在 8080 上运行

c++ - 为 C++ 中的应用程序最佳使用而打开的并行套接字/TCP 连接数

c# - 不同端口上 TcpClient 上的多个流?