@dlangru

Страница 560 из 719
Pavel
10.05.2018
11:34:59
bool writeTCP(Fd fd, in ubyte[] buffer) { ssize_t count; ssize_t result; while (count != buffer.length) { assert(count < buffer.length); result = write(fd, buffer[count..$]); logTrace("fd:%d, written %d, need %d", fd, result, buffer.length); if (result == -1) { if (errno != EAGAIN) { logError("Write error fd: %d, %d %s", fd, errno, strerror(errno).to!string); return false; } debug logTrace("Need to write again"); continue; } else if (result < buffer.length) { debug logTrace("Need to write more"); } count += result; } return true; } Вот первоначальная реализация записи в целевой сокет из буфера

Мы в цикле ждем до упора либо пока не возникнет ошибка, либо пока не кончатся данные в буфере

Это значит что если принимающая сторона тормозит и все время говорит что она занята, то мы тут застряли навечно.

И весь event loop тут зависнет и не сможет обрабатывать тысячи других сообщений

Google
Pavel
10.05.2018
11:37:51
Все равно нужно будет "дергать стоп-кран"
Стоп крана не будет, мы получим сообщение о пришедших данных, увидим что буфер полон и пропустим это событие. Это легковесная операция, скорее всего это даже дешевле чем делать вызов на исключение сокета из прослушки событий

Кроме того все же может на пишущем сокете возникнуть событие "сокет разорвал соединение" и тогда мы просто закрываем источник и целевой и они сами удаляются из epoll очереди на прослушку.

Ievgenii
10.05.2018
11:42:16
Стоп

Ну а нафиг там цикл то?

Я подразумивал не такую реализацию

У тебя есть 2 сокета

1й на вход, 2й на выход

Ты вычитываешь в буфер из 1го сокета

2й сокет вешаешь в пул на запись (когда он будет готов принимать новые данные - система вернет и его)

За один обработчик ты пишешь столько, сколько в него влезит

Denis
10.05.2018
11:44:23
Да я вот думаю, а надо ли на каждый чих пакет пилить? Может сделать как все поступают - запилить проект "мой всякий стафф" и туда коммитить )
тоже вариант, но 1) надо перечислить в описании чтобы найти можно было, 2) надо всё равно начать с чего-то такой пакет

Ievgenii
10.05.2018
11:44:25
После записи в него ты ставишь пометку, что буфер теперь не 50 байт (как был в начале), а уже 30 (первые 20 просто игнорируются)

И уходишь в луп

Google
Ievgenii
10.05.2018
11:45:39
Если в 1м сокете что-то есть уже новое, и буфур еще не полон - ты вычитываешь столько, сколько влезит в конец (скажем еще 50, т.к. общий буфер 100)

Когда 2й сокет готов принять еще - пишешь в него с 21го байта

столько, сколько осталось (80), а потом меняешь флаг, сколько же ты реально в него записал

Твой случай - вычитывать из 1го сокета новые данные в начало буфера

И пытаться отправить неотправленные ("старые") данные

во второй сокет

НО! если у тебя реально на столько медленный 2й сокет

Ты попадешь на ситуацию, когда нужно игнорировать 1й сокет, пока не появится "свободная" часть буфера

И для этого ты и делал циклический, если я верно понял

НО зачем было его делать, если ты просто можешь игнорировать первый сокет, пока не скинешь весь буфер во 2й.

Если у тебя буфер не большой

Если у тебя буфер большой, скажем >4Kb, то да, твоя реализация, возможно, и даст плоды. Но она даст плоды только если второй сокет значительно медленее первого

А если +/- 20%, то ты всего то один раз на 5 итераций пропустишь обработку 1го сокета

А писать во второй сокет нужно, конечно, не в цикле, а разово столько, сколько в него залезит

а остальное дописывать только тогда, когда он будет готов к приему новых данных

Но ты опубликуй свою реализацию. Интересно глянуть.

По коду было бы прикольно что-то типа такого:

stream2.setInBuff(stream1.getOutBuff);

Denis
10.05.2018
11:54:09
Ждать пока исходящий сокет освободится - бесплатно

так что пул и не нужен вроде

Google
Denis
10.05.2018
11:54:27
мы же не в бесконечном цикле ждём

Про пакет: несколько месяцев назад мне понадобился кольцевой буфер и я сделал костылями его. Просто потому что "ну не может же быть что никто его ещё не сделал! не нашёл, наверно. А найду - заменю"

Ievgenii
10.05.2018
12:00:33
мы же не в бесконечном цикле ждём
Ты используешь некий евент луп?

Если да - то ты в вечном цикле

Denis
10.05.2018
12:00:49
@chebotarevp использует в виде epool

он ведь отдаст управление

а pool его позавёт когда надо

Ievgenii
10.05.2018
12:01:11
ну так смотри

Если у него на входе есть не вычитанные данные, то epoll его будет возвращать все время, когда ты его вызываешь

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

Так что тут двуяко, нужно ли прекращать его мониторить или каждый раз просто скипать

Возможно просто скипать его реально дешевле

Denis
10.05.2018
12:02:50
а вот если данные ушли то это событие

(имхо)

Ievgenii
10.05.2018
12:03:02
Чем исключать, а потом назад ключать, в пул

Эм...

Denis
10.05.2018
12:04:51
epool триггерится же не каждый раз когда там данные, а когда событие сокета типа "данные отправлены" или "получены"

т.е. можно смело ждать - он не будет в холостую дергаться

Ievgenii
10.05.2018
12:05:36
Разве?

Google
Ievgenii
10.05.2018
12:05:46
Я напрямую с ним не работал, как правило через обвертки

Но считал, что он по другому работает

Denis
10.05.2018
12:08:56
ну мы же на события подписываемся там?

Ievgenii
10.05.2018
12:09:28
Смотри

В ЛибЕвент, в часности 2я версия, там идет работиа не с событиями

А с буферами стримов

Если ты регистрируешь сокет в нем как желаемый сокет для записи

То он вернет для него управление тогда, когда его входной буфер очистится

Если ты его регистрируешь как желаемый для чтения (или соединения)

То ему передадут управление, когда в его исходящий буфере изменит свое состояние - прийдут новые данные или новое соединение

Admin
ERROR: S client not available

Ievgenii
10.05.2018
12:12:17
НО! Если не вычитать все данные из сокета (не очистить его исходящий буфер), при следующей итерации он снова вернется (передастся ему управление)

Иными словами, если ты игнорируешь сокет (его буфер), то он будет возвращаться каждую итерацию

Это так работает LibEvent2

Но это простая надстройка над select, poll и epoll

Возможно он так их приводит к единому алгоритму

Напрямую с epoll я никогда не работал

Не знаю

Нужно попробовать

Igor
10.05.2018
12:19:32
epool триггерится же не каждый раз когда там данные, а когда событие сокета типа "данные отправлены" или "получены"
зависит от режила level-trigerred/edge-trigerred. чаще используют level-trigerred - пока не снимашь причину события epoll будет говорить что данные готовы

Google
Pavel
10.05.2018
12:20:15
И для этого ты и делал циклический, если я верно понял
На 99% я делал не только для этого, а чтобы просто максимально эффективно обрабатывать события на сокетах. Случай когда буфер целиком полон - действительно редкий, а вот случай когда мы прочитали данные в буфер а потом часть из них отправили - частый.

В буфере остается пустое место, и вот чтобы иметь возможность туда дописать новые данные - и нужен кольцевой буфер

Pavel
10.05.2018
12:21:44
Так весь процесс не завязывается на размер буфера. Сколько повезло - столько и прочитали/отправили. И не надо ждать полного опустошения/заполнения буфера.

Ievgenii
10.05.2018
12:21:45
И какой ты буфер делаешь обычно?

Pavel
10.05.2018
12:22:06
4кб стандартно

Ievgenii
10.05.2018
12:22:13
Ясно

Ну опубликуешь. Думаю всем будет интересно

Igor
10.05.2018
12:23:25
я кстати вчера вспомнил еще один поинт за отдельные буфера вне самого кольцевого буфера - если принятые данные нужно обрабатывать ты не можешь их перезаписать, следовательно выход - копировать куда-то что очень плохо

если у тебя приём данных в отдельные буфера то ты просто отдаёшь их на обработку.

Pavel
10.05.2018
12:23:50
Ага, ну в моем случае не нужно обрабатывать

Igor
10.05.2018
12:23:53
да

Pavel
10.05.2018
12:24:08
Ну опубликуешь. Думаю всем будет интересно
Я опубликовал по ссылке выше, но может пакет сделаю на выходных

Ievgenii
10.05.2018
12:29:46
А зачем ты читаешь в цикле?

for (ssize_t count; ; freeslice.length > 0) {

И пишешь в цикле

Не понимаю зачем

Если сам выше говорил о блокировках

Или это старый вариант?

Pavel
10.05.2018
12:35:06
Нет, я пишу пока пишется

Бывает так что можно записать в 2-3 итерации и это нормально

А вот когда сокет вернул EAGAIN это значит что он заполнен и нам пора прекратить процесс записи.

Ievgenii
10.05.2018
12:36:28
Да ладно!

Страница 560 из 719