cppmm 26.06.2009 23:15

CodingТестируем клиент-серверный софт.

Иногда бывают ситуации, когда надо увидеть, что же на самом деле происходит между клиентом и сервером. Т.е. увидеть, о чём и как они разговаривают. Конечно, всегда есть tcpdump, но иногда его не хватает(или он не совсем удобен). Например, если надо подробно увидеть все передаваемые данные в нормальном, понятном человеку виде. Вот, например, недавно я видел, что клиент серверу запрос отправил, а вместо ответа получает какую-то ерунду. Потом, после очередного ковыряния конфигов, было видно, что сервер отвечает нормально, но теперь уже клиент непонятно что сыпет.

Ну или другой пример - пишете вы своё небольшое клиент-серверное приложение, общающиеся по своему протоколу, но вот что-то как-то не выходит общение.

В таких случаях одним из вариантов тестирования может быть написание простенького фейкового сервера. Простой пример подробного я и опишу.



Представим такую абстрактную ситуацию: сервер, слушающий порт 3333 и клиент, что-то у этого сервера спрашивающий. После tcpdump'а стало видно, что после очередного запроса, клиент ждёт какого-то ответа, но вместо этого получает сообщение об ошибке от сервера(tcpdump -ntvvvXpi). Кто виноват - неясно. То ли клиент неправильный запрос шлёт, то ли сервер неправильно на него отвечает. Разбираться в исходниках клиента и сервера нет ни времени, ни желания(а зачастую и знаний, потому как чёрт его знает, чего там разработчики напридумывают), поэтому сделаем хитрый манёвр - напишем простейший сервер, выполняющий минимум запросов и посмотрим, что же нам говорит на это клиент. В качестве языка я выбрал perl, потому как это perl, а всё остальное неправославно. :)

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/perl -w

#

# Подключаем нужные модули.

use IO::Socket;

use IO::File;



# Инициализируем сервер.

$server_port = 3333;

$server = IO::Socket::INET->new(LocalPort => $server_port,

Type => SOCK_STREAM,

Reuse => 1,

Listen => 1)

or die "Не могу создать tcp-сервер на $server_port порту : $@\n";



# Отключаем буферизацию, чтобы не мешалась - мы не высоконагруженный

# боевой сервер пишем, а тестовый скрипт.

$server->autoflush(1);

# Запускаем сервер в бесконечном цикле.

while ($client = $server->accept()) {

# Читаем всё, что приходит на сервер в переменную $data

while ( $data = <$client>) {

# Ожидаемый диалог

if ( $data eq "client-request\n" ) {

print $client "server-answer\n";

}

# Действительный запрос, записываемый в лог.

else {

$logfile = IO::File->new(">> file.log")

or die "Файл file.log не найден. : $!\n";

print $logfile $data;

$logfile->close();

}

}

}

$server->close();



Ну а теперь подробнее. Инициализацию сервера, буферизацию и подробные особенности perl'а описывать смысла нет - это всё есть в документации(пакет perl-doc). Переходим сразу к делу. Наш импровизированный сервер висит на интерфейсе и слушает всё, что туда приходит. Всё, что можно, он складывает в лог, но если получает запрос "client-request", то отвечает на него так, как ожидает клиент. Т.е. вместо client-request мы подставляем запрос, на котором затыкается общение клиента с сервером. Скрипт, видит его и отвечает на него так, как нужно клиенту, чтобы общение продолжалось. Следующий запрос клиента складывается в лог. Информация собрана.

После этого любимым текстовым редактором открываем лог и читаем, что же там наговорили наши клиенты с сервером. Находим строки, связанные с проблемным запросом и смотрим, правильно ли ответил клиент на него, или нет. Если правильно, то значит проблема в сервере и начинаем ковырять его, если же нет, то мучаем настройки клиента.

Вот, собственно и всё.

Разумеется, я выложил самый простой пример. В реальных условиях скрипт дополняется несколькими запросами. Иногда даже приходится целый диалог организовывать, чтобы найти ошибку. Но организация подобного диалога по сути - обычная копипаста внутри скрипта. Это значительно проще, чем лопатить километровые конфиги или разбираться в чужих исходниках. Зная же точно, где происходит затык, найти ошибку уже проще.

Т.е. это не рабочий пример, а просто описание одного из способов тестирования ПО.


Тэги: perl script сервер
+ 3 -
Похожие Поделиться

sdvn 26.06.2009 23:46 #
А может просто сразу стоит слушать через тот же Ethereal с дешифровкой пакетов? Ничего писать не придется, и сразу поможет понять где ошибка.
cppmm 27.06.2009 00:10 #
Слушать можно. Но иногда некоторые ошибки можно выявить только после некоторого диалога. Простой пример: если просто слушать 25-ый порт, нельзя проверить работоспособность почтового клиента, потому что пока клиент не получит ответа на приветствие(helo или ehlo), он не начнёт аутентификацию и собственно пересылку почты. Цель этого подхода как раз "заставить говорить".
sdvn 28.06.2009 00:31 #
Это называется "юнит-тест", который обязан провести программист. На мой взгляд ничего нового сей пост в ряды программистов не принес.
cppmm 29.06.2009 14:36 #
Я админ и от программирования относительно далёк. :)
lwilis 27.06.2009 00:42 #
Если сервер с клиентом договориться не могут, то запросы от клиента получить не трудно тем же tcpdump, а вот что должен отвечать сервер нужно где-то узнавать. Какие есть варианты?
lwilis 27.06.2009 00:46 #
после непродолжительного раздумья.
В документации к клиенту, в разделе "поддерживаемые ответы"?
cppmm 27.06.2009 09:23 #
Зависит от клиента.
Но определённо в документации.
К примеру, когда приходилось тестировать swf-флешку, она высылала запрос вида: <policy-file-request/> и ожидала в ответ файл crossdomain.xml одной строкой.
Ну или другой вариант - в описании протокола. Взять ту же почту. Там вполне определённый набор команд и чётка последовательность (helo, ответ, mail from:<>, ответ и т.д.).