Java les prises et pertes de connexion
Quel est le moyen le plus approprié pour détecter si un socket a été abandonné ou pas? Ou si un paquet n'a en fait envoyé?
J'ai une bibliothèque pour l'envoi de Notifications Push d'Apple pour iphone sur l'Apple gatways (disponible sur GitHub). Les Clients ont besoin d'ouvrir un socket et de l'envoyer à une représentation binaire de chaque message; mais, malheureusement, Apple n'a pas de retour d'accusé de réception que ce soit. La connexion peut être réutilisé pour envoyer plusieurs messages. Je suis en utilisant le Java simple connexions Socket. Le code correspondant est:
Socket socket = socket(); //returns an reused open socket, or a new one
socket.getOutputStream().write(m.marshall());
socket.getOutputStream().flush();
logger.debug("Message \"{}\" sent", m);
Dans certains cas, si une connexion est abandonnée alors qu'un message est envoyé ou vers la droite avant; Socket.getOutputStream().write()
se termine avec succès. Je m'attends c'est en raison de la fenêtre TCP n'est pas encore épuisé.
Est-il une manière que je peux dire pour sûr que si un paquet effectivement eu dans le réseau ou pas? J'ai testé les deux solutions suivantes:
- Insérer un
socket.getInputStream().read()
opération avec un 250ms délai d'attente. Cela oblige une opération de lecture qui échoue lorsque la connexion a été interrompue, mais se bloque sinon pour les 250ms. - ensemble TCP envoi de taille de la mémoire tampon (par exemple,
Socket.setSendBufferSize()
) pour le message binaire de taille.
À la fois des méthodes de travail, mais ils dégradent considérablement la qualité du service; le débit va de 100 messages/seconde à environ 10 messages/seconde au plus.
Des suggestions?
Mise à JOUR:
Contestée par plusieurs réponses remettre en question la possibilité de le décrire. J'ai construit "l'unité" des tests de comportement que je décris. Découvrez l'unité des cas à Gist 273786.
À la fois les tests unitaires ont deux fils, un serveur et un client. Le serveur se ferme tandis que le client envoie des données sans une IOException jeté de toute façon. Ici est la principale méthode:
public static void main(String[] args) throws Throwable {
final int PORT = 8005;
final int FIRST_BUF_SIZE = 5;
final Throwable[] errors = new Throwable[1];
final Semaphore serverClosing = new Semaphore(0);
final Semaphore messageFlushed = new Semaphore(0);
class ServerThread extends Thread {
public void run() {
try {
ServerSocket ssocket = new ServerSocket(PORT);
Socket socket = ssocket.accept();
InputStream s = socket.getInputStream();
s.read(new byte[FIRST_BUF_SIZE]);
messageFlushed.acquire();
socket.close();
ssocket.close();
System.out.println("Closed socket");
serverClosing.release();
} catch (Throwable e) {
errors[0] = e;
}
}
}
class ClientThread extends Thread {
public void run() {
try {
Socket socket = new Socket("localhost", PORT);
OutputStream st = socket.getOutputStream();
st.write(new byte[FIRST_BUF_SIZE]);
st.flush();
messageFlushed.release();
serverClosing.acquire(1);
System.out.println("writing new packets");
//sending more packets while server already
//closed connection
st.write(32);
st.flush();
st.close();
System.out.println("Sent");
} catch (Throwable e) {
errors[0] = e;
}
}
}
Thread thread1 = new ServerThread();
Thread thread2 = new ClientThread();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
if (errors[0] != null)
throw errors[0];
System.out.println("Run without any errors");
}
[D'ailleurs, j'ai aussi une simultanéité bibliothèque de test, qui fait l'installation de manière un peu plus précise et plus claire. La caisse de l'échantillon à gist ainsi].
Lors de l'exécution j'obtiens le résultat suivant:
Closed socket
writing new packets
Finished writing
Run without any errors
Comme je l'ai déjà indiqué dans ma réponse, si vous voulez être sûr qu'il n'y a aucune erreur, vous devez effectuer une gracieuse de fin de connexion. Exécuter shutdownOutput(), la lecture jusqu'à ce que vous recevez des expressions du FOLKLORE, puis fermer le socket. La drôle de fin de connexion est dans l'essence de la réception d'un accusé de pairs qu'il a reçu de vous OK, ce qui est exactement ce que vous voulez.
OriginalL'auteur notnoop | 2010-01-08
Vous devez vous connecter pour publier un commentaire.
Ce n'est pas d'une grande aide pour vous, mais, techniquement, les deux solutions proposées sont incorrectes. OutputStream.flush() et tout ce que les appels d'API que vous pouvez penser ne vont pas faire ce dont vous avez besoin.
Le seul portable et fiable pour déterminer si un paquet a été reçu par les pairs, c'est d'attendre une confirmation de la part de l'interlocuteur. Cette confirmation peut être une réponse, ou un gracieux arrêt du socket. Fin de l'histoire - il n'y a vraiment pas d'autre moyen, et ce n'est pas Java spécifique - c'est un des fondamentaux de la programmation réseau.
Si ce n'est pas une connexion permanente - qui est, si vous venez d'envoyer quelque chose et puis fermer la connexion - la façon dont vous le faire est de vous attraper tous IOExceptions (l'un d'eux indique une erreur) et que vous effectuez un gracieux arrêt du socket:
OriginalL'auteur Tzvetan Mikov
Après beaucoup d'ennuis avec les pertes de connexion, j'ai déplacé mon code pour utiliser le format amélioré, ce qui signifie en gros vous changer de forfait pour ressembler à ceci:
De cette façon, Apple ne sera pas tomber d'une connexion si une erreur se produit, mais ecrire un commentaire du code de la douille.
OriginalL'auteur Phil Calçado
Si vous êtes à envoyer des informations en utilisant le protocole TCP/IP pour apple que vous avez à recevoir des accusés de réception. Cependant, vous avez affirmé:
Qu'entendez-vous par là? TCP/IP garantit la livraison, par conséquent, le récepteur DOIT en accuser réception. Il n'a pas de garantie lors de la livraison aura lieu, cependant.
Si vous envoyer une notification d'Apple et vous cassez votre connexion avant de recevoir l'accusé de réception il n'y a aucun moyen de dire si vous avez réussi ou non, vous devez simplement envoyer à nouveau. Si en poussant deux fois la même information est un problème ou pas géré correctement par l'appareil, puis il y a un problème. La solution est de fixer l'appareil de manutention de la double notification push: il n'y a rien que vous pouvez faire sur le poussant de côté.
@Commentaire De Clarification/Question
Ok. La première partie de ce que vous comprendre, c'est votre réponse à la deuxième partie. Seulement les paquets qui ont reçu les accusés de réception ont été envoyés et reçus correctement. Je suis sûr qu'on pourrait penser d'un schéma compliqué de garder une trace de chaque paquet nous-mêmes, mais le protocole TCP est supposer pour résumé cette couche de loin et gère pour vous. De votre côté vous n'avez qu'à composer avec la multitude de défaillances qui pourraient se produire (en Java si l'un de ces cas se produit, une exception est levée). Si il n'y a pas d'exception les données que vous venez essayé d'envoyer est envoyé garantis par le protocole TCP/IP.
Il existe une situation où les données sont apparemment "envoyé", mais ne sont pas garantis pour être reçu, dans lequel aucune exception n'est levée? La réponse devrait être non.
@Exemples
Nice exemples, cela clarifie les choses un peu. J'aurais pensé à une erreur sera levée. Dans l'exemple affiché une erreur est renvoyée sur la deuxième écrire, mais pas la première. Ce qui est intéressant comportement... et je n'ai pas pu trouver beaucoup d'informations expliquant pourquoi il se comporte comme cela. Il permet toutefois d'expliquer pourquoi nous devons développer notre propre application de protocoles de niveau pour vérifier la livraison.
Dirait que vous êtes correct que sans un protocole de confirmation leur est pas la garantie de l'appareil d'Apple recevrez une notification. Apple a également seule file d'attente est le dernier message. En regardant un peu sur le service, j'ai été en mesure de déterminer ce service est plus pour des raisons de commodité pour le client, mais ne peut pas être utilisé pour garantir le service et doit être combinée avec d'autres méthodes. J'ai lu ce à partir de la source suivante.
http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/
Semble que la réponse est pas de savoir si ou non vous pouvez dire à coup sûr. Vous pouvez être en mesure d'utiliser un renifleur de paquets comme Wireshark pour savoir si il a été envoyé, mais cela ne garantit pas qu'il a été reçu et envoyé à l'appareil en raison de la nature du service.
Merci pour la mise à jour. Je me rends compte que je devrais avoir porté la question sur TCP avec les exemples, plutôt que de discuter les services Apple. Je pense que je vais abandonner pour la perte de la connexion support de détection pour l'instant.
OriginalL'auteur Derek Litz