TCP 设置时间是否这么慢(1 秒),通常吗? [英] Are TCP setup times this slow (1 second), typically?

查看:36
本文介绍了TCP 设置时间是否这么慢(1 秒),通常吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的场景是我有一百个小文本文件,我想加载、解析并存储在 DLL 中.DLL 的客户端是瞬态的(命令行程序),我不想在每次命令行调用时重新加载数据.

My scenario is that I have a hundred small text files that I want to load, parse, and store in a DLL. Clients of the DLL are transient (command line programs), and I would prefer not to reload the data on every command line invocation.

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

So, I thought I would write a Windows server to store the data and have the clients query the server using TCP. But, the TCP performance was really slow. I wrote the following code using Stopwatch to measure the socket setup time.

    // 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 本地主机上运行的客户端和服务器之间的正常套接字设置时间吗?

Much to my surprise, that little bit of code took 1,037 milliseconds (1 second) to execute. I expected the time to be far smaller. Is that a normal socket setup time between a client and server running on a modern Windows 10 localhost?

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

To compare, I wrote a loop that loaded 10 files x 100 lines each, and that experiment only took 1ms. So, it was 1000x faster reading from disk (an SSD) than it was to use sockets to a server.

我知道在我的场景中要做什么(在每次调用时使用文件读取),但我想知道是否有人可以确认这些类型的套接字设置时间.或者,本地机器可能有更快的进程间通信机制,可以与文件读取/解析相媲美.我真的不想相信 File.ReadAllLines(filepath) 是分散在数百个命令行客户端调用中的最快方法.

I know what to do in my scenario (use file reads on each invocation), but I would like to know if anyone can confirm these kinds of timings for socket setup times. Or maybe there are faster interprocess communication mechanisms for a local machine that would compare favorably with file reads/parses. I really don't want to believe that File.ReadAllLines(filepath) is the fastest way when spread over hundreds of command-line client invocations.

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

EDIT - Avoid DNS lookup by using explict IPEndPoint address

根据下面的评论,我用 IPEndpoint 方法替换了localhost"以建立连接.更改将 1037 毫秒减少到大约 20 毫秒,但是 (1) TcpClient 不会自动连接,以及 (2) 发送文本未能到达服务器.所以,原始方法和 IPEndPoint 方法之间一定有什么不同.

Following the comments below, I replaced "localhost" with an IPEndpoint method to set up the connection. The change reduced the 1037ms to about 20ms, but (1) the TcpClient would not automatically connect, and (2) the sending of text failed to reach the server. So, there must be something different between the original and IPEndPoint methods.

// 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

当 TcpClient 之前会自动连接时,我不知道为什么使用 IPEndPoint 作为 TcpClient 的输入参数需要显式连接.而且我不知道为什么 netstream.Write 现在也失败了.网络上的示例总是使用带有 IPEndPoints 的 socket.Connectsocket.Send.

I don't know why using an IPEndPoint as an input argument to TcpClient requires an explicit connect when TcpClient would automatically connect before. And I don't know why the netstream.Write fails now too. Examples on the net always use socket.Connect and socket.Send with IPEndPoints.

编辑 #2 - 将 IPEndPoint 用于套接字,而不是流

EDIT #2 - Use IPEndPoint with sockets, not streams

// 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 评论进行实验后:

EDIT #3 - After experiments based on Evk comments:

利用上面Evk提供的信息,我做了如下几个实验.使用了三个客户端和两个服务器.

Using the information provided by Evk above, I did several experiments as follows. Three clients and two servers were used.

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 个可能的配对返回以下结果:

From worst to best, the 6 possible pairs returned the following results:

c4xs6 - 客户端 1 ip4 与服务器 2 ip6 - 主动拒绝连接.

c4xs6 - Client 1 ip4 with Server 2 ip6 – connection actively refused.

c6xs4 - 客户端 2 ip6 与服务器 1 ip4 - 主动拒绝连接.

c6xs4 - Client 2 ip6 with Server 1 ip4 – connection actively refused.

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

c46xs4 - Client 3 (both) with Server 1 ip4, always delayed 1000ms because client tried using IPv6 before timing out and trying ip4, which worked consistently. This was the original code in this post.

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

C46xs6 - Client 3 (both) with Server 2 ip6, after a fresh restart of both, was fast on the first try (21ms) and on subsequent closely-spaced tries. But after waiting a minute or three, the next try was 3000ms, followed by fast 20ms times on closely-spaced subsequent tries.

C4xs4 – 与上述相同的行为.重新启动后的第一次尝试很快,随后的密集尝试也是如此.但是等了一两分钟后,下一次尝试是 3000 毫秒,紧接着是快速(20 毫秒)密集的后续尝试.

