我使用ServerSocket设置服务器,使用客户端计算机连接到服务器。它们通过交换机直接联网,ping时间<1ms。
现在,我尝试通过套接字的输出流将“大量”数据从客户端推送到服务器。转移0.6Gb需要23分钟。我可以通过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);
}
}
到目前为止看起来非常简单......
答案 0 :(得分:26)
当您传输大量数据时,不想要写单个字节。
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 GiB数据。这里的关键是使用接受字节数组作为参数的InputStream.read和OutputStream.write方法。缓冲区的大小并不重要,它应该比比如说大一点.5。尝试使用上面的BUFFER_SIZE来查看它如何影响速度但是请记住,对于你运行的每台机器来说它可能都不同这个节目。 64 KiB似乎是一个很好的妥协。
答案 1 :(得分:12)
这是故事的奇异道德:
永远不要使用DataInputStream / DataOutputStream和套接字!!
如果我将套接字包装在BufferedOutputStream / BufferedInputStream中,那么生活就很棒。写它原始就好了。
但是将套接字包装在DataInputStream / DataOutputStream中,或者甚至让DataOutputStream(BufferedOutputStream(sock.getOutputStream()))非常慢。
对此的解释对我来说非常有趣。但在将所有内容交换进去之后,这就是最新情况。如果你不相信我,请亲自尝试。
感谢所有快速帮助。
答案 2 :(得分:6)
也许你应该尝试以块(帧)发送ur数据,而不是单独写每个字节。并将帧与TCP数据包大小对齐以获得最佳性能。
答案 3 :(得分:2)
你可以尝试通过环回来做这个,它应该在第二个传输数据。
如果需要几分钟,您的应用程序就会出现问题。如果只是通过互联网慢速发送数据,那么网络链接可能很慢。
我的猜测是你的客户端和服务器之间有一个10 Mb / s的网络,这就是你的传输速度很慢的原因。如果是这种情况,请尝试使用DeflatoutOutputStream和InflatorInputStream进行连接。
答案 4 :(得分:2)
你是如何实现接收端的?请发布您的接收代码。
由于TCP是一种可靠的协议,因此需要采取措施确保客户端能够接收发送方发送的所有数据。这意味着如果您的客户端无法及时从数据接收缓冲区中获取数据,则发送方将停止发送更多数据,直到客户端有机会读取接收缓冲区中的所有字节为止。
如果您的接收方一次读取一个字节的数据,那么您的发送方可能会花费大量时间等待接收缓冲区清除,因此传输时间较长。我建议将接收代码更改为在每次读取操作中读取尽可能多的字节。看看这是否能解决您的问题。
答案 5 :(得分:2)
由于我还不能对这个网站发表评论,我必须在这里写@Erik的答案。
问题是DataOutputStream没有缓冲。 Java中的整个Stream-thing基于装饰器设计模式。所以你可以写
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
它会将原始流包装在更有效的BufferedOutputStream中,然后将其包装到DataOutputStream中,该DataOutputStream提供了其他很好的功能,如writeInt(),writeLong()等。
答案 6 :(得分:1)
@Erik:使用DataXxxputStream不是问题所在。问题是你是以太小的块发送数据。使用缓冲区解决了您的问题,因为即使您一点一点地写缓冲区也可以解决问题。 Bombe的解决方案更好,更通用,更快。
答案 7 :(得分:0)
你应该下载一个好的数据包嗅探器。我个人非常喜欢WireShark,每次进行套接字编程时我都会使用它。请记住,您必须让客户端和服务器在不同的系统上运行才能获取任何数据包。
答案 8 :(得分:0)
要尝试的事情:
答案 9 :(得分:-1)
您的堆大小是如何设置的?我最近遇到了类似的问题,大量数据的套接字传输,只是通过查看JConsole
我意识到应用程序花了大部分时间来做完整的GC。
尝试-Xmx1g
答案 10 :(得分:-1)
用于发送数据的字节缓冲区