Эта статья не несёт в себе огромного прикладного смыла, хотя в некоторых случаях быстрая передача файлов через netcat может выручить. Основная её цель – объяснить, почему работает именно так и как сделать, чтобы работало быстрее. Итак, было замечено, что скорость передачи файлов по ftp составляет ~8 Mb/s, а через netcat ~1MB/s. Экперимент проводился в 100BaseTX сети, состоящей из двух узлов (Freebsd и Solaris).
На одном узле включаем сервер (Solaris):
# nc -l -p 1234 > /tmp/ccc
На другом запускаем копирование (FreeBSD):
# dd if=/tmp/file | nc 192.168.1.1 1234
Будем смотреть количество переданных пакетов (Solaris):
# snoop -o /tmp/snoop -d dmfe1 port 1234
Смотрим результат:
48183818 bytes transferred in 24.023524 secs (2005693 bytes/sec)
Количество переданных пакетов пакетов 23423. При копировании по ftp пакетов передаётся около 5000. Очевидно, что данные по ftp передаются бОльшими порция, поэтому и быстрее. Смотрим на системные вызовы ftp-клиента:
# truss -p 1501
….
read(4," 87\n04:02:35 3 "...,32768) = 32768 (0x8000)
write(5," 87\n04:02:35 3 "...,32768) = 32768 (0x8000)
….
4 - дескриптор файла /tmp/file, 5 – дескриптор сокета. Читаем и пишем кусками по 32768 байт.
Смотрим на системные вызовы nc-клиента:
# truss -p 1311
….
read(0," 4 11 0 86\n04"...,1024) = 1024 (0x400)
write(3," 4 11 0 86\n04"...,1024) = 1024 (0x400)
….
В этом случае мы читаем и пишем кусками по 1024 байт, поэтому узкое место в количесте прерываний в секунду (смотрим vmstat 1 на Solaris'е и видим, что их число может доходить до 5000!!!). Задача ясна – нужно увеличить буфер на отправку и на приём.
Берём netcat: http://internap.dl.sourceforge.net/sourceforge/netcat/netcat-0.7.1.tar.gz
В файле src/core.c видим, что размер буфера зашит в коде в двух местах:
int core_readwrite(nc_sock_t *nc_main, nc_sock_t *nc_slave)
{
int fd_stdin, fd_stdout, fd_sock, fd_max;
int read_ret, write_ret;
unsigned char buf[1024];
bool inloop = TRUE;
fd_set ins, outs;
struct timeval delayer;
assert(nc_main && nc_slave);
…..
for (socks_loop = 1; socks_loop <= sockbuf[0]; socks_loop++) {
int recv_ret, write_ret;
struct msghdr my_hdr;
unsigned char buf[1024];
struct iovec my_hdr_vec;
struct sockaddr_in rem_addr;
Исправляем на 32768 на обоих хостах, компилируем, запускаем копирование, получаем ~9000 Kb/s. А если увеличить буфер до 65536 байт, то скорость возрастает до 10500 Kb/s.