Как мне создать постоянные TCPSockets? - PullRequest
1 голос
/ 28 мая 2011

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

require 'socket'
require 'thread'
connections = []
server = TCPServer.new(9998)
loop do
  Thread.start(server.accept) do |client|
    client.print 'Once'
    client.print 'Upon a time.'
  end #eo Thread
end #eo infinte loop

Клиент:

require 'socket'
client = TCPSocket.new('localhost', 9998)
2.times { print client.read }
client.close

Клиент «останавливается» до тех пор, пока я не выключу сервер, и только после этого распечатывает сообщения. Я знаю, что добавление client.close к серверу исправит приостановку, но я не хочу закрывать сокет.

Я знаю, что некоторые приложения повторно используют TCPSocket. Таким образом, это может быть Client > Server, Server > Client, Client > Server x2 и так далее. Я уверен, что есть способ сделать это в Ruby; Я просто не могу понять, как.

Итак, мой список вопросов:

  1. Как создать постоянное соединение с сервером <-> клиента, как описано выше?
  2. Есть ли лучший способ сохранить сервер открытым без использования loop?
  3. Почему он так приостановлен без client.close на сервере? Сервер буферизует сообщения перед отправкой?

Ответы [ 2 ]

4 голосов
/ 28 мая 2011
  1. Как создать постоянное соединение с сервером <-> Client, как описано выше.

У вас есть несколько вариантов: вы можете написать многопоточноесервер, где один «процесс» с большей частью совместно используемой памяти выполняет несколько потоков выполнения, и каждый поток имеет некоторое локальное хранилище потока, например, client.Или вы могли бы написать многопроцессорный сервер, где каждое клиентское соединение получает свой собственный независимый процесс через fork(2) (что в значительной степени означает, что оно работает только на Unix, так как Windows пытается реализовать fork(2) никогда не работает хорошо).Или вы можете написать конечный автомат на основе цикла событий и использовать select(2) или poll(2) или epoll(4) для управления тем, какие клиенты доступны для чтения, записи и имеют ошибки.

Выбор между ними может показатьсясложно, но есть рекомендации .Я хотел бы предположить, что -small- системы, где ожидается, что у вас будет только 20-30 одновременных клиентов, могут быть очень хорошо обработаны через многопоточные или разветвленные серверы.Если вы хотите выйти за пределы этого числа одновременных клиентов, вы должны использовать такой инструмент, как EventMachine или libevent , чтобы написать свой сервер.(Который может быть, а может и не быть проще поддерживать.)

2) Есть ли лучший способ сохранить сервер открытым без использования цикла [потому что я чувствую, что совершил убийство, создаваябесконечный цикл]

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

3) Почему он так приостановлен без client.close на сервере?Сервер буферизует сообщения перед отправкой?

Ах, здесь - вопрос, который вам действительно нужен - TCP попытается буферизовать результатынескольких пакетов с сервера, и из-за потенциальной необходимости фрагментации слишком больших пакетов в середине маршрута вы, как клиент, не имеете никаких гарантий о сохранении любых границ дейтаграмм.

Скорее всего, очень хорошо, что стек TCP / IP вашего сервера немного подождал, прежде чем отправлять первый пакет, чтобы узнать, скоро ли поступит больше данных.(Часто так и бывает.) Вероятно, он объединил два вызова client.print() в один пакет TCP.Ваш клиент вызывает client.read() дважды , но, вероятно, данных достаточно только для чтения one .Если вы хотите, чтобы ваш поток TCP отправлял первый пакет немедленно , вы можете установить параметр сокета TCP_NODELAY, чтобы отключить эту небольшую задержку.(Но это может не помочь всем само по себе; вам может понадобиться использовать его вместе с TCP_CORK, чтобы попытаться «заморозить» все записи, пока вы не очистите их явно. Это, вероятно, ужасная идея.)

Лучшим вариантом было бы переписать ваш клиент, чтобы он знал о протоколе между вашим сервером и клиентом;ему нужно прочитать входные данные в буфер, проанализировать буфер, чтобы найти «полные сообщения», использовать эти сообщения, а затем вернуться в свой основной цикл обработки событий.

Возможно, ваш протокол предназначен для чтения / записи ASCII или UTF-8 оканчивается "\n" символами;в этом случае вы можете изменить print на puts или добавить \n в нужных местах и ​​изменить read() на readline().(Это позволит стандартной библиотеке ввода-вывода обрабатывать ваш протокол для вас.) Другой подход заключается в отправке данных с длинами + пары данных и ожидании правильной длины данных.

1 голос
/ 28 мая 2011

Используйте вместо этого путы.И используйте socket.flush, если хотите, чтобы он был уверен.В противном случае он буферизует данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...