Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2002.11.07;
Скачать: [xml.tar.bz2];

Вниз

Увеличить размер UDP датаграммы   Найти похожие ветки 

 
_landy   (2002-08-22 10:43) [0]

Как можно увеличить размер UDP датаграммы? Пробовал использовать Indy, передается только 1024 байта за один вызов. TNMUDP позволяет передавать 2048 байт, но не больше.


 
Digitman   (2002-08-22 10:59) [1]

http://www.biger.kiev.ua/tcpip/tcp11.html


 
Digitman   (2002-08-22 11:06) [2]

Цитата оттуда же :


Максимальный размер UDP датаграммы

Теоретически максимальный размер IP датаграммы может составлять 65535 байт, что ограничивается 16-битным полем полной длины в IP заголовке (см. рисунок 3.1). При длине IP заголовка равной 20 байтам и длине UDP заголовка равной 8 байтам в UDP датаграмме для пользовательских данных остается максимум 65507 байт. В большинстве реализаций, однако, используются датаграммы значительно меньшего размера.

Обычно играют роль два ограничения. Во-первых, программа приложение может быть ограничена программным интерфейсом. Сокеты API (глава 1, раздел "Интерфейсы прикладного программирования") предоставляют функцию, которая может быть вызвана приложением, чтобы установить размер буферов ввода и вывода. Для UDP сокета этот размер напрямую связан с максимальным размером UDP датаграммы, которая может быть прочитана и записана UDP. В настоящее время большинство систем предоставляют по умолчанию максимальный размер UDP датаграммы, которая может быть прочитана или записана, равный 8192 байтам. (Эта значение установлено в 8192, потому что именно столько по умолчанию читается и записывается системой NFS.)

Следующее ограничение определяется реализацией ядра TCP/IP. Могут существовать характеристики реализации (или ошибки), которые ограничивают размер UDP датаграммы значением меньшим, чем 65535 байт.



Автор экспериментировал с различными размерами UDP датаграмм, используя программу sock. С использованием loopback интерфейса под SunOS 4.1.3, максимальный размер UDP датаграммы был 32767 байт. Использовать большее значение не удавалось. При передаче по Ethernet от BSD/386 к SunOS 4.1.3, максимальный размер IP датаграммы, которую мог принять Sun, составлял 32786 (при этом пользовательских данных было 32758 байт). С использованием loopback интерфейса в Solaris 2.2 максимальный размер IP датаграммы, которая могла быть отправлена и принята, составлял 65535 байт. При передаче от Solaris 2.2 к AIX 3.2.2 удалось передать IP датаграмму максимального размера в 65535 байт.



В разделе "IP заголовок" главы 3 мы упомянули, что хосту необходимо получать IP датаграммы размером по меньшей мере 576 байт. Большинство приложений UDP разработаны таким образом, чтобы ограничивать свои приложения в размере 512 байт данных или меньше, чтобы уложиться в это ограничение. В разделе "RIP: протокол обмена информацией о маршрутизации" главы 10, например, мы видели, что RIP всегда посылает в датаграмме меньше чем 512 байт. Это же самое ограничение мы найдем и в других UDP приложениях: DNS (глава 14), TFTP (глава 15), BOOTP (глава 16) и SNMP (глава 25).

Усечение датаграмм

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

К сожалению, ответ зависит от программного интерфейса и реализации.



Традиционные версии Berkeley сокет API обрезают датаграммы, отбрасывая любые непоместившиеся данные. Будет ли приложение поставлено в известность, зависит от версии. (4.3 BSD Reno и более поздние версии могут уведомить приложение о том, что датаграмма была обрезана.) API сокеты под SVR4 (включая Solaris 2.x) не обрезают датаграммы. Любые непоместившиеся данные последовательно считываются. Приложение не уведомляется о нескольких циклах считывания и ему будет передана одна UDP датаграмма. TLI API не отбрасывают данные. Вместо этого возвращается флаг, указывающий на то, что данных больше, чем можно считать за один раз, поэтому приложение начинает последовательно считывать оставшуюся датаграмму.



Когда мы будем обсуждать TCP, то увидим, что этот протокол предоставляет последовательные потоки байт, направляемые в приложение, без каких-либо ограничений. TCP передает в приложение данные любого размера, которые требуются для приложения - причем данные на этом интерфейсе никогда не теряются.



 
_landy   (2002-08-22 11:56) [3]

Это я уже читал. Собственно говоря, интересует это момент:

> Сокеты API (глава 1, раздел "Интерфейсы прикладного программирования")
> предоставляют функцию, которая может быть вызвана приложением,
> чтобы установить размер буферов ввода и вывода


Какая именно функция sockets API позволяет изменить размер этого буфера?


 
Digitman   (2002-08-22 12:03) [4]

см. WinsockAPI-ф-цию SetSockOpt()


 
_landy   (2002-08-22 15:46) [5]

