Задания к выполнению лабораторной работы выдаются преподавателем непосредственно перед занятием




Методические указания для выполнения лабораторных работ по курсу «Глобальные вычислительные сети»

 

«Сравнительный анализ производительности протоколов TCP и UDP»

 

 

Новополоцк, 2007


Цель работы: провести сравнительный анализ производительности протоколов TCP и UDP.

Теоретические сведения

TCP – это сложный протокол, обеспечивающий базовую службу доставки IP-датаграмм, надежность и управление потоком. В то же время UDP добавляет лишь контрольную сумму. Поэтому может показаться, что UDP должен быть на порядок быстрее TCP. Исходя из этого предположения, многие программисты полагают, что с помощью UDP можно достичь максимальной производительности.

Да, действительно, есть ситуации, когда UDP существенно быстрее TCP. Но иногда использование TCP оказывается эффективнее, чем применение UDP.

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

Рассмотрим, когда и почему производительность UDP больше, чем TCP. Прежде всего, поскольку TCP сложнее, он выполняет больше вычислений, чем UDP. Например, реализация TCP в системе 4.4 BSD содержит примерно 4500 строк кода на языке С в сравнении с 800 строками для UDP. Но в типичной ситуации большая часть времени процессора в обоих протоколах тратиться на копирование данных и вычисление контрольных сумм, поэтому здесь нет большой разницы.

Для обеспечения надежности TCP должен посылать подтверждения (ACK-сегменты) на каждый принятый пакет. Это несколько увеличивает объем обработки на обоих концах. Во-первых, принимающая сторона может включить ACK в состав данных, которые она посылает отправителю. В действительности во многих реализациях TCP отправка ACK задерживается на несколько миллисекунд: предполагается, что приложение-получатель вскоре сгенерирует ответ на пришедший сегмент. Во-вторых, TCP не обязан подтверждать каждый сегмент. В большинстве реализаций при нормальных условиях ACK посылается только на каждый второй сегмент.

Еще одно принципиальное отличие между TCP и UDP в том, что TCP требует логического соединения и, значит, необходимо заботиться об его установлении и разрыве. Для установления соединения обычно требуется обменяться тремя сегментами. Для разрыва соединения нужно четыре сегмента, которые, кроме последнего, часто можно скомбинировать с сегментами, содержащими данные.

Предположим, что время, необходимое для разрыва соединения в большинстве случаев не расходуется зря, поскольку одновременно передаются данные. Следует выяснить, что же происходит во время установления соединения. Как показано на рисунке 1, клиент начинает процедуру установления соединения, посылая серверу сегмент SYN (синхронизация). В этом сегменте указывается порядковый номер, который клиент присвоит первому посланному байту, а также другие параметры соединения. В частности, максимальный размер сегмента (MSS), который клиент готов принять, и начальный размер окна приема. Сервер в ответ посылает свой сегмент SYN, который также содержит подтверждение ACK на сегмент SYN клиента. И, наконец, клиент отсылает ACK на сегмент SYN сервера. На этом процедура установления соединения завершается. Теперь клиент может посылать свой первый сегмент данных.

 

 

Рисунок 1 – Установление соединения

 

На рисунке 1 RTT (round-trip time) – это период кругового обращения, то есть время, необходимое пакету для прохождения с одного хоста на другой и обратно. Для установления соединения нужно полтора таких периода. При длительном соединении между клиентом и сервером (например, клиент и сервер обмениваются большим объемом данных) указанный период «размазывается» между всеми передачами данных, так что существенного влияния на производительность это не оказывает. Однако если речь идет о простой транзакции, в течение которой клиент посылает запрос, получает ответ и разрывает соединение, то время инициализации составляет заметную часть от времени всей транзакции. Таким образом, следует ожидать, что UDP намного превосходит TCP по производительности именно тогда, когда приложение организует короткие сеансы связи. И, наоборот, TCP работает быстрее, когда соединение поддерживается в течение длительного времени при передаче больших объемов данных.

Организовать посылку заданного числа датаграмм UDP клиентом можно следующим образом:

 

 

//число посылаемых датаграмм

int datagrams = 5000;

//размер одной датаграммы

int dgramsz = 1440;

SOCKET s;

char buf[1440];

int rc;

struct sockaddr_in peer;

//посылка серверу указанного числа датаграмм

while (datagrams-- > 0)

{

rc = sendto(s, buf, dgramsz, 0, (struct sockaddr*)&peer, sizeof(peer));

if (rc <= 0) printf(“Ошибка вызова sendto”);

}

