-
Доброго времени суток, уважаемые!
Начал поднимать незавершенный проект, очень похожий на прокси.
Все, что нужно вроде бы уже готово, но вот остановился я на ограничении пропускной способности, т.к. с этим сервером будет работать промышленный компьютер, для которого пакеты будут немного перерабатываться и отсылаться с более медленной скоростью.
Ну так вот в чем проблема. Для каждого соединения с "прокси" создается свой поток, в этом потоке учавствуют две основные переменные: одна переменная связывает клиента с собой (сервером), вторая образует связь сервер<->запрашиваемый сервер. Проблемы касаются именно второй переменной и выявляются, например, при скачивании большого файла. Ее тип (второй переменной) - TClientSocket. Алгоритм примерно такой: клиент запрашивает нужный файл у прокси, прокси соединяется с удаленным сервером и затем в цикле передает данные (файл) клиенту до тех пор, пока Socket.ReceiveBuf удаленного соединения возвращает больше нуля, т.е. пока есть принятые данные. Внутри цикла еще работает ограничение пропускной способности по такому типу: смотрим сколько скачали за секунду, если достигнут разрешенный предел, уходим в Sleep. Но в какой-то момент почему-то ReceiveBuf становится равен нулю, т.е. будто удаленный сервер все что хотел сказать уже сказал, но файл не был передан до конца. Т.е. удаленный сервер не хочет больше отдавать файл, хотя соединение с ним остается установленным, поток также не сообщает о том, что уже убит и клиент упорно ждет, когда же ему отдадут недостающийся кусок файла. Затем устает ждать и говорит "пока". Т.е. вся проблема именно в том, что ReceiveBuf не хочет ничего толкать в буфер и утверждает, что ничего ему не приходило (Receive Length = 0).
Из-за чего такое может быть? В коде проблем не наблюдается, все работает как нужно, но до каких-то пор.
Вообще я был уверен, что проблема появилась именно тогда, когда начал удерживать пропускную способность с помощью слипов. Но сейчас, пока писАл это сообщение, что-то уверенность в этом пропала (забыл уже, т.к. проект давно не щупал). В общем, завтра еще раз проверю, убрав ограничение проп. спос. Но вопрос остается открытым :)
-
> (Receive Length = 0).
> Из-за чего такое может быть?
If the socket is connection oriented and the remote side has shut down the connection gracefully, a recv will complete immediately with zero bytes received.
> удерживать пропускную способность с помощью слипов
Дурная затея.
-
> If the socket is connection oriented and the remote side
> has shut down the connection gracefully, a recv will complete
> immediately with zero bytes received.
Но я так понял, что "remote side has shut down" не происходит, т.к. соединение между удаленным сервером остается установленным. Или это не показатель?
> Дурная затея.
Сам так думал потом, но порывшись в интернете ничего другого в голову не пришло.
-
> соединение между удаленным сервером остается установленным
На основании чего сделано такое умозаключение ?
> ничего другого в голову не пришло
Обычный таймер.
-
> На основании чего сделано такое умозаключение ?
Socket.Connected возвращает True
> Обычный таймер.
Таймер есть, он раз в секунду обнуляет счетчик. А слипы срабатывают только при достижении счетчиком предела. Или я что-то не так понял?
-
> Socket.Connected возвращает True
Режим какой - блокирующий ?
> Или я что-то не так понял?
>
С заданным интервалом, скажем 1 секунду, тикает таймер
При каждом тике от удаленного сервера запрашивается ровно столько, сколько требуется для обеспечения заданной скорости, скажем 10 кб. Все что получено тут же отдается клиенту. При этом как раз и обеспечивается требуемой скорости отдачи. Никакая буферизация при этом не требуется.
-
> Режим какой - блокирующий ?
stThreadBlocking
> При каждом тике от удаленного сервера запрашивается ровно
> столько, сколько требуется для обеспечения заданной скорости,
> скажем 10 кб. Все что получено тут же отдается клиенту.
> При этом как раз и обеспечивается требуемой скорости отдачи.
> Никакая буферизация при этом не требуется.
Примерный алгоритм понял, буду думать, как создавать таймеры для каждого подключения (кстати, таймер же можно, наверное, создать прямо внутри потока нужного подключения?).
-
> kernel (05.12.2010 20:45:04) [4]
А команда Disconnect поступала?
-
> stThreadBlocking
никак не вяжется с
> Socket.ReceiveBuf удаленного соединения
потому что с удаленным сервером твой прокси связывается через TClientSocket, у которого НЕТ такого свойства. У него есть ctNonBlocking и ctBlocking
-
> А команда Disconnect поступала?
От меня (т.е. от прокси) не поступала. Если только удаленный сервер сам решил разорвать.
> никак не вяжется с
Ага, виноват. Не у того сокета подглядел режим.
ClientType := ctBlocking;
-
> kernel © (05.12.10 21:16) [9]
Ну тогда все верно: с твоей стороны не было Socket.Disconnect - значит Connected будет True до второго пришествия.
-
> Ну тогда все верно: с твоей стороны не было Socket.Disconnect
> - значит Connected будет True до второго пришествия.
Т.е. пока я сам его не отрублю?
Тогда вытекает другой вопрос. Почему удаленный сервер отцепляется? Причем это не зависит от самого удаленного сервера (пробовал скачивать файлы с разных серверов). Я так понимаю, он должен это делать в какой-то момент, а я его должен попросить отдать файл дальше.
-
> Т.е. пока я сам его не отрублю?
Он уже "отрубился" по инициативе партнера - об этом тебе сказал ReceiveBuf = 0
Остается только освободить ресурсы, занятые сокетом на ТВОЕЙ стороне УЖЕ несуществующего соединения - вызовом Socket.Disconnect либо ClientSocketComponent.Close
> Почему удаленный сервер отцепляется?
А это ты у него спроси)
Может ему элементарно не нравится то что ты ему перед этим послал..
-
> А это ты у него спроси)
Он не говорит.
> Может ему элементарно не нравится то что ты ему перед этим
> послал..
Перед этим я ему только подготовленный "headers" запрашиваемого документа (файла) посылал. Затем получаю от него данные.
-
В любом случае спасибо всем ответившим. Завтра буду пробовать ковырять дальше (у меня сейчас ночь), отрублю временно ограничение ПС. Попытаю в таком состоянии его.
-
> Он не говорит
И не обязан, если прикладным протоколом это не предусмотрено.
> подготовленный "headers" запрашиваемого документа (файла)
> посылал
Значит недопослал или послал что-то не то.
Опять же надо смотреть на конкретный прикладной протокол и то как ты его соблюдаешь.
-
> Значит недопослал или послал что-то не то.
Сейчас посмотрел еще раз, заголовок посылает в моем случае Opera, а я лишь читаю его и вытаскиваю для себя оттуда хост, порт и URI.
-
Протокол - HTTP, надо понимать ?
-
HTTP(S)
-
Т.е. браузер шлет твоему прокси CONNECT-запрос, ты его парсишь, коннектишься к удал.серверу и ретранслируешь потом через себя инф.обмен между браузером и удал.сервером, выполняя при этом шейпинг входящего (для браузера) трафика, так ?