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

Вниз

Что-то я не понял в устройстве TCustomWinSocket   Найти похожие ветки 

 
AkaSaint ©   (2004-03-20 12:49) [0]

Изучил часть исходников сабж (Дельфи 6):

function TCustomWinSocket.SendStream(AStream: TStream): Boolean;
begin
 Result := False;
 if FSendStream = nil then
 begin
   FSendStream := AStream;
   Result := SendStreamPiece;
 end;
end;

function TCustomWinSocket.SendStreamPiece: Boolean;
var
 Buffer: array[0..4095] of Byte;
 //...

begin
 //...
     while True do
     begin
         //...
         AmountSent := send(FSocket, Buffer, AmountInBuf, 0);
         if AmountSent = SOCKET_ERROR then
         begin
           ErrorCode := WSAGetLastError;
           if ErrorCode <> WSAEWOULDBLOCK then
           begin
 //...
 DropStream;
 //...
           end else
           begin
             FSendStream.Position := StartPos;
             Break;
           end;
         end else //...;
  //...
     end;//while
     Result := True;
     //...
end;

DropStream уничтожает поток, который был аргументом SendStream, и кое-что еще, здесь это не имеет значения.
Я использую неблокирующие сокеты.
У меня 2 вопроса по приведенному коду:
1) Если я правильно понял, то, если send вернет через WSAGetLastError ошибку WSAEWOULDBLOCK, то, вопреки хелпу по SendStream, где говортися, что Stream в любом случае будет уничтожен, Stream на самом деле уничтожен не будет, кроме того,
SendStream вернет True, хотя, как я понимаю, данные в этом случае отправлены не будут! Объясните, пожалуйста, в чем дело.
2) SendStream передает Stream библиотеке сокетов порциями по 4096 байт. Я бы хотел знать: если я посылаю данные большего объема (скажем, требующие 2 порции), то как будут распределены эти данные по пакетам TCP/IP: а) ни при каких обстоятельствах в
одном пакете не окажутся данные из 2-х порций; б) при определенных условиях (например, если первая порция еще не отправлена) порции могут быть снова собраны вместе и в одном пакете могут оказаться данные из обеих порций; в) если выполнение
метода SendStream не будет прервано ОС (например, закончилось время потока), то порции обязательно будут слиты вместе и опять же в одном пакете могут оказаться данные из обеих порций.
Вопрос 2 меня волнует по той причине, что, если имеет место ответ а), то снижается эффективность использования сети: практически наверняка, если объем передаваемых разом данных превышает 4 Кб, часть пакетов будет идти по сети "недогруженными", а объем заголовков у них один и тот же.


 
Verg ©   (2004-03-20 13:52) [1]

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

procedure TCustomWinSocket.Write(Socket: TSocket);
begin
 if (FSocket = INVALID_SOCKET) or (Socket <> FSocket) then Exit;
 if not SendStreamPiece then Event(Self, seWrite);
end;


2) Размер пакетов TCP/IP формируется вне зависимости от того, буферами какого размера мы преписывали информацию в передающий буфер транспорта (при помощи ф-ции send).
TCP отправляет данные IP-порциями размером не более MSS (maximum segment size). MSS анонсируется собеседником при установлени соединения и как правило выбирается равным MTU сетевого интерфейса, через который идет обмен, минус размеры заголовков TCP и IP (Например у Ethernet, при использовании IPv4 этот MSS=1460, а при IPv6 MSS=1440). Если собеседник не указал MSS, то по-умолчанию он считается равным 536.
Дробление порций по MSS происходит когда приложение успевает наполнять передающий буфер TCP. Если ли же не успевает, то работает так называемый алгоритм Нагла, который приводит к тому, что несмотря на наличие в ПБТ информации, передача сегмента не начинается, если количество этой информации меньше, чем MSS. Если приложение так и не "дошлет" новой информации, то по этому алгоритму, спустя некий таймаут передача сегмента таки начнется. В результате сформируется сегмент размером меньше, чем MSS. Алгоритм Нагла может быть отключен на сокете, если требуется избежать этих псевдозадержек, когда передача приложением ведется маленькими кусочками. Например, TelNet.


 
AkaSaint ©   (2004-03-20 18:50) [2]

