Peaufiner les performances de Java sockets

J'ai créé un contrôle de bureau à distance de l'application. Évidemment, il se compose de client et de serveur de pièces:

Serveur:

  • De la réception de la souris/du clavier à partir d'un client;
  • L'envoi de capture d'écran de desktop client.

Client:

  • Réception capture d'écran du serveur;
  • L'envoi de la souris/du clavier;

Envisager d'envoyer la capture d'écran. Lorsque j'utilise mon PC à la maison en tant que serveur, je me retrouve avec une capture d'écran de 1920x1080 dimension. En utilisant JAI Image I/O Outils et l'encodage au format PNG, j'ai pu atteindre les stats suivantes pour une telle image:

  1. Écrire le temps de ~0,2 s; (pas dans la douille, mais sur un "régulier" flux de sortie, je. e. moment de l'encodage)
  2. Temps de lecture ~0,05 s; (pas de prise, mais d'un "régulier" flux d'entrée, je. e. le temps de décodage)
  3. Taille ~250 KO;
  4. Qualité parfaite.

Comme un résultat, selon #1 - l'idéal possible FPS doit être d'environ 5.

Malheureusement, je ne suis pas en mesure d'atteindre même ceux ~5 FPS, même pas 2 FPS. J'ai cherché le goulot d'étranglement et a trouvé que l'écriture/la lecture/de socket flux e/S prend jusqu'à ~2 s (Voir annexe 1 et 2 pour des précisions). Sûrement, c'est inacceptable.

J'ai étudié le sujet un peu et ajouté de mise en mémoire tampon de la socket d'e/S de flux (avec BufferedInputStream et BufferedOutputStream) sur les deux côtés. J'ai commencé avec 64 KO de tailles. En effet, l'amélioration de la performance. Mais ne peut toujours pas avoir au moins 2 FPS! En outre, j'ai expérimenté avec Socket#setReceiveBufferSize et Socket#setSendBufferSize et il y avait quelques changements dans la vitesse, mais je ne sais pas exactement comment ils se comportent, donc je ne sais pas les valeurs à utiliser.

Regarder le code d'initialisation:

Serveur:

    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

Client:

    Socket socket = new Socket(...);
    socket.setSendBufferSize( ? ); //#6
    socket.setReceiveBufferSize( ? ); //#7

    OutputStream outputStream = new BufferedOutputStream(
            socket.getOutputStream(), ? ); //#8
    InputStream inputStream = new BufferedInputStream(
            socket.getInputStream(), ? ); //#9

Questions:

  1. Quelles valeurs voulez-vous recommander (pour améliorer les performances) pour tous les
    ces cas et pourquoi?
  2. Préciser Socket#setReceiveBufferSize et
    Socket#setSendBufferSize comportement.
  3. Ce que d'autres méthodes ou techniques peuvent vous conseiller pour améliorer les performances
    d'une telle application?
  4. Skype offre une bonne qualité de bureau en temps réel de transmission - comment font-ils?

Annexe 1: Ajoutant le déroulé de la pseudo-code du client, de la prise de la lecture (@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();
            }
        });
    }
}

Annexe 2: Ajoutant le déroulé de la pseudo-code de la socket du serveur d'écriture (@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.
    }
}

Conclusion:

Merci pour vos réponses, spécialement EJP - il a fait les choses un peu plus clair pour moi. Si vous êtes comme moi, en quête de réponses sur la manière d'ajuster les sockets de la performance, vous devriez certainement vérifier TCP/IP Sockets en Java, Deuxième Édition: Guide Pratique pour les Programmeurs, en particulier le chapitre 6 "Sous le Capot", qui décrit ce qui se passe derrière les coulisses de *Socket les classes, comment envoyer et recevoir des tampons sont gérées et utilisées (qui sont les principales clés de la performance).

  • Êtes-vous sûr que la prise paramètres sont vos facteurs limitants? Quel type de connexion avez-vous entre votre client et votre serveur? Quelle est la vitesse de votre serveur?
  • Pouvez-vous poster le code où vous lisez à partir de la sockets sur le côté client?
  • en.wikipedia.org/wiki/Delta_encoding
  • Lindsjö Que pensez-vous que vous entendez par "type de connexion"? Comment puis-je mesurer la vitesse de mon serveur?
  • Pourquoi êtes-vous en testant la valeur null? et poursuit-il? Attendez-vous à écrire une valeur null à la ObjectOutputStream?
  • Nope, c'est juste pour la sécurité - puisque je n'étais pas sûr que ça s'arrête sur readObject() et attend jusqu'à ce qu'il y a vraiment quelque chose à y lire. En d'autres mots, j'ai pensé qu'il vérifie et si il ne peut pas lire encore, il retourne null.
  • J'étais incertaine de la façon dont vous déployez votre client/serveur. Si vous avez un 1MBit rapport entre eux, alors il sera physiquement prendre environ 2 secondes pour transférer 250KB. Vous pouvez tester la vitesse de simplement récupérer le contenu de votre serveur et de mesurer le temps qu'il faut.