Ее я уже пробовал использовать. Вот пример:

UDP.RemoteHost := "10.10.3.255";
NewOption := 16384;
Result := SetSockOpt (UDP.ThisSocket, SOL_SOCKET, SO_SNDBUF, @NewOption, SizeOf(NewOption));


UDP - экземпляр TNMUDP. Для передачи используется SendBuf. На принимающей стороне соответственно делается:

NewOption := 16384;
Result := SetSockOpt (UDP.ThisSocket, SOL_SOCKET, SO_RCVBUF, @NewOption, SizeOf(NewOption));


Тем не менее, стоит только увеличить размер передаваемых данных до величины свыше 2048 байт, в обработчике на принимающей стороне

procedure TClientForm.UDPDataReceived(Sender: TComponent;
NumberBytes: Integer; FromIP: String; Port: Integer);


NumberBytes становится равным -1.

Еще идеи есть?


 
Digitman   (2002-08-22 16:49) [6]

И чему у тебя равен Result после возврата из SetSockOpt() ?
Уверен, что ф-ция отработала успешно ?


 
_landy   (2002-08-23 07:36) [7]

Да, конечно. Это первым делом проверяется. И на приемной и на передающей стороне Result = 0.


 
_landy   (2002-08-23 08:17) [8]

Вот более полный пример. Берем стандартный UDPDEM.PAS, добавляем в него установку размеров приемного и передающего буферов:

procedure TForm1.Button1Click(Sender: TObject);
var
MyStream: TMemoryStream;
TmpStr: String;
J: Integer;
NewOption, Result, OptLen: Integer;
Begin
NMUDP1.ReportLevel := Status_Basic;
NMUDP1.RemoteHost := Edit1.Text;
NMUDP1.RemotePort := StrToInt(Edit2.Text);

NewOption := 16384;
Result := SetSockOpt (NMUDP1.ThisSocket, SOL_SOCKET, SO_RCVBUF, @NewOption, SizeOf(NewOption));
if Result <> 0 then begin
MessageDlg("Ошибка " + IntToStr(Result) + " on SetSockOpt", mtError, [mbOK], 0);
Exit;
end;

NewOption := 16384;
Result := SetSockOpt (NMUDP1.ThisSocket, SOL_SOCKET, SO_SNDBUF, @NewOption, SizeOf(NewOption));
if Result <> 0 then begin
MessageDlg("Ошибка " + IntToStr(Result) + " on SetSockOpt", mtError, [mbOK], 0);
Exit;
end;

// TmpStr := Edit3.Text;
TmpStr := "";
for J := 1 to 2050 do
TmpStr := TmpStr + IntToStr(J mod 10);

MyStream := TMemoryStream.Create;
try
MyStream.Write(TmpStr[1], Length(TmpStr));
NMUDP1.SendStream(MyStream);
finally
MyStream.Free;
end;
end;


Пытаемся передавать 2050 байт самому себе. 2048 байт передается нормально, больше - никак, или сваливается с исключением при передаче, или при приеме приходит -1 байт.


 
Digitman   (2002-08-23 08:24) [9]

1. Что показывает детальный анализ ошибок приема и передачи ?
Нигде не вижу WSAGetLastError().

2. Режим NMUDP по-умолчанию - асинхронный. Где обработка событий OnRead(), OnWrite(), OnError() ?


 
_landy   (2002-08-23 10:18) [10]

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

procedure TForm1.NMUDP1DataReceived(Sender: TComponent;
NumberBytes: Integer; FromIP: String; Port: Integer);
var
MyStream: TMemoryStream;
TmpStr: String;
Result: Integer;
begin
Result := WSAGetLastError;

// error! Result = 10040 (wsaEMsgSize)

MyStream := TMemoryStream.Create;
try
NMUDP1.ReadStream(MyStream);
SetLength(TmpStr,NumberBytes);
MyStream.Read(TmpStr[1],NumberBytes);
Memo1.Lines.Add(FromIP+": "+TmpStr);
finally
MyStream.Free;
end;
end;



Ругается, как и следовало ожидать, :)) на размер буфера. А обработчики все есть. OnRead я привел, остальные только в строку статуса пишут сообщения. Эвента OnError у TNMUDP нет.

Кстати, насколько я знаю, по-умолчанию этот размер буфера (в Indy так, и предварительный вызов GetSockOpt это подтверждает) составляет 8192 байта, тем не менее, компоненты UDP из Indy передавать могут только по 1024 байта. Пробовал увеличивать его до 32768 - тот же самый результат: буфер устанавливается, но пакет не доходит.


 
_landy   (2002-08-23 10:40) [11]

Кстати, пробовал получать значение SO_MAX_MSG_SIZE, возвращает 65507. Вполне достаточно, казалось бы. :))


 
Digitman   (2002-08-23 13:28) [12]