1) Твои слова о том, что поток будет передан в любом случае, не
согласуются с исходным кодом SendStreamPiece. Предположим, Stream
содержит более 4 Кб исходных данных и при передаче первых 4 Кб
происходит WSAEWOULDBLOCK. Тогда в SendStreamPiece выполнится:

FSendStream.Position := StartPos;
Break;//выход из while

Получается, что передан не весь поток. И при этом SendStreamPiece, а вместе с ним и SendStream вернет True. И FSendStream не будет Free, хотя это утверждается в хелпе по SendStream.

>надо дождаться FD_WRITE, чтобы продолжать передачу оставшихся
>данных


Это не соответствует методу Write, приведенному тобой. Метод
Event:

procedure TCustomWinSocket.Event(Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);
begin
 if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket,
SocketEvent);
end;


Он просто вызовет обработчик OnSocketEvent, но он не ждет прихода
FD_WRITE от собственно сокета.

2) А как отключить этот метод Нагла, или куда копать, чтобы найти, как это сделать?


 
Digitman ©   (2004-03-22 09:11) [3]

Note: The Stream passed as a parameter to SendStream becomes “owned” by the windows socket object.  The Windows socket object frees the stream when it is finished with it.  

finished отнюдь не означает "завершение работы метода SendStream"


 
Digitman ©   (2004-03-22 09:14) [4]

The value returned by SendStream indicates whether any information was successfully written to the connection.

any information отнюдь не означает "вся информация, хранимая потоком"


 
Verg ©   (2004-03-22 09:32) [5]


> Это не соответствует методу Write, приведенному тобой. Метод
>
> Event:
>
> procedure TCustomWinSocket.Event(Socket: TCustomWinSocket;
>
>
> SocketEvent: TSocketEvent);
> begin
>  if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket,
>
> SocketEvent);
> end;
>
>
> Он просто вызовет обработчик OnSocketEvent, но он не ждет
> прихода
> FD_WRITE от собственно сокета.


Я че-то не понял. Что значит не соответсвует? Событие FD_WRITE, которое обязательно возникнет после send с WSAEWOULDBLOCK, будет обработано в CMSocketMessage таким образом
 with Message do
   if CheckError then
     case SelectEvent of
       FD_CONNECT: Connect(Socket);
       FD_CLOSE: Disconnect(Socket);
       FD_READ: Read(Socket);
       FD_WRITE: Write(Socket);
      FD_ACCEPT: Accept(Socket);
     end;

а Write первым делом вызовет SendStreamPiece  и передача потока возобновится с того места, где она была прервана всвязи с WSAEWOULDBLOCK.


> 2) А как отключить этот метод Нагла, или куда копать, чтобы
> найти, как это сделать?


А зачем тебе его отключать? Тебе его отключение ровным счетом ничего не даст, раз ты передаешь потоки.


 
Verg ©   (2004-03-22 09:38) [6]

Ааааа. Кажется я понял:

Ты считаешь, что вызвав Socket.SendStream и получив управление от нее обратно - дело сделано? Поток передан?
Ничуть не бывало!
Возврат из SendStream означает лишь то, что операция передачи потока НАЧАЛАСЬ. И все-е!
Когда его передача будет реально закончена можно узнать только в деструкторе этого потока.


 
Digitman ©   (2004-03-22 09:50) [7]


> Когда его передача будет реально закончена можно узнать
> только в деструкторе этого потока.


реализуй своего наследника TStream, перекрой диструктор - и будешь извещен о разрушении потока, т.е. о факте полной обработки потока

примерная реализация непрерывной поточной передачи с использованием SendStream()

 TOutgoingMemStream = class(TMemoryStream)
 private
   FOnDestroy: TNotifyEvent;
 public
   constructor Create(OnDestroy: TNotifyEvent);
   destructor Destroy; override;
 end;

