web-dev-qa-db-fra.com

lire et écrire simultanément sur le même socket en C ou C ++

J'implémente un serveur simple, qui accepte une seule connexion, puis utilise ce socket pour lire et écrire simultanément des messages à partir des threads de lecture et d'écriture. Quel est le moyen sûr et facile de lire et d'écrire simultanément à partir du même descripteur de socket en c/c ++ sur linux? Je n'ai pas besoin de m'inquiéter de la lecture et de l'écriture de plusieurs threads à partir du même socket car il y aura un seul thread de lecture et d'écriture dédié dédié à l'écriture sur le socket.

Dans le scénario ci-dessus, un type de verrouillage est-il requis?

Le scénario ci-dessus nécessite-t-il un socket non bloquant?

Existe-t-il une bibliothèque open source qui pourrait aider dans le scénario ci-dessus?

34
Jimm

Dans le scénario ci-dessus, un type de verrouillage est-il requis?

Aucun.

Le scénario ci-dessus nécessite-t-il un socket non bloquant?

Le bit qui vous inquiète probablement - les threads de lecture et d'écriture sur une connexion établie - n'a pas besoin d'être non bloquant si vous êtes satisfait que ces threads restent là en attendant de terminer. C'est normalement l'une des raisons pour lesquelles vous utiliseriez des threads plutôt que de sélectionner, d'interroger ou d'opérer des opérations asynchrones ... simplifie également le code.

Si le thread acceptant de nouveaux clients est heureux de bloquer l'appel à accept(), alors vous êtes tous bons aussi.

Pourtant, il y a un problème subtil avec les serveurs TCP que vous voudrez peut-être garder à l'esprit ... si votre programme se développe pour gérer plusieurs clients et avoir des tâches ménagères périodiques à faire. C'est naturel et tentant d'utiliser une instruction select avec un délai d'expiration pour vérifier la lisibilité sur le socket d'écoute - qui indique une tentative de connexion client - puis accept la connexion. Il y a une condition de concurrence: la connexion client la tentative peut avoir chuté entre select() et accept(), auquel cas accept() se bloquera si le socket d'écoute n'est pas non bloquant, et cela peut empêcher un retour en temps opportun à la boucle select() et arrêtez le traitement du délai d'expiration périodique jusqu'à ce qu'un autre client se connecte.

Existe-t-il une bibliothèque open source qui pourrait aider dans le scénario ci-dessus?

Il existe des centaines de bibliothèques pour écrire des serveurs de base, mais en fin de compte, ce que vous avez demandé est facilement réalisable sur les sockets BSD fournis par le système d'exploitation ou leur bâtardisation Windows.

30
Tony Delroy

Les prises sont bidirectionnelles. Si vous avez déjà disséqué un câble Ethernet ou série ou vu le schéma de câblage matériel de bas niveau pour eux, vous pouvez réellement VOIR des fils de cuivre distincts pour les lignes "TX" (émission) et "RX" (réception). Le logiciel d'envoi des signaux, depuis le contrôleur de périphérique jusqu'à la plupart des API du système d'exploitation pour un `` socket '', reflète cela et c'est la principale différence entre un socket et un tuyau ordinaire sur la plupart des systèmes (par exemple Linux).

Pour tirer le meilleur parti des sockets, vous avez besoin de:
1) Async IO support qui utilise IO Ports de complétion, epoll (), ou un rappel asynchrone similaire ou un système d'événements pour se réveiller) "chaque fois que des données arrivent sur le socket. Celui-ci doit alors appeler votre API" ReadData "de niveau le plus bas pour lire le message sur la connexion du socket.
2) Une 2e API qui prend en charge les écritures de bas niveau, une "WriteData" (transmission) qui pousse des octets sur le socket et ne dépend pas de tout ce dont la logique "ReadData" a besoin. N'oubliez pas que vos envois et vos réceptions sont indépendants même au niveau matériel, donc n'introduisez pas de verrouillage ou autre synchronisation à ce niveau.
3) Un pool de threads Socket IO, qui effectuent aveuglément tout traitement de données lues ou écrites sur un socket.
4) RAPPEL DE PROTOCOLE: Un objet de rappel vers lequel les threads de socket ont des pointeurs intelligents. Il gère toute couche PROTOCOLE, telle que l'analyse de votre objet de données en une véritable requête HTTP, qui se trouve au-dessus de la connexion de socket de base. N'oubliez pas qu'un socket n'est qu'un canal de données entre les ordinateurs et que les données qui y sont envoyées arriveront souvent sous la forme d'une série de fragments, les paquets. Dans des protocoles comme UDP, les paquets ne sont même pas en ordre. Les "ReadData" et "WriteData" de bas niveau rappelleront de leurs threads ici, car c'est là que le traitement des données sensible au contenu commence réellement.
5) Tous les rappels dont le gestionnaire de protocole lui-même a besoin. Pour HTTP, vous empaquetez les tampons de demande bruts dans des objets Nice que vous transférez à un véritable servlet, qui devrait renvoyer un objet de réponse Nice qui peut être sérialisé en une réponse conforme aux spécifications HTTP.

Remarquez le modèle de base: vous devez rendre l'ensemble du système fondamentalement asynchrone (un "oignon de rappels") si vous souhaitez tirer pleinement parti de la bidirectionnelle, async IO sur les sockets. Le seul la façon de lire et d'écrire simultanément sur le socket est avec des threads, donc vous pouvez toujours synchroniser entre un thread "écrivain" et "lecteur", mais je ne le ferais que si le protocole ou d'autres considérations me forçaient la main. La bonne nouvelle est que vous pouvez obtenir d'excellentes performances avec des sockets utilisant un traitement hautement asynchrone, le problème est que la construction d'un tel système de manière robuste est un effort sérieux.

18
Zack Yezek

Vous n'avez pas à vous en soucier. Une lecture de fil et une écriture de fil fonctionneront comme prévu. Les sockets sont en duplex intégral, vous pouvez donc lire pendant que vous écrivez et vice-versa. Vous auriez à vous inquiéter si vous aviez plusieurs écrivains, mais ce n'est pas le cas.

15
mfontanini