web-dev-qa-db-fra.com

Socket vs SocketChannel

J'essaie de comprendre SocketChannels , et NIO en général. Je sais comment travailler avec des sockets ordinaires et comment créer un serveur thread-par-client simple (en utilisant les sockets de blocage classiques).

Alors mes questions:

  • Qu'est-ce qu'un SocketChannel?
  • Quel est le supplément que j'obtiens lorsque je travaille avec un SocketChannel au lieu d'un Socket.
  • Quelle est la relation entre un canal et un tampon?
  • Qu'est-ce qu'un sélecteur?
  • La première phrase du documentation est A selectable channel for stream-oriented connecting sockets.. Qu'est-ce que ça veut dire?

J'ai lu aussi cette documentation , mais je ne comprends pas ...

66
Ramzi Khahil

Un Socket est un périphérique d'entrée/sortie bloquant. Il fait le Thread qui l'utilise pour bloquer sur les lectures et potentiellement aussi sur les écritures si le tampon sous-jacent est plein. Par conséquent, vous devez créer un tas de threads différents si votre serveur a un tas de Socket ouverts.

Un SocketChannel est un moyen non bloquant de lire à partir de sockets, afin que vous puissiez faire communiquer un thread avec plusieurs connexions ouvertes à la fois. Cela fonctionne en ajoutant un groupe de SocketChannel à un Selector, puis en bouclant sur la méthode select() du sélecteur, qui peut vous informer si des sockets ont été acceptées, des données reçues, ou fermé. Cela vous permet de communiquer avec plusieurs clients dans un même thread et de ne pas avoir la surcharge de plusieurs threads et de synchronisation.

Les Buffer sont une autre fonctionnalité de NIO qui vous permet d'accéder aux données sous-jacentes à partir des lectures et des écritures pour éviter la surcharge de copie des données dans de nouvelles baies.

53
Andrew Mao

À ce jour, NIO est si vieux que peu de gens se souviennent de ce que Java était comme avant 1.4, c'est ce que vous devez savoir pour comprendre le "pourquoi" de NIO.

En résumé, jusqu'à Java 1.3, toutes les E/S étaient de type bloquant. Et pire, il n'y avait pas d'analogue de l'appel système select() au multiplex I/O. Par conséquent, un serveur implémenté en Java n'avait d'autre choix que d'employer une stratégie de service "un thread par connexion".

Le point de base de NIO, introduit dans Java 1.4, était de rendre la fonctionnalité des E/S non bloquantes multiplexées de style UNIX disponibles en Java. Si vous comprenez comment programmer avec select() ou poll() pour détecter la disponibilité des E/S sur un ensemble de descripteurs de fichiers (sockets, généralement), alors vous trouverez les services dont vous avez besoin dans NIO: vous utilisera SocketChannels pour les points de terminaison d'E/S non bloquants et Selectors pour les fdsets ou les tableaux pollfd. Les serveurs avec des pools de threads, ou avec des threads gérant plus d'une connexion chacun, deviennent désormais possibles. "supplémentaire".

Un Buffer est le type de tableau d'octets dont vous avez besoin pour les E/S de socket non bloquantes, en particulier du côté sortie/écriture. Si seulement une partie d'un tampon peut être écrite immédiatement, avec le blocage des E/S, votre thread se bloquera simplement jusqu'à ce que l'intégralité puisse être écrite. Avec des E/S non bloquantes, votre thread obtient une valeur de retour de la quantité écrite, vous laissant le soin de gérer le reste pour le prochain tour. Un Buffer s'occupe de ces détails mécaniques en implémentant explicitement un modèle producteur/consommateur pour le remplissage et la vidange, étant entendu que vos threads et le noyau de la JVM pas seront synchronisés.

17
arayq2

Même si vous utilisez SocketChannels, il est nécessaire d'utiliser un pool de threads pour traiter channels.

En pensant au scénario, vous n'utilisez qu'un seul thread qui est responsable à la fois de l'interrogation select() et du traitement du SocketChannels sélectionné parmi Selectors, si un canal prend 1 seconde pour le traitement, et il y a 10 canaux en file d'attente, cela signifie que vous devez attendre 10 secondes avant la prochaine interrogation, ce qui est intolérable. il devrait donc y avoir un pool de threads pour le traitement des canaux.

En ce sens, je ne vois pas d'énorme différence avec le modèle de sockets de blocage thread-par-client. la différence principale est dans le modèle NIO, la tâche est plus petite, elle ressemble plus à un thread par tâche, et les tâches peuvent être lues, écrites, processus biz, etc. pour plus de détails, vous pouvez jeter un oeil à - Netty implémentation de NioServerSocketChannelFactory, qui utilise un thread Boss acceptant la connexion, et répartit les tâches vers un pool de threads Worker pour traitement

Si vous êtes vraiment intéressé par un thread, le résultat est qu'au moins vous devez avoir regroupé les threads d'E/S, car les opérations d'E/S sont souvent plus lentes que les cycles de traitement des instructions, vous ne voudriez pas le précieux thread étant bloqué par les E/S, et c'est exactement ce que NodeJS fait, en utilisant une connexion d'acceptation de thread, et toutes les E/S sont asynchrones et traitées parallèlement par le pool de threads d'E/S back-end

l'ancien style thread-par-client est-il mort? Je ne pense pas, la programmation NIO est complexe, et le multi-thread n'est pas naturellement mauvais, gardez à l'esprit que les systèmes d'exploitation modernes et les CPU deviennent meilleurs et mieux en multitâche, de sorte que les frais généraux du multithreading deviennent plus petits au fil du temps.

5
Frank Zhang