9 votes

Comment stub une socket en C ?

J'ai écrit un code client qui est censé envoyer des données via un socket et lire une réponse du serveur distant.

Je voudrais faire un test unitaire de ce code. La signature de la fonction est quelque chose du genre :

double call_remote(double[] args, int fd);

fd est le descripteur de fichier de la socket vers le serveur distant.

Maintenant, le call_remote Après avoir envoyé les données, la fonction bloquera la lecture de la réponse du serveur. Comment puis-je bloquer un tel serveur distant pour tester le code à l'unité ?

Idéalement, je voudrais quelque chose comme :

int main() {
  int stub = /* initialize stub */
  double expected = 42.0;

  assert(expected == call_remote(/* args */, stub);

  return 0;
}

double stub_behavior(double[] args) {
  return 42.0;
}

Je voudrais stub_behavior pour être appelé et envoyer le 42.0 dans le descripteur de fichier bloqué.

Y a-t-il un moyen facile de le faire ?

3voto

caf Points 114951

Si c'est un système POSIX, vous pouvez utiliser fork() y socketpair() :

#define N_DOUBLES_EXPECTED 10
double stub_behaviour(double []);

int initialize_stub(void)
{
    int sock[2];
    double data[N_DOUBLES_EXPECTED];

    socketpair(AF_UNIX, SOCK_STREAM, 0, sock);

    if (fork()) {
        /* Parent process */
        close(sock[0]);
        return sock[1];
    }

    /* Child process */

    close(sock[1]);

    /* read N_DOUBLES_EXPECTED in */
    read(sock[0], data, sizeof data);

    /* execute stub */
    data[0] = stub_behaviour(data);

    /* write one double back */
    write(sock[0], data, sizeof data[0]);
    close(sock[0]);
    _exit(0);
}

int main()
{
  int stub = initialize_stub();
  double expected = 42.0;

  assert(expected == call_remote(/* args */, stub);

  return 0;
}

double stub_behavior(double args[])
{
  return 42.0;
}

...bien sûr, vous voudrez probablement ajouter des contrôles d'erreurs et modifier la logique qui lit la requête.

Le descripteur de fichier créé par socketpair() est un socket normal, et donc les appels de socket comme send() y recv() fonctionnera parfaitement.

1voto

Karoly Horvath Points 45145

Vous pouvez utiliser tout ce à quoi on peut accéder avec un descripteur de fichier. Un fichier ou, si vous voulez simuler un comportement de blocage, un tube.

Note : évidemment, les appels spécifiques aux sockets (setsockopt, fcntl, ioctl, ...) ne fonctionnent pas.

0voto

Michael Mior Points 13475

J'ai rencontré la même situation et je vais partager mon approche. J'ai créé des vidages de réseau avec exactement ce que le client doit envoyer et ce que la réponse du serveur doit être. J'ai ensuite effectué une comparaison octet par octet de la requête du client pour m'assurer qu'elle correspondait. Si la demande est valide, je lis le fichier de réponse et le renvoie au client.

Je serai heureux de fournir plus de détails (lorsque je serai sur une machine ayant accès à ce code).

0voto

Mark Lakata Points 3458

Voici une implémentation en C++ (je sais, la question initiale portait sur le C, mais il est facile de la reconvertir en C si on le souhaite). Cela ne fonctionne probablement pas pour les très grandes chaînes de caractères, car le socket se bloquera probablement si la chaîne ne peut pas être mise en mémoire tampon. Mais cela fonctionne pour les petits tests unitaires.

/// Class creates a simple socket for testing out functions that write to a socket.
/// Usage:
///  1. Call GetSocket() to get a file description socket ID
///  2. write to that socket FD
///  3. Call ReadAll() read back all the data that was written to that socket.
///  The sockets are all closed by ReadAll(), so this is a one-use object.
///
/// \example
///  MockSocket ms;
///  int socket = ms.GetSocket();
///  send(socket,"foo bar",7);
///  ...
///  std::string s = ms.ReadAll();
///  EXPECT_EQ("foo bar",s);

class MockSocket
{
public:
    ~MockSocket()
    {
    }

    int GetSocket()
    {
        socketpair(AF_UNIX, SOCK_STREAM, 0, sockets_);
        return sockets_[0];
    }

    std::string ReadAll()
    {
        close(sockets_[0]);
        std::string s;
        char buffer[256];
        while (true)
        {
            int n = read(sockets_[1], buffer, sizeof(buffer));
            if (n > 0) s.append(buffer,n);
            if (n <= 0) break;
        }
        close(sockets_[1]);
        return s;
    }
private:
    int sockets_[2];
};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X