Le serveur peut-il utiliser le même socket pour envoyer la réponse au client? Comment?

StackOverflow https://stackoverflow.com/questions/467396

  •  19-08-2019
  •  | 
  •  

Question

J'utilise les sockets Berkeley (domaine Internet et domaine Unix) et je me demandais si le serveur pouvait utiliser les mêmes sockets pour lire la demande et écrire une réponse au client. Sinon, le client doit-il créer un autre socket pour attendre la relecture et que le serveur se connecte après le traitement du message reçu.

À propos, je parle de sockets orientés connexion (sockets de flux, TCP, ...).

Il s’agit du code serveur simplifié (j’ai omis de vérifier les erreurs sur les appels système pour des raisons de simplicité):

int main() {

    int server_socket, connected_socket;
    struct sockaddr_in server_addr;
    char buf[1024];
    char aux[256];
    int bytes_read;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);    

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(1234);
    bind(server_socket, &server_addr, sizeof(server_addr))

    listen(server_socket, 5)

    connected_sodket = accept(server_socket, 0, 0);
    do {
        bzero(buf, sizeof(buf));
        bytes_read = read(connected_socket, buf, sizeof(buf));        
    } while (bytes_read > 0);          

    /* Here I want to use connected_socket to write the reply, can I? */

    close(connected_socket);       

    close(server_socket);

    return (EXIT_SUCCESS);
}

Et c’est le code client simplifié (j’ai oublié de vérifier les appels système ici pour plus de simplicité):

int main() {

    int client_socket;
    struct sockaddr_in server_addr;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    hp = gethostbyname("myhost");
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr, hp->h_addr_list[0], hp->h_length);
    server_addr.sin_port = htons(1234);

    connect(client_socket, &server_addr, sizeof(server_addr));

    write(client_socket, MSG, sizeof(MSG));

    /* Here I want to wait for a response from the server using client_socket, can I? */

    close(client_socket);

    return (EXIT_SUCCESS);
}

Puis-je utiliser connected_socket sur le serveur et client_socket sur le client pour renvoyer un message de réponse? Ou devrais-je utiliser l'adresse du client que j'ai dans le serveur lorsque je suis dans "accepter"? se connecter à un socket du client?

J'ai essayé d'utiliser read / wrint dans le client / serveur où le commentaire est affiché, mais de cette façon, les deux programmes restent bloqués, cela semble être un blocage immédiat.

Merci ins avance! Cordialement.

Était-ce utile?

La solution

Vous pouvez utiliser le même socket MAIS votre programme est configuré pour que le serveur lit TOUT ce que le client envoie avant de tenter de répondre. La boucle du serveur ne s'achèvera donc pas tant que le client n'aura pas fermé le côté écriture de son socket afin que le serveur reçoive un EOF (lecture de 0 octet), de sorte que le serveur ne renverra jamais sa réponse.

Vous pouvez gérer ce problème de plusieurs manières.

  1. Vous pouvez interrompre la boucle sur le serveur après avoir vu la totalité de la requête, plutôt que de lire jusqu'à EOF. Cela nécessite que les données envoyées par le client soient auto-délimitantes afin que le serveur puisse savoir quand il les a toutes lues.
  2. Vous pouvez utiliser une deuxième connexion pour la réponse. Probablement pas le meilleur.
  3. Vous pouvez utiliser un arrêt asymétrique du socket. Demandez au client d’exécuter shutdown (client_socket, SHUT_WR) pour fermer à demi le socket. Le serveur verra alors le EOF (et la boucle se terminera), mais l’autre direction sur le socket sera toujours ouverte pour la réponse.

Autres conseils

Vous devriez utiliser le même socket!

Votre protocole d’application définit sans ambiguïté le moment où le client et le serveur doivent attendre des données ou s’envoyer des messages; En supposant un protocole avec une seule demande du client et une réponse du serveur, les éléments suivants doivent être conservés:

  • Le client établit une connexion avec le serveur;
  • le client envoie sa requête (avec send ());
  • le client sait , en vertu du protocole, que le serveur répondra; par conséquent, il attend des données sur le même socket (recv ());
  • après avoir validé la réponse, le client peut fermer le socket.
  • le serveur accepte une connexion du client;
  • le serveur sait que la première étape revient au client, il attend donc des données (recv ());
  • le serveur valide la demande;
  • le serveur sait maintenant que le client est en attente du contenu du protocole; par conséquent, il envoie sa réponse avec send ();
  • le serveur sait , d'après le protocole, qu'il n'y a pas d'autres étapes; par conséquent, il peut fermer la prise.

Oui, c'est possible. Regardez cette page pour un exemple d'un serveur simple (et client simple). Notez que le serveur transmet généralement le descripteur de fichier "accepté" à un nouveau processus afin de pouvoir continuer à écouter davantage de connexions entrantes.

Non seulement devriez-vous utiliser le même socket (comme le dit Federico), mais vous devez également passer vos pare-feux à travers les pare-feu.

Les pare-feu connaissent les connexions TCP et autorisent automatiquement le transfert des données renvoyées si un ordinateur à l'intérieur du pare-feu a établi la connexion. Si au lieu de cela vous essayez de créer un nouveau socket TCP à partir de l'extérieur, le pare-feu le bloquera sauf si ce trafic est spécifiquement autorisé.

Oui, les sockets SOCK_STREAM sont bidirectionnels. Vous devriez être capable de lire et d'écrire sur / depuis le même socket de chaque côté de la connexion. La page de manuel de socket (2) contient plus de détails à ce sujet.

Oui, vous pouvez. Les sockets TCP sont bidirectionnels. Utilisez simplement les mêmes fonctions read () et write (). Assurez-vous également de vérifier les conditions d'erreur sur tous les appels à connecter (), read (), write (), ... car vous ne pouvez pas contrôler ce qui se passe sur le réseau.

Désolé; Je ne l'ai pas dit mais je l'ai essayé comme ça

Ce code sur le serveur où se trouve le commentaire:

write(connected_socket, "Ok, I got it", sizeof("Ok, I got it"));

et ce code dans le client où se trouve le commentaire:

read(client_socket, buf, sizeof(buf));

Les deux programmes restent bloqués et lorsque je tue le client, le serveur affiche les messages qu'il a reçus (j'ai un printf juste après que le serveur appelle read ).

Je l’essaye avec send et recv (les deux avec 0 drapeaux) au lieu de lire et écrire et cela n’a pas changé.

Dans votre configuration actuelle, le serveur essaie de lire jusqu'à ce que le client ferme le socket, tandis que le client ne ferme pas le socket jusqu'à ce que le serveur réponde. Par conséquent, vous avez une sorte de "blocage". Le serveur ne s’arrête pas pour lire dans l’espoir que des données supplémentaires pourraient arriver et que le client n’a aucun moyen de le dire au serveur.

Le serveur doit trouver un moyen de reconnaître que la demande est terminée, alors que la connexion est toujours ouverte. Si, par exemple, vous mettez fin à votre demande par une nouvelle ligne, le serveur peut vérifier s’il a reçu une ligne complète, puis arrêter de lire et envoyer la réponse.

L'avez-vous essayé? Il semble que cela devrait fonctionner quand vous insérez le code à l'endroit indiqué

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top