C4xs4 – Same behavior as above. First try after a fresh restart was fast, as were closely-spaced subsequent tries. But after waiting a minute or two, the next try was 3000ms, followed by fast (20ms) closely-spaced subsequent tries.

C6xS6 – 与上述行为相同.新的服务器重新启动后很快,但一两分钟后,延迟尝试(3000 毫秒),然后是对密集尝试的快速响应(20 毫秒).

C6xS6 – Same behavior as above. Fast after a fresh server reboot, but after a minute or two, a delay try (3000ms) followed by fast (20ms) responses to closely-spaced tries.

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

My experiments showed no consistently fast responses over time. There must be some kind of a delay or timeout or sleeping behavior when the connections go idle. I use netstream.Close; client.Close(); to close each connection on each try. (Is that right?) I don’t know what could be causing the delayed responses after a minute or two of idle no-active-connection time.

知道在一两分钟空闲收听时间后可能导致延迟的原因是什么吗?客户端应该是在系统内存之外,已经退出了控制台程序.服务器应该没有做任何新的事情,只是在侦听另一个连接.

Any idea what might be causing the delay after a minute or two of idle listening time? The client is supposedly out of the system memory, having exited the console program. The server is supposedly doing nothing new, just listening for another connection.

推荐答案

不,1 秒建立到 localhost 的连接不是预期的性能.您的情况的问题不是 DNS 查找本身.本地主机的 DNS 查找不需要时间(可能只有几毫秒),当然也不可能需要 1 秒.下面我假设您的 TCP 服务器仅绑定到 IpV4 环回 (127.0.0.1),例如像这样:

No, 1 second to establish connection to localhost is not expected perfomance. The problem in your case is not DNS lookup by itself. DNS lookup of localhost takes no time (few milliseconds maybe) and certainly cannot take 1 second. Below I assume that your TCP server is bound only to IpV4 loopback (127.0.0.1), for example like this:

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

当你像这样初始化客户端时:

When you initialize client like this:

new TcpClient("localhost", port)

它查询 DNS(不需要时间),DNS 返回 2 个 IP 地址:::1(IpV6 localhost)和 127.0.0.1(IpV4 localhost).它不知道是否需要使用 IpV4 或 IpV6 地址.所以它同时尝试(优先选择 IpV6).您观察到的 1 秒延迟是它需要意识到与 ::1 (IpV6 localhost) 的连接失败的时间.

It queries DNS (which takes no time) and DNS returns 2 ip addresses: ::1 (IpV6 localhost) and 127.0.0.1 (IpV4 localhost). It has no idea whether it needs to use IpV4 or IpV6 address. So it tries both (with preference of IpV6). That 1 second delay you observe is time it needs to realize that connection to ::1 (IpV6 localhost) fails.

如果你这样初始化客户端:

If you initialize client like this:

var client = new TcpClient();

这与:

// InterNetwork means IpV4
var client = new TcpClient(AddressFamily.InterNetwork);

这两个版本都将客户端绑定到本地 IpV4 套接字.这意味着当你以后做:

Both those versions will bind client to local IpV4 socket. That means when you later do:

client.Connect("localhost", port);

客户端不需要尝试IpV6 localhost地址,因为本地socket是IpV4.这两个版本都将消除您观察到的 1 秒延迟.另一个消除延迟的方法是将您的服务器绑定到 ipv6 环回(到 IPAddress.IPv6Loopback).

There is no need for client to try IpV6 localhost address, because local socket is IpV4. Both this versions will remove 1 second delay you observe. Another option to remove a delay is to bind your server to ipv6 loopback (to IPAddress.IPv6Loopback).

请注意:

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

只是错了.TcpClient 构造函数的重载需要 local 端点,而不是远程端点.在您的示例中,应该只在客户端或服务器上抛出异常(端口已在使用中),因为您试图绑定到服务器和客户端上的相同 ip 和端口.如果您想直接连接而不使用 DNS 查找(无论如何,本地主机都需要 0 时间,但在连接到真实服务器时可能很重要),请执行以下操作:

Is just wrong. This overload of TcpClient constructor expects local endpoint, not remote. In your example that should just throw exception (port already in use) on either client or server, because you are trying to bind to the same ip and port on both server and client. If you want to connect directly without DNS lookup (which takes 0 time for localhost anyway, but might be important when you connect to real server), do this:

IPEndPoint remoteEP = new IPEndPoint(address, hostport);
client = new TcpClient();
client.Connect(remoteEP);

这篇关于TCP 设置时间是否这么慢(1 秒),通常吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