У меня - D5, исходников к TMNUDP нет. Соответственно, не представляю, в какой момент гнездо инициализируется в объекте (NMUDP.ThisSocket - св-во отсутствующее в D5)

Если в D6 есть исходники, посмотри, не происходит где-либо по тексту SetSockOpt(SO_RCVBUF/SO_SNDBUF) уже ПОСЛЕ твоей принудительной установки, непосредственно перед recvfrom()/sentto(). Подозреваю, что может быть и так.


В событии OnRead() я бы первым делом прочитал реальное состояние проблемной опции вызовом GetSockOpt(). Кр.того, некорректно требовать WSAGetLastError() до выполнения WSA-ф-ции, выполняющей чтение/запись. Актуальна эта ф-ция в интересующем нас контексте будет лишь после recvfrom() (на стороне приемника) и sendto() (на стороне передатчика).

Исследуй исходники, переработай события приема/передачи с учетом этих замечаний и сообщи о результатах.


 
_landy   (2002-08-26 09:16) [13]

Нет, исходников TNMUDP в d6 также нет, но пробовал перечитывать размер буфера непосредственно при приеме - он не изменился. Бросил этот NMUDP, переписал код на winAPI, заработало сразу, даже без установки буферов. Передается сразу 65520 байт, при дефолтном буфере в 8192. :)) Так что проблема снялась, будем писать так. :) Thx за содействие.


S := "";
for J := 1 to sLen do
S := S + IntToStr(J mod 10);

// Передача
SendAddr.sin_family := AF_INET;
SendAddr.sin_port := 8100;
SendAddr.sin_addr.S_addr := inet_addr ("10.10.3.255");
Result := SendTo (_UDP, S[1], sLen, 0, SendAddr, SizeOf(SendAddr));
Result := WSAGetLastError;

// Прием
S := "";
for J := 1 to sLen do S := S + "0";
RecvSize := SizeOf(_UDP_Addr);
Result := RecvFrom (_UDP, S[1], sLen, 0, _UDP_Addr, RecvSize);
Result := WSAGetLastError;



 
_landy   (2002-08-26 09:19) [14]

Да, забыл добавить, если вдруг кому понадобится:
Переменные описаны как:

var
_UDP: TSocket;
_UDP_Addr, SendAddr: TSockAddr;


Инициализируется все это дело:

_UDP := Socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
_UDP_Addr.sin_family := AF_INET;
_UDP_Addr.sin_port := 8100;
_UDP_Addr.sin_addr.S_addr := INADDR_ANY;
Bind (_UDP, _UDP_Addr, SizeOf(_UDP_Addr));


Причем, вызов WSAStartup не понадобился.


 
Digitman   (2002-08-26 10:06) [15]


> Бросил этот NMUDP, переписал код на winAPI


Лишний раз подтверждает : где-то в реализации NMUDP есть фрагменты, реинициализирующие режим гнезда непосредственно перед вызовом транспортных ф-ций, вне зависимости от результатов явной инициализации. И лишний раз дает повод усомниться в правильности выбора компонентов без исх.текстов при необходимости полного контроля над происходящим


> Причем, вызов WSAStartup не понадобился.

А вот это ты напрасно рекламируешь, уверяю тебя : этого делать не следует.
В частном случае (N гнезд создается в одном и том же код.потоке) этот фокус еще пройдет (но вряд ли без последствий). В общем же случае следует таки соблюдать требования спецификации : каждое гнездо должно создаваться/уничтожаться (вне зависимости от конкретного кодового потока - лишь бы в одном и том же) в контексте индивидуальной "сессии", каковой по сути явл-ся блок в теле "скобок" WSAStartup/WSACleanup. Сессия управляет распределением ресурсов, выделяемых системой при создании/инициализации/работе/деинициализации/уничтожении гнезда как объекта в составе ОС. При нарушении сессионных WinSock-соглашений возникает риск утечки ресурсов и, как следствие - весьма вероятного отказа типа "падение сети" (WSAENETDOWN)


 
МОДЕРАТОРЫ - ЛОХИ ! МЕРЛИН, ГОНИ ИХ НАХУЙ !   (2002-08-26 14:43) [16]

test


 
!!!!   (2002-09-07 14:08) [17]

Уважаемые смотрящие за форумом - уберите мусор !!!



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2002.11.07;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.51 MB
Время: 0.008 c
1-21621
KIP
2002-10-26 19:11
2002.11.07
Озвучка


4-21895
slippery1
2002-09-24 16:01
2002.11.07
Помогите!!!


1-21675
AFrolov
2002-10-25 17:15
2002.11.07
как создать свой обработчик и повесить его на событие Timer a


3-21418
lesalesa
2002-10-18 15:46
2002.11.07
Возможна ли фильтрация по вычислимому полю для TTable?


8-21686
Самборский Евгений
2002-07-10 19:29
2002.11.07
как поместить GIF на форму





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский