Java:套接字 - DataStream非常缓慢

5
我正在使用普通的DataInputStream和DataOutputStream来进行接收、发送(在服务器上使用accept)以制作游戏,但速度非常慢,延迟超过5秒。
以下是我做法的示例:
(dos表示DataOutputStream)
dos = new DataOutputStream(socket.getOutputStream());
dos.writeFloat(dp.x);
dos = new DataOutputStream(socket.getOutputStream());
dos.writeFloat(dp.y);
dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(dp.username);

对于输入(这个在服务器中),我使用:

(dis是DataInputStream,它在一个for循环中,所以i是每个玩家的)

dis = new DataInputStream(list_sockets.get(i).getInputStream());
x = dis.readFloat();
dis = new DataInputStream(list_sockets.get(i).getInputStream());
y = dis.readFloat();
dis = new DataInputStream(list_sockets.get(i).getInputStream());
username = dis.readUTF();

我的应用很慢,但我不知道为什么 :( 请帮帮我?

编辑:每个操作(发送、接受、接收)都有自己的守护线程。


4
为什么你会反复创建新的DataInput/OutputStreams? - Mat
那基本上就是整个东西的样子,没有太多其他的东西。只是发送和接收位置,没有复杂的循环或其他什么东西。 - user1217946
好的,试着移除所有不必要的东西并尝试一下。只有你自己能够衡量性能差异是否显著。 - Mat
我明白你的应用程序是唯一运行缓慢的东西,但是其他与该计算机的通信是正常的,对吗? - Miquel
是的,我们两个都有完美的互联网连接,并且住得很近。 - user1217946
显示剩余4条评论
3个回答

7
重复创建DataOutputStream和DataInputStream实例对性能不利。但是,我怀疑更重要的性能问题是您在没有Java端缓冲的情况下进行读写。这意味着每个读/写调用都会进行一个(可能多个)系统调用以读取数据。系统调用比普通Java方法调用慢几个数量级。
您需要将Socket输入流包装在BufferedInputStream中,将输出流包装在BufferedOutputStream中,然后将这些流包装在Data流中。(请注意,当您这样做时,每条消息或一系列消息结束时,刷新DataOutputStream变得更加重要。)
引用:
“我写一些信息,然后在每次写入后都刷新吗?”
如上所述,您应在每个消息或每个消息序列结尾处进行刷新。在缓冲输出流上刷新每次都会导致一个系统调用,假设缓冲区中有要刷新的数据。如果您在每次写入时都刷新,那么您可能正在进行不必要的系统调用。
确定协议的消息边界取决于您发送/接收的数据的细节以及处理方式。

哦哦哦!谢谢,所以 BufferedStream 对我没用是因为我没有刷新(flush)? - user1217946
我是写一些信息,然后每次写完之后刷新还是闪存? - user1217946
2
我不建议刷机。这样做可能会被逮捕 :-) - Stephen C
我的意思是,每写几个就刷新一次,或者每次写完都刷新。哈哈 - user1217946
1
@IvanDonat 在请求/响应协议中,你只需要在每次读取之前刷新即可。想一想。 - user207421
更普遍地说,对协议的仔细分析以及您对其实现的了解将告诉您何时需要刷新。 - Stephen C

2

就像你所说的,如果在循环中为每个迭代创建新的数据输入/输出流,将会导致巨大的开销。

你应该为每个客户端(在单独的线程上)拥有1个数据输入和输出流,然后通过循环读取流中的数据,直到输入流为空,来获取用户的输入。


1

如果你所说的“慢”只是指网络延迟,那么你应该“刷新”你的流以强制传输非完整缓冲区。

补充:

即使你像这样进行“工作草案”,也要考虑到一个良好设计的通信协议包含各种各样的功能和特性。问题涉及:

  • 异常处理
  • 排序
  • 身份验证
  • 完整性检查(避免作弊、重放等)
  • 可扩展性(新命令,使用显式命令对象)
  • 实时处理
  • 当然还有很多其他问题...

有一些框架可以帮助你减轻负担。


在每个单独的“命令”完成后(例如,如果一个命令序列是三个int,在三个int之后...)。 - mtraut
我还是不明白。你能否编辑一下你的东西,加上写几个整数或其他内容的示例,并说明在哪里放置flushes? - user1217946

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接