//посылаем серверу последнюю датаграмму,

//содержащую нулевой байт. Для сервера она

//выполняет роль конца файла

sendto(s, “”, 0, 0, (struct sockaddr*)&peer, sizeof(peer));

 

Код UDP сервера, принимающего датаграммы выглядит следующим образом:

//число принимаемых датаграмм

int datagrams = 0;

//размер одной датаграммы

int dgramsz = 1440;

int rcvbufsz = 5000*1440;

SOCKET s;

char buf[1440];

//выделяем память для буфера на 5000 датаграмм длиной до 1440 байт

setsockopt(s, SOL_SOCKET, SO_RVCBUF, (char*)&rcvbufsz, sizeof(int));

//читаем и подсчитываем датаграммы,

//пока не придет пустая датаграмма или не произойдет ошибка

for(;;)

{

rc = recv(s, buf, sizeof(buf), 0);

if(rc <= 0)

break;

datagrams++;

}

 

Повысить производительность TCP можно за счет выбора правильного размера буферов передачи и приема. Нужно установить размер буфера приема для сокета сервера и размер буфера передачи для сокета клиента. Сообщать TCP размеры буферов нужно во время инициализации соединения, то есть до вызова listen в сервере и до вызова connect в клиенте.

 

//Код TCP-клиента, выполняющего роль источника

struct sockaddr_in peer;

char *buf;

SOCKET s;

//число операций записи в сокет

int blks = 5000;

//количество данных, передаваемых при каждой операции записи в сокет

int sndbufsz = 32*1024;

//размер буфера

int sndsz = 1440;

/*

Стандартный код инициализации TCP-клиента с добавлением обращения

к функции setsockopt для установки размера буфера передачи, а также

с помощью функции malloc выделения буфера запрошенного размера

для размещения данных, посылаемых при каждой операции записи

*/

//Инициализировать память, на которую указывает buf не надо, так как

//в данном случае безразлично, какие данные посылать

if(buf=malloc(sndsz) == NULL)

printf(“Ошибка вызова malloc”);

//функция set_address записывает в поля переменной типа sockaddr_in

//указанные адрес и номер порта

set_address(“localhost”, “9000”, &peer, “tcp”);

s = socket(AF_INET, SOCK_STREAM, 0);

if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&sndbufsz, sizeof(sndbufsz)))

printf(“Ошибка вызова setsockopt c опцией SO_SNDBUF”);

if(connect(s, (struct sockaddr*)&peer, sizeof(peer)))

printf(“Ошибка вызова connect”);

//вызываем функцию send нужное число раз

while(blks-- > 0)

send(s, buf, sndsz, 0);

 

/*

Стандартный код инициализации TCP-сервера с добавлением

вызова функции getsockopt для установки размера буфера

*/

struct sockaddr_in local;

struct sockaddr_in peer;

int peerlen;

SOCKET s;

SOCKET s1;

const int on = 1;

//размер буфера приема сокета

int rcvbusz = 32*1024;

set_address(NULL, “9000”, &local, “tcp”);

s = socket(AF_INET, SOCK_STREAM, 0);

if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)))

printf(“Ошибка вызова функции setsockopt с опцией SO_REUSEADDR”);

if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcvbufsz, sizeof(rcvbufsz)))

printf(“Ошибка вызова функции setsockopt с опцией SO_RCVBUF”);

if(bind(s, (struct sockaddr*)&local, sizeof(local)))

printf(“Ошибка вызова bind”);

listen(s,5);

do{

peerlen = sizeof(peer);

s1 = accept(s, (struct sockaddr*)&peer, peerlen);

server(s1, rcvbufsz);

close (s1);

}while(0);

/*

Функция server читает и подсчитывает поступающие байты,

пока не обнаружит конец файла или не возникнет ошибка.

Она выделяет память под буфер того же размера, что и

буфер приема сокета, чтобы прочитать максимальное

количество данных за одно обращение к recv.

*/

static void server(SOCKET s, int rcvbufsz){

char *buf;

int rc;

int bytes = 0;

if((buf=malloc(rcvbufsz)) == NULL)

printf(“Ошибка вызовы malloc”);

for(;;){
rc = recv(s, buf, rcvbufsz, 0);

if(rc <= 0)

bytes+=rc;

}

}

 

Для тестирования производительности TCP и UDP запустим клиента и сервера на одной машине, чтобы оценить производительность TCP и UDP, устранив влияние сети. В данном случае сегменты TCP или датаграммы UDP инкапсулируются в IP-датаграммах и посылаются возвратному интерфейсу lo0, который немедленно переправляет их процедуре обработки IP-входа, как показано на рисунке 2.

 

