【问题标题】:TCP-IP, C# Client and Java Server, very high latencyTCP-IP、C# 客户端和 Java 服务器,延迟非常高
【发布时间】:2015-08-07 03:22:28
【问题描述】:

在一些基于教程的代码中,我通过 Web 服务中的 Web 应用程序的 WebMethod 将 C# Web 应用程序连接到 Java 套接字服务器。不幸的是,这发生得非常缓慢。例如,当 Java 服务器向 C# 客户端回显一些数据时,我得到以下结果:

  • 发送的数据大小 = 32MB,总时间 = 980 毫秒(没问题)
  • 发送的数据大小 = 4MB,总时间 = 530 毫秒(变得有点慢)
  • 发送的数据大小 = 1MB,总时间 = 520 毫秒(绝对瓶颈)
  • 发送的数据大小 = 1kB,总时间 = 516 毫秒(这一定是某种恒定的延迟)

我了解到人们可以使用某些服务器应用程序进行实时通信 (~60/s),有时甚至可以进行数百万个流/s。我的实施可能有什么问题?它通过一个打开的连接发送多条消息,所以对象创建开销应该只显示在第一条消息中?为什么我的消息传递开销约为 500 毫秒?

C# webmethod 在 web 应用启动时启动,每次调用此 webmethod 时都连接到同一个 Java 服务器。

public static IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
public static IPAddress ipAddress = ipHostInfo.AddressList[0];
public static IPEndPoint remoteEP = new IPEndPoint(ipAddress, 9999);

// Create a TCP/IP  socket.
public static Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public static int z = 0; 

[WebMethod]
public BenchmarkData_ StartClient()
{
    lock(lck)
    {
        z++;
        if (z == 1)
        {
            sender.Connect(remoteEP);
        }
    }
    int bytesRec = 0;
    int boy = 0;
    byte[] bytes = new byte[1024 * 1024];
    int bytesSent = 0;
    SocketFlags sf = new SocketFlags();
    Stopwatch sw = new Stopwatch(); Stopwatch sw2 = new Stopwatch();

    #region r
    lock (lck)
    {
        sw.Start();
        // Data buffer for incoming data.

        // Connect to a remote device.
        try
        {
            // Establish the remote endpoint for the socket.
            // This example uses port 11000 on the local computer.

            // Create a TCP/IP  socket.
            sender.ReceiveBufferSize = 1024 * 1024;
            sender.ReceiveTimeout = 1;

            // Connect the socket to the remote endpoint. Catch any errors.
            try
            {
                Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());
                // Encode the data string into a byte array.
                byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");

                // Send the data through the socket.
                bytesSent = sender.Send(msg);

                // Receive the response from the remote device.
                sw.Stop();

                sw2.Start();
                while ((bytesRec = sender.Receive(bytes)) > 0)
                {
                    boy += bytesRec;
                }

                Console.WriteLine("Echoed test = {0}", Encoding.ASCII.GetString(bytes, 0, bytesRec));

                // Release the socket.
                // sender.Shutdown(SocketShutdown.Both);
                // sender.Close();
                sw2.Stop();
            }
            catch (ArgumentNullException ane)
            {
                Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
            }
            catch (SocketException se)
            {
                Console.WriteLine("SocketException : {0}", se.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.ToString());
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
    #endregion

    return new BenchmarkData_() { .... };
}

这是Java代码(半伪代码)

serverSocket=new ServerSocket(port); // in listener thread
Socket socket=serverSocket.accept(); // in listener thread

// in a dedicated thread per connection made:
out=new  BufferedOutputStream( socket.getOutputStream());
in=new DataInputStream(socket.getInputStream());        

boolean reading=true;
ArrayList<Byte> incoming=new ArrayList<Byte>();

while (in.available() == 0)
{
    Thread.sleep(3);    
}

while (in.available() > 0)
{
    int bayt=-2;
    try {
        bayt=in.read();
    } catch (IOException e) { e.printStackTrace(); }

    if (bayt == -1)
    {
        reading = false;
    }
    else
    {
        incoming.add((byte) bayt);                      
    }
}

byte [] incomingBuf=new byte[incoming.size()];
for(int i = 0; i < incomingBuf.length; i++)
{
    incomingBuf[i] = incoming.get(i);
}

msg = new String(incomingBuf, StandardCharsets.UTF_8);
if (msg.length() < 8192)
    System.out.println("Socket Thread:  "+msg);
else
    System.out.println("Socket Thread: long msg.");

OutputStreamWriter outW = new OutputStreamWriter(out);
System.out.println(socket.getReceiveBufferSize());
outW.write(testStr.toString()); // 32MB, 4MB, ... 1kB versions
outW.flush();

【问题讨论】:

  • 我假设您在测量时间中包含传输实际数据的时间。你可以同时运行一堆这样的,但是传输 32Mb 的数据即使在一个不错的局域网上也需要接近一秒钟的时间。
  • 它在 javascript 的回调函数上重新运行相同的函数以进行预热序列,现在一次只运行一个。
  • 不要使用那个while(in.available()==0) { Thread.sleep(3); } 一个设计好的程序永远不需要休眠。 (顺便说一句:你永远不会得到精确的 3 毫秒睡眠。至少 16 毫秒,具体取决于你使用的 Windows 版本)
  • 不,我说的是您声称的数百万个流——1Mb 传输需要一秒钟的事实并不意味着您不能同时运行数千个这些流,所有流都在 1Mb /sec(如果你有带宽)。
  • 线程在没有这个睡眠/等待部分的情况下不断读取缓冲区,并且没有从客户端获得单个字节。

标签: java c# performance-testing tcp-ip


【解决方案1】:

更换问题解决

while ((bytesRec = sender.Receive(bytes))>0)
{
   boy += bytesRec;
}

 while (sender.Available <= 0) ;

 while (sender.Available>0)
 {
      bytesRec = sender.Receive(bytes);
      boy += bytesRec;
 }

现在它以微秒为单位读取 1kB 而不是 500 毫秒。因为它检查单个整数而不是尝试读取整个缓冲区?也许。但它现在不读取从服务器发送的所有消息。它需要某种类型的标题才能知道要阅读多少。即使服务器发送兆字节,也会读取大约几千字节。

当服务器发送 3MB 和客户端读取完全相同的量时,需要 30 毫秒。两者都在同一台机器上。试图读取超过服务器已发送的内容(甚至是单个字节)会引发异常,因此 TCP 确实发送了客户端所需的完全相同的数量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多