リモート デスクトップ コントロール アプリケーションを作成しました。明らかに、クライアントとサーバーの部分で構成されています。
サーバ:
- クライアントからのマウス/キーボード アクションの受信。
- デスクトップのスクリーンショットをクライアントに送信します。
クライアント:
- サーバーからスクリーンショットを受け取ります。
- マウス/キーボード アクションの送信;
スクリーンショットを送信することを検討してください。自宅の PC をサーバーとして使用すると、スクリーンショットのサイズが 1920x1080 になります。JAI Image I/O Toolsを使用して PNG としてエンコードすることで、このような大きな画像に対して次の統計を達成することができました。
- 書き込み時間 ~0.2 秒。(ソケットではなく、「通常の」出力ストリーム、つまりエンコード時間)
- 読み取り時間 ~0.05 秒。(ソケットからではなく、「通常の」入力ストリーム、つまりデコード時間から)
- サイズ ~250 KB。
- 完璧な品質。
その結果、#1 によると、可能な理想的な FPS は ~5 になるはずです。
残念ながら、私はこれらの ~5 FPS でさえ達成できず、2 FPS でさえ達成できません。ボトルネックを探したところ、ソケット I/O ストリームへの書き込み/読み取りに最大 2 秒かかることがわかりました (説明については、付録 1 および 2 を参照してください)。確かにそれは受け入れられません。
私はこのトピックを少し調べました - そしてソケットの I/O ストリームのバッファリングを ( とBufferedInputStream
でBufferedOutputStream
) 両側に追加しました。私は 64 KB のサイズから始めました。これにより、実際にパフォーマンスが向上しました。しかし、少なくとも 2 FPS はまだありません。さらに、私はSocket#setReceiveBufferSize
andSocket#setSendBufferSize
を試してみましたが、速度にいくつかの変化がありましたが、それらがどのように動作するか正確にはわかりません。したがって、どの値を使用すればよいかわかりません。
初期化コードを見てください:
サーバ:
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReceiveBufferSize( ? ); // #1
serverSocket.bind(new InetSocketAddress(...));
Socket clientSocket = serverSocket.accept();
clientSocket.setSendBufferSize( ? ); // #2
clientSocket.setReceiveBufferSize( ? ); // #3
OutputStream outputStream = new BufferedOutputStream(
clientSocket.getOutputStream(), ? ); // #4
InputStream inputStream = new BufferedInputStream(
clientSocket.getInputStream(), ? ); // #5
クライアント:
Socket socket = new Socket(...);
socket.setSendBufferSize( ? ); // #6
socket.setReceiveBufferSize( ? ); // #7
OutputStream outputStream = new BufferedOutputStream(
socket.getOutputStream(), ? ); // #8
InputStream inputStream = new BufferedInputStream(
socket.getInputStream(), ? ); // #9
質問:
- これらすべてのケースに対して (パフォーマンスを向上させるために) どの値をお勧めしますか? またその理由は?
- 明確
Socket#setReceiveBufferSize
にしてSocket#setSendBufferSize
行動してください。 - そのようなアプリケーションのパフォーマンスを向上させるために、他にどのような方法/テクニックをアドバイスできますか?
- Skype は高品質のリアルタイム デスクトップ転送を提供します - どうやってそれを行うのですか?
付録 1:クライアント ソケット読み取りの展開された疑似コードの追加 (@mcfinnigan):
while(true) {
// objectInputStream is wrapping socket's buffered input stream.
Object object = objectInputStream.readObject(); // <--- Bottleneck (without setting proper buffer sizes is ~2 s)
if(object == null)
continue;
if(object.getClass() == ImageCapsule.class) {
ImageCapsule imageCapsule = (ImageCapsule)object;
screen = imageCapsule.read(); // <--- Decode PNG (~0.05 s)
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
repaint();
}
});
}
}
付録 2:サーバー ソケット書き込み (@EJP) の展開された疑似コードの追加:
while(true) {
// objectOutputStream is wrapping socket's buffered output stream.
BufferedImage screen = ... // obtaining screenshot
ImageCapsule imageCapsule = new ImageCapsule();
imageCapsule.write(screen, formatName()); // <--- Encode PNG (~0.2 s)
try {
objectOutputStream.writeObject(imageCapsule); // <--- Bottleneck (without setting proper buffer sizes is ~2 s)
}
finally {
objectOutputStream.flush();
objectOutputStream.reset(); // Reset to free written objects.
}
}
結論:
あなたの答え、特に EJP に感謝します - 彼は物事をもう少し明確にしてくれました。あなたが私のようで、ソケットのパフォーマンスを微調整する方法についての答えを探しているなら、TCP/IP Sockets in Java, Second Edition: Practical Guide for Programmers、特に第 6 章「Under the Hood」を確認する必要があります。*Socket
クラス、送信バッファと受信バッファがどのように管理および利用されるか (これはパフォーマンスの主要な鍵です)。