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

Вниз

Систмы передачи данных. Help   Найти похожие ветки 

 
__boolean   (2004-02-02 14:17) [0]

В общем написал систму передачи данных. Стал тестить. Запустил на 4 машинах под 10 клиентов.
Задал, чтобы они слали по 1000 запросов(ответы я сразу не дожидаюсь, отосланные запросы ставлю в очередь на ожидание), по 10 Кб каждый, на сервер, одновременно. В общем иногда происходил зависон. Как потом выяснил, сервер получал запросы и ставил из в очередь на отправку. И в какой-то момент времени так получалось что со стороны сервера слался ответ на запрос, а со стороны клиента очередной запрос и происходила взаимоблокировка 8( Даже не знаю как такую ситуацию разрулить. Сокеты асинхронные. Может кто подскажет как разрулить? Уже больше склоняюсь склоняюсь к тому, что требуется два канала. Один для отправки запросов на сервер, другой для приема ответов. Кстати, в системе заложено так, что сервер так же может делать запросы к клиенту. Опять же может получится так, что и клиент и сервер будут дделать запрос одновременно 8(


 
Digitman ©   (2004-02-02 14:25) [1]

в ситуации когда каждая из сторон должна иметь возможность (и право) посылать асинхронные запросы и, соотв-но, получать асинхронные ответы на ранее посланные запросы, необходимо реализовать ПИО, в котором каждое отправляемое/получаемое инф.сообщение снабжено признаком "запрос / ответ на запрос"

каждое инф.сообщение, имеющее признак "запрос", должно снабжаться уник.идентификатором запроса

каждое инф.сообщение, имеющее признак "ответ на запрос", должно ссылаться именно на тот идентификатор, которым был снабжен ранее переданный "запрос"

под такую схему и следует реализовывать диспетчер транспорта


 
Verg ©   (2004-02-02 14:28) [2]


> И в какой-то момент времени так получалось что со стороны
> сервера слался ответ на запрос, а со стороны клиента очередной
> запрос и происходила взаимоблокировка


Не очень понятно. Почему же взаимоблокировка происходит?
Значит не все ответы на запросы сервер отправлял?
Либо в протокле не поставлено четкой взаимосвязи запроса и ответа.
Ну в общем, пока как-то мутновато ты изложил суть.

> Опять же может получится так, что и клиент и сервер будут
> дделать запрос одновременно 8(


???????????


 
Digitman ©   (2004-02-02 14:37) [3]


> Уже больше склоняюсь склоняюсь к тому, что требуется два
> канала


не нужны они абсолютно
достаточно одного трансп.канала

диспетчеру транспорта д.б. совершенно безразлично, что он в дан.момент посылает/принимает.. его задача либо послать инф.сообщение, стоящее в очереди передачи, либо принять инф.сообщение и поместить его в соотв.очередь обработки (с возбуждением соот.события) .. т.е. прикладной транспорт + арбитраж очередей прикладного ввода/вывода

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


 
__boolean   (2004-02-03 13:20) [4]

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

Отослав запрос на сервер, я не дожидаюсь на него ответа(имеется ввиду результат обработки данных). Заголовок запроса на клиенте, я кладу в очередь ожидания результата. Как только с сервера приходит ответ, я по уникальному номеру нахожу на какой запрос был прислан результат обработки. Вытаскиваю его из очереди ожидания.

Допустим идет отправка данных большого размера. Возникает ситуация, когда отправлять данные уже нельзя(WSAEWOULDBLOCK), пока не получим сообщение что писать разрешено.(сокеты асинхронные). Я формирую цикл ожидания события FD_WRITE.

За это время, запросто может сформироваться ответ на другой запрос, который необходимо отправить. А т.к. отправка на запрос уже идет, его я ставлю в "очередь на отправку". Как только отсылка очередного ответа завршена, я проверяю очередь на отправку, если она не пуста, отсылаю следующий результат запроса. Тоже самое и на клиенте. Это сделано для того, чтобы можно было работать с фоновыми запросами. (не стопорю программу, чтобы обязательно дождаться результата, когда запрос выполниться, вызовется обработчик результата запроса)

В общем, обвешал я логами весь свой диспетчер транспорта(я его называю менеджер связи 8) ). Получилось такая вот штука, пакетов штук 30 летит как положенно. Перед тем как слать ответ клиенту(а клиент все запросы еще не отослал, шлет), я проверяю не идет ли в этот момент прием, если нет, шлю ответ. Но при ожидании события FD_WRITE, каким то хреном, прилетает событие FD_READ. Вот тут и возникает стопор на send, а потом вылетает ошибка 10053.

Систему построил по типу, каждому клиенту свой клсасс, со своим окном.

ps: Больше всего, что беспокоит если клиент 1, то все работает, пусть даже 10000 запросов.

pss: Может стоит на каждый запрос дожидаться ответа? После этого слать следующий запрос.


 
__boolean   (2004-02-03 13:31) [5]

У меня подозрение, что отправка идет с двух сторон, одновременно.
В моем случае, такое может получится запросто, машины разные, процессы разные. Программу не остановишь. Что клиент, что сервер оба проверели, что отправка либо прием не идут. И с двух сторон начинают слать. Клиент шлет очередной запрос и в тот же момент сервер шлет очередной ответ на запрос. И как разрулить ситуацию, не понятно.


 
Verg ©   (2004-02-03 13:34) [6]

Ну и что. Несмотря на FD_READ никто тебе пока не давал права делать send, раз был WOULDBLOCK  На FD_READ прими как обычно порцию данных, обработай, поставь ответ в свою очередь передачи и возвращайся к ожиданию событий ( в том числе и FD_WRITE ).

Если ты ждешь сообщений от асинхронного сокета, то ты должен быть готов, что может придти любое из заказанных тобой в WSAAsyncSelect в произвольный момент времени. т.е. ты не должен делать никаких предположений о том каое именно событие произойдет в ближайшее время. Может быть любое, а не обязательно FD_WRITE, например.


 
Digitman ©   (2004-02-03 13:38) [7]

диспетчер транспорта каждой из сторон соединения в каждый момент времени либо читает пришедший блок либо отправляет очер.блок ..

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


 
__boolean   (2004-02-03 13:43) [8]

Ладно, допустим такая ситуация. На сервере произошло событие, о котором должны знать все клиенты. Т.е. теперь сервер играет роль клиента, а клиенты роль сервером. Сервер должен оповестить  клиентов, и формирует для них запросы. Проверяет что ни запись ни чтение не идет, и вызывает send. И в этот же момент, клиент тоже шлет запрос и вызывает send. Нормальная ситуация. И сокеты все разрулят? Т.е. шлются данные, возникает WSAEWOULDBLOCK, прилетает FD_READ, читаем, возникает WSAEWOULDBLOCK, прилетает FD_WRITE, пишем и т.д. Я правильно понял?


 
Verg ©   (2004-02-03 14:00) [9]

1. С точки зрения протокла TCP/IP сервер - клиент отличаются только пока соединение не установлено. После этого они становятся совершенно  равноправными.

2.

> Т.е. шлются данные, возникает WSAEWOULDBLOCK, прилетает
> FD_READ, читаем, возникает WSAEWOULDBLOCK, прилетает FD_WRITE,
> пишем и т.д. Я правильно понял?


Шлем данные. может возниккнуть WSAEWOULDBLOCK.
Возвращаемся к циклу ожидания событий (сообщений)

Может появится либо FD_READ, либо FD_WRITE, либо FD_CLOSE
Case Event Of
FD_READ : обрабатываем запрос, если появился результат, то вызываем <процедуру передачи>
FD_WRITE : Если очередь передачи не пуста, то передаем в сокет до тех пор, пока либо не кончится эта очередь, либо не появится ошибка сокета.
FD_CLOSE : думаю, что понятно что надо делать

end;
Возвращаемся к циклу ожидания событий (сообщений)
end of;

procedure <процедура передачи>;
Проверяем не пуста ли очередь передачи.

if пуста then передаем в сокет до тех пор, пока не появится ошибка сокета.

Остатки данных (непереданную часть) складываем в очередь.
end of procedure;


 
Verg ©   (2004-02-03 14:12) [10]

Вот - на скору руку накидал. Может и с ошибками, но принцип должен быть понятен.



TBridge = class(TDataModule)
.............
 TxQue : string;
.........
end;

function  TBridge.SendData(var Buffer; Size : integer):integer;
var Bptr : pChar;
   Sends : integer;
   S : string;
begin
 BPtr := pchar(@Buffer);
 Result:=1; Sends:=0;
 if TxQue="" then
 begin
   while (Result>0) and (Sends<Size) do
   begin
     result:=ClentSocket1Socket.SendBuf((Bptr+Sends)^,Size-Sends);
     if Result>=0 then inc(Sends,Result);
   end;
   if Result=SOCKET_ERROR then
   begin
     if WSAGetLastError=WSAEWOULDBLOCK then
       SetString(TxQue, Bptr+Sends, Size-Sends);
   end else begin result:=Sends; TxQue:=""; end;
 end else
 begin
   SetString(S, Bptr, Size);
   TxQue:=TxQue+S;
   Result:=0;
 end;
end;

procedure TBridge.ClientSocket1Write(Sender: TObject;
 Socket: TCustomWinSocket);
var S : string;
begin
 if TxQue<>"" then
 begin
   S:=TxQue;
   TxQue:="";
   SendData(Pchar(@S[1])^, length(S));
 end;
end;


Точно тоже самое и с серверной стороной, только соединений может быт несколько.


 
__boolean   (2004-02-03 16:41) [11]

Да я-то все подобное делаю. Все работает, но с одним клиентом. Вот когда их штук 10 и шлют запросы пачками, тогда и загибается. В среднем для 8 все нормально, а 2 отваливаются. В общем ошибка прилетает в WSAGETSELECTERROR(lParam) и равна 10053.


 
Verg ©   (2004-02-03 17:21) [12]

Тогда не видя твоего кода ничего сказать нельзя. Ошибка-то где-то там (в коде).

Понимаешь, вот как это понять?

> Но при ожидании события FD_WRITE, каким то хреном, прилетает
> событие FD_READ. Вот тут и возникает стопор на send, а потом
> вылетает ошибка 10053.


Я понял так, что ты, ожидая события именно только FD_WRITE и получив вместо него FD_READ, записываешь таки данные в сокет ф-цией send. А это есть неправильно...

Короче опять получаются "глухие телефончики".


 
Digitman ©   (2004-02-04 08:32) [13]

1. ждешь FD_XXXX ...

получил FD_READ - читай маленькую (!) порцию данных из вх.потока и вновь на 1..

получил FD_WRITE - пиши маленькую (!) порцию данных из исх.потока, если он не пуст, и вновь на 1..

получил FD_CLOSE или WM_ЗАКРУГЛЯТЬТЯ - немедленно закрывай гнездо , освобождай прочие ресурсы и выходи из цикла

вот и все премудрости транспортного диспетчера ... более ничем он заниматься не должен ... все остальное (обработка принимаемых порций, "склейка"/"разбиение" входящих порций, формирование исх.потока) должны делать другие код.потоки, не имеющие к транспорту прямого отношения


 
__boolean   (2004-02-04 10:02) [14]

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


 
Verg ©   (2004-02-04 10:18) [15]


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


Так он же отключается не на все ядро TCP, а только для данного сокета. Если по этому гнезду идут коротенькие запросики, то и пусть Нагл будет отключен на этом сокете.


 
__boolean   (2004-02-04 16:12) [16]

Логи сервера

Recive Header:   QueryID:1 User:1 hWnd:  787916 Socket:464 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Recive Data:     QueryID:1 User:1 hWnd:  787916 Socket:464 bCanRead=1 bCanWrite=1 bReading=0 bWriting=0
Recive Finished: QueryID:1 User:1 hWnd:  787916 Socket:464 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Recive Header:   QueryID:2 User:1 hWnd:  787916 Socket:464 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Header:     QueryID:1 User:1 hWnd:  787916 Socket:464 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Data:       QueryID:1 User:1 hWnd:  787916 Socket:464 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Finished:   QueryID:1 User:1 hWnd:  787916 Socket: -1 bCanRead=0 bCanWrite=0 bReading=0 bWriting=0

Логи клиента

Send Header:     QueryID:1 User:1 hWnd: 2425270 Socket:492 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Data:       QueryID:1 User:1 hWnd: 2425270 Socket:492 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Finished:   QueryID:1 User:1 hWnd: 2425270 Socket:492 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Header:     QueryID:2 User:1 hWnd: 2425270 Socket:492 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Data:       QueryID:2 User:1 hWnd: 2425270 Socket:492 bCanRead=0 bCanWrite=1 bReading=0 bWriting=0
Send Finished:   QueryID:2 User:1 hWnd: 2425270 Socket: -1 bCanRead=0 bCanWrite=0 bReading=0 bWriting=0

Как видно, на сервере принят заголовок от запроса 2. Потом идет ожидание самих данных. Пока сервер ждет прилета данных, т.к. писать можно (флаги состояния показаны), сервер отсылает ответ на запрос номер 1. Но в это время клиент спокойно шлет данные от запрос номер 2. т.к. как писать можно (bCanWrite=1). Т.е., с обоих сторон получилось и клиент и сервер шлет данные. Клиент от запроса номер 2, сервер ответ на запрос номер 1. И обоих вылетает ошибка. Вот про этот случай я и говорил(взаимоблокировка).

зы: Флаг состояния что писать нельзя я сталю в false когда при отправке вылетело WSAEWOULDBLOCK

зыы: После отправки очередного запроса, я смотрю, есть ли в очереди еще запросы на отправку, если есть, возбуждаю событие отправить следующий запрос из функции(делаю за счет того, что ставлю таймер на 1мс, т.к. у таймера самый низкий приоритет,  FD_READ я поймаю быстрее, если прилетит сообщение, что необходимо читать)


 
__boolean   (2004-02-04 16:17) [17]

bCanRead  - можно читать
bCanWrite - можно писать
bWriting  - идет чтение
bReading  - идет запись


 
__boolean   (2004-02-04 16:17) [18]

Прошу прощение, наоборот
bReading  - идет чтение
bWriting  - идет запись


 
Verg ©   (2004-02-04 22:47) [19]


> Verg ©   (03.02.04 17:21) [12]



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

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

Наверх




Память: 0.52 MB
Время: 0.04 c
7-1075381286
RedSerg
2004-01-29 16:01
2004.04.11
Нужна прога управления LPT под NT !


1-1082471366
ReNoiZer
2004-04-20 18:29
2004.04.11
компиляция midas и dbExpress dll s прямо в приложение...


14-1079488343
vidiv
2004-03-17 04:52
2004.04.11
Как скрыть код php?


14-1081797439
RealRascal
2004-04-12 23:17
2004.04.11
Как же называется эта мелодия....


4-1075794884
LiteX
2004-02-03 10:54
2004.04.11
Поиск файлов и определение доступных дисков в системе.





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