Форум: "Сети";
Текущий архив: 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.043 c