среда, 6 января 2010 г.

Клиент-серверное взаимодействие

Предисловие, написанное после завершения заметки: все тесты выполнялись на ноутбуке core2duo, со сниженной до 800Мгц частотой процессора (для мирного использования дома хочется тишины). На десктопе с coreQuad процессором время уменьшится в несколько раз (имхо примерно втрое).

Воспользуемся утилитами DJB. Проверим скорость:


$ time ./header@ localhost / 8081
HTTP/1.0 200 OK
Date: Tue, 5 Jan 2010 22:12:53 GMT
Connection: close
Cache-control: no-cache, no-store
Content-Type: text/html; charset=utf-8
Content-Length: 1502

real 0m0.017s
user 0m0.008s
sys 0m0.012s

$ time ./responce@ localhost / 8081 > test.html

real 0m0.018s
user 0m0.008s
sys 0m0.008s

$ time cat test.html |./header
HTTP/1.0 200 OK
Date: Tue, 5 Jan 2010 22:22:47 GMT
Connection: close
Cache-control: no-cache, no-store
Content-Type: text/html; charset=utf-8
Content-Length: 1502

real 0m0.009s
user 0m0.004s
sys 0m0.004s


Как видим, отправка запроса и получение ответа с помощью tcpclient занимает около 9 мс (включая время на преобразования переводов строк).

Утилиты header@ и responce@ представляют собой немного модифицированный код http@:


$ cat header@
#!/bin/sh
echo "GET /${2-} HTTP/1.0
Host: ${1-0}:${3-80}
" | /usr/bin/tcpclient -RHl0 -- "${1-0}" "${3-80}" sh -c '
/usr/bin/addcr >&7
exec /usr/bin/delcr <&6
' | awk '/^$/ { body=1; next } { if (!body) {print} else {exit}}'

$ cat responce@
#!/bin/sh
echo "GET /${2-} HTTP/1.0
Host: ${1-0}:${3-80}
" | /usr/bin/tcpclient -RHl0 -- "${1-0}" "${3-80}" sh -c '
/usr/bin/addcr >&7
exec /usr/bin/delcr <&6
'


Передача бинарных данных

Сервер можно организовать разными способами, рассмотрим два из них.
Первый вариант с помощью утилиты socat:

$ socat tcp-l:3000,reuseaddr,fork system:'md5sum',nofork

Второй вариант с помощью tcpserver (воспользуемся трассировкой ввода-вывода через recordio):

$ tcpserver -R -H 127.0.0.1 3000 recordio md5sum
12871 < Hello!
12871 < [EOF]
12871 > e134ced312b3511d88943d57ccd70c83 -
12871 > [EOF]


Клиент в обоих случаях следующий:

$ ./client.tcl
e134ced312b3511d88943d57ccd70c83 -

$ cat ./client.tcl
#!/usr/bin/tclsh8.6
set s [socket localhost 3000]
fconfigure $s -translation binary -buffering none
puts $s "Hello!"
close $s w
puts [read $s]
close $s


Замечу, что использован интерпретатор Tcl 8.6, т.к. для получения результата от потоковой утилиты md5sum нам необходима поддержка полузакрытых соединений (half-closed).

Теперь посмотрим, как организована передача данных в приложение, запускаемое на стороне сервера (для получения более подробного вывода используйте filan без ключа -s, и с ключом -s для краткого вывода, здесь же я привожу профильтрованный awk вывод, чтобы не загромождать текст):


$ socat tcp-l:3000,reuseaddr,fork system:'filan',nofork
$ ./responce@ localhost / 3000|awk '{print $1 "\t" $2}'
FD type
0: socket
1: socket
2: chrdev

$ tcpserver -R -H 127.0.0.1 3000 filan
$ r$ ./responce@ localhost / 3000|awk '{print $1 "\t" $2}'
FD type
0: socket
1: socket
2: chrdev

$ tcpserver -R -H 127.0.0.1 3000 recordio filan
$ telnet localhost 3000
$ ./responce@ localhost / 3000|awk '{print $1 "\t" $2}'
FD type
0: pipe
1: pipe
2: chrdev


Чтобы узнать полную информацию о параметрах сокетов, просто отключите фильтрацию вывода через awk.

Теперь, когда технология и реализация понятны, реализуем решение практической задачи.

Способ первый, через tcpserver:

$ tcpserver -R -H 127.0.0.1 3000 md5sum

$ time ./send.tcl README
34f58706e32612c4f70b480f12a051db -

real 0m0.017s
user 0m0.008s
sys 0m0.004s

$ md5sum README
34f58706e32612c4f70b480f12a051db README

Способ второй, через socat:

$ socat tcp-l:3000,reuseaddr,fork system:'md5sum',nofork

$ time ./send.tcl README
34f58706e32612c4f70b480f12a051db -

real 0m0.021s
user 0m0.008s
sys 0m0.008s

$ md5sum README
34f58706e32612c4f70b480f12a051db README


Итак, мы отправили на обработку бинарный файл, получили результат обработки и проверили его. Вариант с tcpserver работает быстрее (приведенные выше времена выполнения выбраны как наименьшие из 5-ти последовательных запусков для каждого способа). В случае использования контроля доступа выигрыш в скорости при использовании tcpserver окажется еще более заметен, т.к. socat полагается на стандартный способ, а утилиты DJB используют собственный оптимизированный метод.

Реализация утилиты send.tcl приведена ниже:

$ cat ./send.tcl
#!/usr/bin/tclsh8.6

if {$argc < 1} {
puts stderr "No_args"
exit 1
}

set s [socket localhost 3000]
fconfigure $s -translation binary -buffering none
puts $s [exec cat {*}$argv]
close $s w
puts [read $s]
close $s

Комментариев нет:


(C) Alexey Pechnikov aka MBG, mobigroup.ru