Как заглушить сокет в C?
Я написал код клиента, который должен посылать некоторые данные через сокет и читать ответ с удаленного сервера.
Я хотел бы протестировать этот код. Подпись функции - что-то вроде:
double call_remote(double[] args, int fd);
где fd
дескриптор файла сокета для удаленного сервера.
Теперь call_remote
Функция после отправки данных заблокирует чтение ответа с сервера. Как я могу заглушить такой удаленный сервер для модульного тестирования кода?
В идеале я хотел бы что-то вроде:
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;
}
мне бы хотелось stub_behavior
позвонить и отправить 42.0
значение вниз по дескриптору заглушки файла.
Есть ли простой способ сделать это?
4 ответа
Если это система POSIX, вы можете использовать fork()
а также 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;
}
... конечно, вы, вероятно, захотите добавить некоторую проверку ошибок и изменить логику, которая читает запрос.
Дескриптор файла, созданный socketpair()
это нормальный сокет, и, следовательно, вызовы сокетов, как send()
а также recv()
будет хорошо работать на нем.
Вы можете использовать все, что можно получить с помощью файлового дескриптора. Файл или, если вы хотите смоделировать блокирующее поведение, канал.
Примечание: специфичные для сокетов вызовы (setsockopt, fcntl, ioctl, ...) не будут работать.
Я столкнулся с такой же ситуацией, и я поделюсь своим подходом. Я создал сетевые дампы того, что именно должен отправлять клиент, и каким должен быть ответ сервера. Затем я провел побайтовое сравнение запроса клиента, чтобы убедиться, что он совпадает. Если запрос действителен, я читаю из файла ответов и отправляю его обратно клиенту.
Я рад предоставить более подробную информацию (когда я на машине с доступом к этому коду)
Вот реализация C++ (я знаю, оригинальный вопрос был о C, но при желании его легко конвертировать обратно в C). Это, вероятно, не работает для очень больших строк, так как сокет, вероятно, будет блокироваться, если строка не может быть буферизована. Но это работает для небольших юнит-тестов.
/// 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];
};