constructor TOutgoingMemStream.Create(OnDestroy: TNotifyEvent);
begin
 inherited Create;
 FOnDestroy := OnDestroy;
end;

destructor TOutgoingMemStream.Destroy;
begin
 if Assigned(FOnDestroy) then
   FOnDestroy(Self);
 inherited Destroy;
end;

procedure TSocketTransport.DoOnOutgoingStreamDestroyed(Sender: TObject);
begin
 FOutgoingStream := nil;
end;

function TSocketTransport.Send(Data: IDataBlock): Integer;
var
 BlockType: Integer;
 StreamToSend: TMemoryStream;
 StreamSize: DWord;
 CurPos: DWord;
begin
 BlockType := Data.Signature and asMask;
 if (BlockType = CallSig) and (Data.Context = 0) then
   begin
     Inc(FContext);
     Data.Context := FContext;
   end;
 Result := Data.Context;
 InterceptOutgoing(Data);
 StreamToSend := TMemoryStream(Data.Stream);
 StreamSize := StreamToSend.Size;
//!!!!!!!!!!!!!!!!!!!!!!!!!!
 if not Assigned(FOutgoingStream) then
   FOutgoingStream := TOutgoingMemStream.Create(DoOnOutgoingStreamDestroyed);
 CurPos := FOutgoingStream.Position;
 FOutgoingStream.Position := FOutgoingStream.Size;
 FOutgoingStream.WriteBuffer(StreamToSend.Memory, StreamToSend.Size);
 FOutgoingStream.Position := CurPos;
 try
   FWinSocket.SendStream(FOutgoingStream);
 except
   FOutgoingStream.Free;
   raise;
 end;
//!!!!!!!!!!!!!!!!!!!!!!!!!!
end;


 
AkaSaint ©   (2004-03-25 17:15) [8]

2Digitma,Verg: Спасибо вам за ваши комментарии, я действительно неправильно понимал передачу данных из потока. Теперь многое встало на свои места.
2Digitman: Спасибо за пример кода непрерывной передачи из потока.
2Verg:
>А зачем тебе его отключать? Тебе его отключение ровным счетом >ничего не даст, раз ты передаешь потоки.

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


 
AkaSaint ©   (2004-03-25 17:49) [9]

Как отключить алгоритм Нагла, я нашел: setsockopt, TCP_NODELAY. Как вы считаете, даст это положительный результат, если я знаю, что отправляемые мной пакеты малы (десятки байт)?


 
Verg ©   (2004-03-25 18:11) [10]


> AkaSaint ©   (25.03.04 17:49) [9]
> Как отключить алгоритм Нагла, я нашел: setsockopt, TCP_NODELAY.
> Как вы считаете, даст это положительный результат, если
> я знаю, что отправляемые мной пакеты малы (десятки байт)?


Понимаешь, когда как.
В точночности этот алгоритм основывается еще и на наличии или отсутствии наподтвержденных данных у TCP передатчика.
Если ты просто отправил в только что установленное соединение хоть один байт, то он будет отправлен (будет сформирован сегмент из одного байта данных) немедленно, а вот если сразу за ним (например, за время меншее RTT) ты отправишь еще один, то этот второй байт отправлен не будет, пока TCP не получит подтверждение на первый сегмент с первым байтом или пока таких байтов не наберется размером с MSS.
Я же говорил - если передача ведется "маленькими порциями", небольшими кусочками, когда необходим получать ответы (уровня приложения) на каждый из этих кусочков.
В приципе алгоритм Нагла отключают крайне редко - терминальные программы по типу Telnet и т.п. Он же предназначен для более эффективного использования сети (канала). Еще алгоритм Нагла часто взаимодействует с другим алгоритмом TCP - алоритмом delayed ACK. Но, это уже другая история....

Короче. В твоем случае я пока не вижу насущной необходимости в рулежке этим механизмом.


 
Verg ©   (2004-03-25 21:41) [11]