Рисунок 2 – Возвратный интерфейс

Размер датаграмм (в случае UDP) или число передаваемых за один раз байтов (в случае TCP) задается 1440, т.к. эта величина близка к максимальному размеру сегмента, который может передать по локальной сети на базе Ethernet. Это число получается так. В одном фрейме Ethernet может быть передано не более 1500 байт. Каждый заголовок IP и TCP занимает 20 байт, так что остается 1460. Еще 20 байт зарезервировано для опций TCP.

TCP работает намного быстрее, когда в качестве имени сервера выбрано localhost. Для UDP это не так – заметной разницы в производительности нет. Объясним почему. Максимальный размер передаваемого блока (MTU – maximum transmission unit) для bsd равен 1500, а для localhost – 16384. Это объясняется тем, что при первом построении маршрута к хосту bsd в коде маршрутизации предполагается, что хост находится в локальной сети, поскольку сетевая часть IP-адреса совпадает с адресом интерфейса Ethernet. И лишь при первом использовании маршрута TCP обнаруживает, что он ведет на тот же хост и переключается на возвратный интерфейс. Однако к этому моменту все метрики маршрута, в том числе и MTU, уже установлены в соответствии с интерфейсом к локальной сети. При посылке данных на localhost TCP может отправлять сегменты длиной до 16384 байт (или 16384 -20-20-12 = 16332 байт). Однако при посылке данных на хост bsd число байт в сегменте не превышает 1448 (как было сказано выше). Но чем больше размер сегментов, тем меньше их количество приходиться посылать, а это значит, требуется меньший объем обработки, и соответственно снижаются накладные расходы на добавление к каждому сегменту заголовков IP и TCP. В результате обмен данными с хостом localhost происходит в три раза быстрее, чем с хостом bsd.

На хосте localhost TCP работает примерно в два раза быстрее, чем UDP. Это связано с тем, что TCP способен объединять несколько блоков по 1440 байт в один сегмент, тогда как UDP посылает отдельно каждую датаграмму длиной 1440 байт. Следует отметить, что в локальной сети UDP быстрее, чем TCP, но потеря датаграмм значительнее. Потери имеют место даже тогда, когда сервер и клиент работают на одной машине, связаны они с исчерпанием буферов. Это означает, что UDP не дает никакой гарантии относительно доставки данной датаграммы, даже если оба приложения работают на одной машине.

 

Задания к выполнению лабораторной работы выдаются преподавателем непосредственно перед занятием

 

Примерный вариант задания

Написать приложение для тестирования сравнительной производительности протоколов TCP и UDP. Для этого необходимо:

1. реализовать тестового UDP клиента, который посылает нефиксированное (произвольное) количество датаграмм, которые сервер читает, подсчитывает и отбрасывает;

2. реализовать тестового UDP сервера, который принимает датаграммы от клиента и подсчитывает их количество, пока не придет пустая датаграмма, служащая признаком конца посылки датаграмм, или не произойдет ошибка;

3. реализовать тестового TCP клиента, который посылает нефиксированное (произвольное) количество TCP сегментов, которые сервер читает, подсчитывает и отбрасывает;

4. реализовать тестового TCP сервера, который принимает TCP сегменты от клиента и подсчитывает их количество, пока не получит конец файла или не произойдет ошибка;

5. для измерения производительности без влияния сети запустить клиента и сервера на одной машине, в качестве имени сервера в начале указав сетевое имя компьютера, а затем localhost;

6. протестировать клиента и сервера в условиях локальной сети;

7. каждый тест выполнить 30 раз с заданным размером датаграмм (в случае UDP) или числом передаваемых за один раз байтов (в случае TCP), равным 1440 и 500, в ходе тестирования собрать следующие сведения: время, с момента запуска до завершения работы клиента; результат деления общего числа посланных байтов на время (Мб/с), среднее число потерянных датаграмм для протокола UDP;

 

Содержание отчета

 

1. Наименование работы, цель работы

2. Блок-схемы реализованных программ

3. Результаты тестов в виде таблиц вида:

 

 

Таблица 1 – Сравнение производительности TCP и UDP при количестве посылаемых байтов, равном 1440

TCP
сервер время Мб/с потеряно
Bsd      
Localhost      
Sprc      
UDP
сервер время Мб/с потеряно
Bsd      
Localhost      
Sprc      

 

4. Выводы по полученным данным

 

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: