bufio.Reader зависает

55 views
Skip to first unread message

Дмитрий Фролов

unread,
Aug 25, 2019, 2:39:00 PM8/25/19
to Golang Russian
Упрощенный пример функции клиента, который подключается к серверу и ждет команды:

func connectCom() {
 
for {
 c
, err := net.DialTCP(tcpProtocol, nil, connectAddr)
 buf
:= bufio.NewReader(c)
 
for {
 msg
, err := buf.Peek(readWriterSize)
 
//msg, _, err := buf.ReadLine() так тоже не работает
 
if err != nil {
 
break // io.EOF - соединение прервано со стороны сервера
 
}
 
...
 
}
 time
.Sleep(10 * time.Second)
 
}
}
Если серверную часть останавливаю в отладчике, или прибиваю уже скомпилированный и запущенный файл на линуксе (kill pid), клиент на строке 
msg, err := buf.Peek(readWriterSize
возвращает ошибку, далее снова пытается подключиться.
Обнаружил нюанс при перезагрузке сервера, клиент не видит ошибки и висит бесконечно, соответственно не переподключается при старте серверной части.

Degtyarev Evgeny

unread,
Aug 25, 2019, 11:21:37 PM8/25/19
to Golang Russian
понедельник, 26 августа 2019 г., 1:39:00 UTC+7 пользователь Дмитрий Фролов написал:
Обнаружил нюанс при перезагрузке сервера, клиент не видит ошибки и висит бесконечно, соответственно не переподключается при старте серверной части.

io.EOF ты получишь если сервер нормально закрыл коннект и соединение было живо на этот момент. Хочешь достоверно узнать жив коннект или нет - запиши в него что-нибудь.

Из своего опыта - как вариант, вводить в протокол команду ping, на которую сервер должен сразу отвечать. С нужным интервалом слать пинг в пищущей горутине, в читающей горутине читать с таймаутом, таймаут меньше чем интервал отправки пинг. Если чтение завершилоь по таймауту - коннект мертв.

Ща гуру подтянутся, может поизящнее варианты подскажут.

Kvark

unread,
Aug 26, 2019, 2:08:51 AM8/26/19
to gola...@googlegroups.com
26.08.2019 Degtyarev Evgeny пишет:
> Из своего опыта - как вариант, вводить в протокол команду ping, на
> которую сервер должен сразу отвечать. С нужным интервалом слать пинг в
> пищущей горутине, в читающей горутине читать с таймаутом, таймаут меньше
> чем интервал отправки пинг. Если чтение завершилоь по таймауту - коннект
> мертв.
Серверу не надо отвечать на ping-сообщения. Запись завершится успешно
только если другая сторона сможет выполнить чтение.
Ещё можно задействовать механизм TCP keepalive, переложив заботы о
поддержании коннекта на ОС:
https://habr.com/ru/company/intersystems/blog/155565/

Daniel Podolsky

unread,
Aug 26, 2019, 2:51:42 AM8/26/19
to gola...@googlegroups.com
На самом деле - нет :(

Единственный вариант точно знать, что соединение живо - это слать ping и дожидаться pong

--
Вы получили это сообщение, поскольку подписаны на группу Golang Russian.

Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Просмотреть это обсуждение в Сети можно по адресу https://groups.google.com/d/msgid/golang-ru/cf14a4c8-a5d3-843e-d570-3a395c6e918a%40gmail.com.

Aln Kapa

unread,
Aug 26, 2019, 2:53:22 AM8/26/19
to gola...@googlegroups.com
Добрый день.

А какой смысл  в buf := bufio.NewReader(c) почему нельзя читать просто из сокета?

вс, 25 авг. 2019 г. в 21:39, Дмитрий Фролов <frolov.d...@gmail.com>:
--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/7dac91be-65a6-4e57-9407-b51768a058b6%40googlegroups.com.

Никита Лебедев

unread,
Aug 26, 2019, 3:15:02 AM8/26/19
to gola...@googlegroups.com
А можно ли повесить timeout на read? Тогда точно будем знать что удаленный сервер упал

пн, 26 авг. 2019 г., 9:53 Aln Kapa <aln...@gmail.com>:
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/CAJqqVEVzhgM4UXFDXXJnyeDt3migq6sGvh9E5Pesx_RM2df7Gg%40mail.gmail.com.

Daniel Podolsky

unread,
Aug 26, 2019, 4:37:14 AM8/26/19
to gola...@googlegroups.com
> А можно ли повесить timeout на read? Тогда точно будем знать что удаленный сервер упал

только если мы точно знаем, что удаленный сервер что-то шлет периодически.

Kvark

unread,
Aug 26, 2019, 7:02:36 AM8/26/19
to gola...@googlegroups.com
26.08.2019 Daniel Podolsky пишет:
> На самом деле - нет :(
> Единственный вариант точно знать, что соединение живо - это слать ping и
> дожидаться pong
А в каких случаях ping может быть успешно передан зависшей стороне
(например при выдернутом кабеле)? Разве на уровне tcp не предусмотрено
подтверждение получения передаваемых данных? Спасибо.

Daniel Podolsky

unread,
Aug 26, 2019, 7:30:31 AM8/26/19
to gola...@googlegroups.com
> А в каких случаях ping может быть успешно передан зависшей стороне
> (например при выдернутом кабеле)? Разве на уровне tcp не предусмотрено
> подтверждение получения передаваемых данных? Спасибо.

передан - ни в каких

а вот отправлен - все чаще.

особенно на мобильном интернете - сотовые вышки ленятся отвечать
"таких тут не проживает", и сокет зависает навсегда

почему не помогает tcp keepalive - я не разбирался, но факт остается
фактом: я вижу, как уходят пакеты в сторону давно отключенного
смартфона, я вижу, как уходят keepalive пакеты, я не вижу никаких
ответов, но сокет продолжает висеть и ошибок записи на нем нет.
часами...

но и без сотового инета все чаще ттакая ситуация, на nat

Degtyarev Evgeny

unread,
Aug 26, 2019, 8:04:59 AM8/26/19
to Golang Russian
но и без сотового инета все чаще ттакая ситуация, на nat
на vpn то же самое

Дмитрий Фролов

unread,
Aug 26, 2019, 9:46:27 AM8/26/19
to Golang Russian
Я пример где в интернете нарыл и по нему сделал, про другой вариант не задумывался даже.

понедельник, 26 августа 2019 г., 9:53:22 UTC+3 пользователь Aln Kapa написал:
Добрый день.

А какой смысл  в buf := bufio.NewReader(c) почему нельзя читать просто из сокета?

вс, 25 авг. 2019 г. в 21:39, Дмитрий Фролов <frolov....@gmail.com>:
Упрощенный пример функции клиента, который подключается к серверу и ждет команды:

func connectCom() {
 
for {
 c
, err := net.DialTCP(tcpProtocol, nil, connectAddr)
 buf
:= bufio.NewReader(c)
 
for {
 msg
, err := buf.Peek(readWriterSize)
 
//msg, _, err := buf.ReadLine() так тоже не работает
 
if err != nil {
 
break // io.EOF - соединение прервано со стороны сервера
 
}
 
...
 
}
 time
.Sleep(10 * time.Second)
 
}
}
Если серверную часть останавливаю в отладчике, или прибиваю уже скомпилированный и запущенный файл на линуксе (kill pid), клиент на строке 
msg, err := buf.Peek(readWriterSize
возвращает ошибку, далее снова пытается подключиться.
Обнаружил нюанс при перезагрузке сервера, клиент не видит ошибки и висит бесконечно, соответственно не переподключается при старте серверной части.

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес gola...@googlegroups.com.

Degtyarev Evgeny

unread,
Aug 26, 2019, 9:38:22 PM8/26/19
to Golang Russian
разные варианты обрыва соединения и как на это реагирует система клиента и сервера

Дмитрий Фролов

unread,
Aug 28, 2019, 12:42:52 PM8/28/19
to Golang Russian

вторник, 27 августа 2019 г., 4:38:22 UTC+3 пользователь Degtyarev Evgeny написал:
разные варианты обрыва соединения и как на это реагирует система клиента и сервера
Не думал что так все замороченно. Делал ручками проблемы связи, выдергивал провод из компа (даже на полчаса) , перезагружал роутер, buf.Peek(readWriterSize) не видела никакой ошибки и потом (при восстановлении связи) как ни в чем не бывало спокойно получала данные.
Проблему с перезагрузкой сервера решил горутиной:
func testConnect(c *net.TCPConn) {
   
for {
        _
, err := c.Write([]byte("up\n"))
       
if err != nil {
           
return
       
}
        time
.Sleep(60 * time.Second)
   
}
}
добавил ее запуск после buf := bufio.NewReader(c)
При перезагрузке сервера начинает срабатывать условие:
Reply all
Reply to author
Forward
0 new messages