Можешь вот это почитать: выдержка из FAQ одного из форумов
http://atakmim.fashion.bg/f0rum/tema.php?TemaID=26&RubID=2

2.16. How do I use TCP_NODELAY?

First off, be sure you really want to use it in the first place. It
will disable the Nagle algorithm (see ``2.11 How can I force a socket
to send the data in its buffer?""), which will cause network traffic
to increase, with smaller than needed packets wasting bandwidth.
Also, from what I have been able to tell, the speed increase is very
small, so you should probably do it without TCP_NODELAY first, and
only turn it on if there is a problem.

Here is a code example, with a warning about using it from Andrew
Gierth:

int flag = 1;
int result = setsockopt(sock, /* socket affected */
IPPROTO_TCP, /* set option at TCP level */
TCP_NODELAY, /* name of option */
(char *) &flag, /* the cast is historical
cruft */
sizeof(int)); /* length of option value */
if (result < 0)
... handle the error ...

TCP_NODELAY is for a specific purpose; to disable the Nagle buffering
algorithm. It should only be set for applications that send frequent
small bursts of information without getting an immediate response,
where timely delivery of data is required (the canonical example is
mouse movements).

2.17. What exactly does the Nagle algorithm do?

It groups together as much data as it can between ACK"s from the other
end of the connection. I found this really confusing until Andrew
Gierth (andrew@erlenstar.demon.co.uk) drew the following diagram, and
explained:

This diagram is not intended to be complete, just to illustrate the
point better...

Case 1: client writes 1 byte per write() call. The program on host B
is tcpserver.c from the FAQ examples.

CLIENT SERVER
APP TCP TCP APP
[connection setup omitted]

"h" ---------> [1 byte]
------------------>
-----------> "h"
[ack delayed]
"e" ---------> [Nagle alg. .
now in effect] .
"l" ---------> [ditto] .
"l" ---------> [ditto] .
"o" ---------> [ditto] .
"n"---------> [ditto] .
.
.
[ack 1 byte]
<------------------
[send queued
data]
[5 bytes]
------------------>
------------> "ellon"
<------------ "HELLOn"
[6 bytes, ack 5 bytes]
<------------------
"HELLOn" <----
[ack delayed]
.
.
. [ack 6 bytes]
------------------>

Total segments: 5. (If TCP_NODELAY was set, could have been up to 10.)
Time for response: 2*RTT, plus ack delay.

Case 2: client writes all data with one write() call.

CLIENT SERVER
APP TCP TCP APP
[connection setup omitted]

"hellon" ---> [6 bytes]
------------------>
------------> "hellon"
<------------ "HELLOn"
[6 bytes, ack 6 bytes]
<------------------
"HELLOn" <----
[ack delayed]
.
.
. [ack 6 bytes]
------------------>

Total segments: 3.

Time for response = RTT (therefore minimum possible).

Hope this makes things a bit clearer...

Note that in case 2, you don"t want the implementation to gratuitously
delay sending the data, since that would add straight onto the
response time.


 
Verg ©   (2004-03-25 21:51) [12]

Там, кстати, в пункте 2.11 (по ссылке сходи) есть высказывания Ричарда Стивенса (W.Richard Stevens) по этому поводу - очень будет полезно.


 
Verg ©   (2004-03-25 21:58) [13]

Да, вот тут оригинал
http://www.ibrado.com/sock-faq/



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

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

Наверх




Память: 0.51 MB
Время: 0.035 c
1-1083158142
Николай
2004-04-28 17:15
2004.05.16
Как сделать форму невидимой?


1-1083383881
BigLeha
2004-05-01 07:58
2004.05.16
Значки на кнопках


3-1082362660
}|{yk
2004-04-19 12:17
2004.05.16
Как создавать оптимальные планы запросов в Interbase?


4-1080243638
Swimmer
2004-03-25 22:40
2004.05.16
Как создать меню полностью на WinAPI?


14-1083208481
тихий вовочка
2004-04-29 07:14
2004.05.16
Ole





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский