Java的TCP套接字:数据传输速度慢(Java TCP socket: data transfer

2019-06-17 14:08发布

我建立了一个服务器与一个ServerSocket,与客户机连接到它。 他们直接通过交换机联网与坪时间<1ms以上。

现在,我尝试通过套接字的输出流从客户端推送数据的“批”到服务器。 用时23分钟到换乘0.6Gb。 我可以在几秒钟通过SCP推向一个更大的文件。

任何想法,我可能是做错了什么? 我基本上只是循环和插座上调用writeInt。 速度问题并不重要,其中的数据是从哪里来的,哪怕我只是送一个恒定的整数,而不是从磁盘读取。

我尝试设置发送和两侧为4MB,没有骰子接收缓冲区。 我用一个缓冲流的读者和作家,没有骰子。

我缺少的东西吗?

编辑:代码

这里就是我的套接字

System.out.println("Connecting to " + hostname);

    serverAddr = InetAddress.getByName(hostname);

    // connect and wait for port assignment
    Socket initialSock = new Socket();
    initialSock.connect(new InetSocketAddress(serverAddr, LDAMaster.LDA_MASTER_PORT));
    int newPort = LDAHelper.readConnectionForwardPacket(new DataInputStream(initialSock.getInputStream()));
    initialSock.close();
    initialSock = null;

    System.out.println("Forwarded to " + newPort);

    // got my new port, connect to it
    sock = new Socket();
    sock.setReceiveBufferSize(RECEIVE_BUFFER_SIZE);
    sock.setSendBufferSize(SEND_BUFFER_SIZE);
    sock.connect(new InetSocketAddress(serverAddr, newPort));

    System.out.println("Connected to " + hostname + ":" + newPort + " with buffers snd=" + sock.getSendBufferSize() + " rcv=" + sock.getReceiveBufferSize());

    // get the MD5s
    try {
        byte[] dataMd5 = LDAHelper.md5File(dataFile),
               indexMd5 = LDAHelper.md5File(indexFile);

        long freeSpace = 90210; // ** TODO: actually set this **

        output = new DataOutputStream(new BufferedOutputStream(sock.getOutputStream()));
        input  = new DataInputStream(new BufferedInputStream(sock.getInputStream()));

这里就是我做的服务器端连接:

    ServerSocket servSock = new ServerSocket();
    servSock.setSoTimeout(SO_TIMEOUT);
    servSock.setReuseAddress(true);
    servSock.bind(new InetSocketAddress(LDA_MASTER_PORT));

    int currPort = LDA_START_PORT;

    while (true) {
        try {
            Socket conn = servSock.accept();
            System.out.println("Got a connection.  Sending them to port " + currPort);
            clients.add(new MasterClientCommunicator(this, currPort));
            clients.get(clients.size()-1).start();

            Thread.sleep(500);

            LDAHelper.sendConnectionForwardPacket(new DataOutputStream(conn.getOutputStream()), currPort);

            currPort++;
        } catch (SocketTimeoutException e) {
            System.out.println("Done listening.  Dispatching instructions.");
            break;
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

好吧,这里的地方,我航运数据超过〜0.6Gb。

public static void sendTermDeltaPacket(DataOutputStream out, TIntIntHashMap[] termDelta) throws IOException {
    long bytesTransferred = 0, numZeros = 0;

    long start = System.currentTimeMillis();

    out.write(PACKET_TERM_DELTA); // header     
    out.flush();
    for (int z=0; z < termDelta.length; z++) {
        out.writeInt(termDelta[z].size()); // # of elements for each term
        bytesTransferred += 4;
    }

    for (int z=0; z < termDelta.length; z++) {
        for (int i=0; i < termDelta[z].size(); i++) {
            out.writeInt(1);
            out.writeInt(1);
        }
    }

这似乎非常简单迄今...

Answer 1:

不想当您传输大量数据的写入单个字节。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Transfer {

    public static void main(String[] args) {
        final String largeFile = "/home/dr/test.dat"; // REPLACE
        final int BUFFER_SIZE = 65536;
        new Thread(new Runnable() {
            public void run() {
                try {
                    ServerSocket serverSocket = new ServerSocket(12345);
                    Socket clientSocket = serverSocket.accept();
                    long startTime = System.currentTimeMillis();
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int read;
                    int totalRead = 0;
                    InputStream clientInputStream = clientSocket.getInputStream();
                    while ((read = clientInputStream.read(buffer)) != -1) {
                        totalRead += read;
                    }
                    long endTime = System.currentTimeMillis();
                    System.out.println(totalRead + " bytes read in " + (endTime - startTime) + " ms.");
                } catch (IOException e) {
                }
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(1000);
                    Socket socket = new Socket("localhost", 12345);
                    FileInputStream fileInputStream = new FileInputStream(largeFile);
                    OutputStream socketOutputStream = socket.getOutputStream();
                    long startTime = System.currentTimeMillis();
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int read;
                    int readTotal = 0;
                    while ((read = fileInputStream.read(buffer)) != -1) {
                        socketOutputStream.write(buffer, 0, read);
                        readTotal += read;
                    }
                    socketOutputStream.close();
                    fileInputStream.close();
                    socket.close();
                    long endTime = System.currentTimeMillis();
                    System.out.println(readTotal + " bytes written in " + (endTime - startTime) + " ms.");
                } catch (Exception e) {
                }
            }
        }).start();
    }
}

这份数据的1 2GB的短过我的机器19秒。 这里的关键是使用InputStream.read和OutputStream.write接受一个字节数组作为参数的方法。 该缓冲区的大小并不重要,它只是应该比,说大一点,5实验与BUFFER_SIZE上面看到它是如何影响速度,但也请记住,它可能是你正在运行的每台机器不同这个程序上。 64昆明植物似乎是一个很好的妥协。



Answer 2:

嘿,我想我会跟进任何人有兴趣。

下面是这个故事的离奇道德:

切勿使用DataInputStream类/ DataOutputStream类和插座!

如果我换一个的BufferedOutputStream /的BufferedInputStream插座,生命是伟大的。 写这原只是罚款。

但包裹插座一个DataInputStream / DataOutputStream类,甚至有DataOutputStream类(的BufferedOutputStream(sock.getOutputStream()))是极其缓慢。

该解释将是非常有意思的。 但进出交换后的一切,这是什么事。 自己尝试一下,如果你不相信我。

感谢所有的快速帮助,虽然。



Answer 3:

也许你应该尝试块(帧)发送乌尔数据,而不是seperately写每个字节。 并与TCP数据包大小为最佳性能调整您的帧。



Answer 4:

你可以尝试这样做了环回,它应该会在第二传输数据。

如果只需要几分钟,有什么不对您的应用程序。 如果只是缓慢通过Internet发送的数据也可能是您的网络链路是缓慢的。

我的猜测是,你有你的客户端和服务器之间的10 Mb / s的网络,这就是为什么您的汇款会慢。 如果是这种情况,请尝试使用DeflatoutOutputStream并为您的连接的InflatorInputStream中。



Answer 5:

你是如何实现接收端? 请发表您的接收代码。

由于TCP是一个可靠的协议,将采取步骤,以确保客户能够接收所有发送者发送的数据。 这意味着,如果您的客户端无法获取数据出的数据的及时接收缓冲区,那么发送方将简单地停止发送更多的数据,直到客户有机会来读取接收缓冲区中的所有字节。

如果您的接收方是一次读取一个字节的数据,那么你的发件人可能会花费大量的时间等待接收缓冲器清除,因此长传输时间。 我会建议改变你的接收代码读取的字节数尽可能在每次读取操作 。 看看是否能解决你的问题。



Answer 6:

因为我还不能在本网站发表评论,我必须写答案,这里@Erik。

问题是,DataOutputStream类不缓冲。 在Java中,整个流的东西是基于装饰设计模式。 所以,你可以写

DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));

这将在其中的BufferedOutputStream是更有效的,然后将其缠绕成DataOutputStream类,其提供如writeInt(),writeLong()等附加很好的功能包的原始流。



Answer 7:

@Erik:使用DataXxxputStream是不是这里的问题。 问题是你在过小块状物发送数据。 使用缓冲解决你的问题,因为即使你会写一点一点地缓冲可以解决这个问题。 气罐的解决方案是好得多,一般的快。



Answer 8:

你应该下载好的数据包嗅探器。 我是一个巨大的风扇是Wireshark亲自和我结束了我每次做一些socket编程时使用它。 只要记住,你必须在客户端和服务器上的不同系统上运行,以拿起任何数据包。



Answer 9:

您可以尝试:

  • 在100%的CPU正在发送的数据? 如果是这样,使用的VisualVM并做了CPU剖析,看看时间都花在
  • 从java.nio中使用的SocketChannel - 这些通常是更快,因为他们可以更容易地使用本地IO - 当然,这不仅有利于如果你的操作是CPU绑定
  • 如果不是CPU的约束,有什么东西在网络层面去错了。 使用数据包嗅探器来分析这个。


Answer 10:

我使用的PrintWriter发送数据。 我删除,并用BufferedOutputStream.send(String.getBytes())发送的数据,并获得约10倍的速度发送。



Answer 11:

堆大小是如何设置的? 我最近也有类似的问题,对大量数据的插座传输和只是看着JConsole ,我意识到应用程序已花费了大量的时间做充分的GC。

尝试-Xmx1g



Answer 12:

使用字节缓冲器,用于发送所述数据



文章来源: Java TCP socket: data transfer is slow