Подключите два клиентских сокета

Скажем, Java имеет два вида сокетов:

  • сервер снабжает "ServerSocket" сокетом
  • клиентские сокеты или просто "Сокет"

Вообразите ситуацию двух процессов:

X = Клиент
Y = Сервер

Серверный процесс Y: имеет "ServerSocket", который слушает порт TCP
Клиентский процесс X: отправляет запрос на установление соединения через "Сокет" к Y.

Y: Затем accept() метод возвращает новый клиентский тип "Сокет",
когда это происходит, два Сокета "взаимосвязаны",

Так: сокет в клиентском процессе, соединен с сокетом в серверном процессе.
Затем: чтение/запись через сокет X похоже на чтение/запись через сокет Y.
Теперь, два Клиентских Сокета взаимосвязаны!!

Но...
Что, если я создаю два Клиентских сокета в том же процессе, и я хочу получить их "взаимосвязанный"?

... даже возможный?

Скажем, то, как иметь два клиентских сокета, взаимосвязано, не используя промежуточный ServerSocket?

Я решил его путем создания двух Потоков для того, чтобы непрерывно читать A и писать B и другой для чтения B и записи...
Но я думаю, мог быть лучший путь... (Те потоки мирового энергетического потребления не необходимы с подходом клиент-сервер),

Любая справка или совет ценились бы!!Спасибо


Править:

Пример приложения: "Существующее серверное приложение могло быть преобразовано в клиент один", Например, сервер VNC, клиентские подключения сокета к серверу VNC и другому клиентскому сокету создаются (для соединения со средним сервером), затем приложение взаимосвязывает два клиента, заканчивающиеся, сервер VNC является клиентским приложением! И затем, никакой общедоступный IP не необходим.

VNCServer---MyApp---> |middle сервер | <---Пользователь

13
задан Martijn Courteaux 13 June 2010 в 11:54
поделиться

11 ответов

ServerSocket позволяет вам прослушивать соединения на определенном порту. Когда серверный сокет принимает соединение, он порождает другой поток и перемещает соединение на другой порт, поэтому исходный порт все еще может прослушивать дополнительные соединения.

Клиент инициирует соединение через известный порт. Затем, как правило, клиент отправляет некоторый запрос, и сервер отвечает. Это будет повторяться до тех пор, пока связь не будет завершена. Это простой подход клиент / сервер, который использует Интернет.

Если вам не нужен этот механизм, и запросы могут приходить из любого сокета в любое время, тогда реализация потоков чтения и записи так, как вы это делаете, кажется подходящей.

Внутри они по-прежнему используют механизмы ожидания, поэтому вы не должны видеть большой загрузки ЦП, пока они ждут прибытия данных.

Я думаю, вам все еще нужен один конец, чтобы быть серверным сокетом, потому что я не думаю, что клиентский сокет может принимать соединение. ClientSocket подразумевает TCP, для которого требуется соединение. Если вы использовали DatagramSocket, что подразумевает UDP, у вас могло бы быть взаимодействие клиента с клиентом без соединения.

2
ответ дан 1 December 2019 в 20:56
поделиться

Зачем вам это нужно?

Если вы хотите иметь систему типа «одноранговая», то у вас просто должен каждый клиент запускать и клиентский, и серверный сокет - серверный сокет для приема подключений от других клиентов и клиентский сокет для установления подключений. другим.

ETA: Было не совсем понятно, о чем вы спрашивали в исходном вопросе, но после вашего редактирования похоже, что вы хотите создать своего рода прокси-сервер .

В вашем примере ваше приложение создаст два клиентских сокета, один из которых подключается к VNCServer, а другой - к «среднему серверу». Тогда «средний сервер» будет иметь два серверных сокета (один для подключения вашего приложения, а другой для подключения пользователя. Затем внутренне ему необходимо знать, как сопоставить эти сокеты и передавать данные между ними.

]
5
ответ дан 1 December 2019 в 20:56
поделиться

Вы пытаетесь создать фиктивный сокет? Если это так, издевательство над обеими сторонами трубы может быть немного сложнее, чем необходимо.

С другой стороны, если вы просто хотите создать канал данных между двумя потоками, вы можете использовать PipedInputStream и PipedOutputStream.

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

1
ответ дан 1 December 2019 в 20:56
поделиться

Зачем нам нужен промежуточный сервер? Если вы хотите просто выставить VNCServer. Почему бы не попробовать архитектуру вроде следующей

VNCServer(S) <-> (C)MyApp(S) <-> (C) User

(S) represents a server socket
(C) represents a client socket

В этом случае MyApp выступает и как клиент (для VNCServer), и как сервер (для User). Поэтому вам придется реализовать в MyApp как клиентские, так и серверные сокеты, а затем передавать данные.

Edit: Для связи с VNCServer, MyApp должен знать IP VNCServer. Пользователь будет общаться только с MyApp и ему нужно знать только IP-адрес MyApp. Пользователю не нужен ip-адрес VNCServer.

0
ответ дан 1 December 2019 в 20:56
поделиться

Сокет (с точки зрения сети) состоит из 2 конечных точек (клиентское и серверное приложение) и 2 потоков . Выходной поток клиента - это входной поток сервера и наоборот.

Теперь попытайтесь представить, что произойдет, если поток записывает много данных в поток, пока никто не читает ... Есть буферы, правда, но они не безграничны и могут различаться по размеру. В конце концов, ваш поток записи достигнет предела буфера и будет блокироваться до тех пор, пока кто-нибудь не освободит буфер.

Сказав это, теперь вы должны знать, что для этого потребуются по крайней мере два разных потока на поток: один, который записывает, и другой, который читает записанные байты.

Если ваш протокол является стилем запрос-ответ, вы можете использовать 2 потока на сокет, но не меньше.

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

interface MyCommunicator{
  public void send(MyObject object);
  public void addReader(MyReader reader);
}

interface MyReader{ //See Observer Pattern for more details
  public void received(MyObject object);
}

Таким образом, вы можете легко удалить всю сеть (включая кодирование и декодирование ваших объектов и т.д.) и минимизировать потоки.

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

Но в любом случае: многопоточность - это неплохо, если вы не злоупотребляете ею.

1
ответ дан 1 December 2019 в 20:56
поделиться

Прежде всего, не называйте принятый клиент (на стороне сервера) его сокет клиентским сокетом . Это очень сбивает с толку.

Скажем, как соединить два клиентских сокета без использования промежуточного ServerSocket?

Это невозможно. Всегда нужно делать серверную часть, которая может принимать клиентов. Теперь вопрос: какая сторона соединения должна быть серверной?
Приняв это решение, вы должны обдумать следующие моменты:

  • Сервер должен иметь статический общедоступный IP-адрес.
  • Сервер, который находится после подключения маршрутизатора, должен выполнить «переадресацию портов». (См. UPnP )
  • Клиент должен знать, к какому хосту он должен подключиться (общедоступный IP-адрес)

Средний сервер

Я не понимаю, что вы хотите делать с этим третьим сервер. Может быть, у вас есть публичный IP-адрес VNCServer? * Элистер * написал, вы хотите провести бригаду между клиентом и VNCServer. Я не вижу в этом пользы.

Почему бы сразу не установить соединение с VNCServer?

Но если вы действительно этого хотите, вы можете создать такую ​​ситуацию:


      /   VNCServer (Server Running)  <---.
     |                                     |
LAN -|                             Connects to VNCServer
     |                                     |
      \   MyApp (Server Running --> Accepts from Middle Server) <------.
                                                                        |
                                                            (Through a router)
                                                                        |
     Middle server (Server Running --> Accepts client) ---> Connects to Your App
                                             ^
                                             |
                                    (Through a router)
                                             |
     Client --> Connects to Middle Server --°

И вот как это выглядит без третьего сервера (что я рекомендую you):


      /   VNCServer (Server Running)  <---.
     |                                     |
LAN -|                             Connects to VNCServer
     |                                     |
      \   MyApp (Server Running --> Accepts Clients) <------.
                                                             |
                                                      (Through a router)
                                                             |
     Client --> Connects to MyApp --------------------------°


РЕДАКТИРОВАТЬ:

Я думаю, что теперь понял:

Мы должны визуализировать вашу ситуацию следующим образом:

                             Your Main Server (What you called middle server)
                    (1)         |       |      (2)
            /⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻/         \⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻\
           |                                                |
      Your VNCServer   <---------------------------->   The client
         (5)                        (3)

(1) VNCServer подключается к главному серверу.Итак, главный сервер получил IP-адрес VNCServer.
(2) Клиент подключается к главному серверу.
(3) Теперь главный сервер знает, где находятся сервер и клиент. Затем он отправляет клиенту информацию о том, где находится сервер. Затем клиент подключится к IP, полученному от главного сервера. Это, конечно, IP-адрес VNCServer.
(5) VNCServer работает - это сервер, принимающий клиента.

Теперь можно начать совместное использование рабочего стола.

Я думаю, что это самая рекомендуемая ситуация.
Конечно, писать это на Java - это вам.

17
ответ дан 1 December 2019 в 20:56
поделиться

В C вы можете вызвать socketpair (2) , чтобы получить пару подключенных сокетов, но я не уверен, есть ли в Java какой-либо встроенный способ выполнения то же самое.

0
ответ дан 1 December 2019 в 20:56
поделиться

Вообще говоря, клиентский TCP-сокет имеет два конца (локальный и "удаленный"), а серверный TCP-сокет имеет один конец (поскольку он ожидает подключения клиента). Когда клиент подключается к серверу, сервер внутренне порождает клиентский сокет для формирования соединенной пары клиентских сокетов, которые представляют канал связи; это пара, потому что каждый сокет просматривает канал с одного конца. Вот как работает TCP (на высоком уровне).

Вы не можете соединить два клиентских сокета друг с другом в TCP, поскольку низкоуровневый протокол соединения не работает таким образом. (Вы можете иметь соединенную пару сокетов, созданную таким образом в Unix, но в Java это не реализовано, и это не сокеты TCP). Что вы можете сделать, так это закрыть сокет сервера, как только вы приняли соединение; для простых случаев этого может быть достаточно.

Сокеты UDP, конечно, отличаются, но они работают с дейтаграммами, а не с потоками. Это совсем другая модель коммуникации.

0
ответ дан 1 December 2019 в 20:56
поделиться

Я понимаю, что вам нужно - мне приходилось решать ту же проблему в ситуациях, когда сервер находился за маскирующим брандмауэром с динамическим IP. Я использовал небольшую бесплатную программу javaProxy , чтобы найти решение. Это заставляет сервер выглядеть как клиентский сокет - внутри он по-прежнему является сервером, но javaProxy предоставляет программу пересылки - в примере My App - которая создает клиентские соединения «с» сервера. Он также предоставляет прокси-сервер посередине (в примере средний сервер) для соединения двух клиентских концов вместе - клиентского сокета, перенаправленного с сервера, и клиентского сокета от реального клиента, пытающегося подключиться к серверу.

Средний сервер размещается за пределами брандмауэра на известном IP-адресе. (Несмотря на то, что мы можем притвориться, что делаем это без серверных сокетов, каждое соединение должно включать в себя клиента и сервер, поэтому мы должны убедиться, что средний сервер находится на IP-адресе, доступном для клиентов.) В моем случае я просто использовал простой хостинг-провайдер, который позволил мне запускать java из оболочки.

С такой настройкой я мог предоставить доступ к удаленному рабочему столу и другим службам, работающим за брандмауэром NAT с динамическим IP, с доступом с моей домашней машины, которая также находилась за NAT с динамическим IP. Единственный IP-адрес, который мне нужно было знать, - это IP-адрес среднего сервера.

Что касается потоковой передачи, то библиотека javaproxy почти наверняка реализована с использованием потоков для перекачки данных между клиентскими сокетами, но они не потребляют никаких ресурсов ЦП (или мощности), пока они блокируют ожидание ввода-вывода.Когда java 7 выпускается с поддержкой асинхронного ввода-вывода, один поток на пару клиентских сокетов не нужен, но это больше касается производительности и избежания ограничений на максимальное количество потоков (пространство стека), а не энергопотребления.

Что касается реализации этого самостоятельно с двумя клиентскими сокетами в одном процессе, требуется использование потоков, если Java зависит от блокировки ввода-вывода. Модель извлекается из конца чтения и проталкивается в конец записи, поэтому для извлечения из конца чтения требуется поток. (Если бы у нас был толчок со стороны чтения, то есть асинхронный ввод-вывод, тогда не понадобился бы отдельный поток для каждой пары сокетов.)

1
ответ дан 1 December 2019 в 20:56
поделиться

Классический подход Java к обмену данными через сокеты на основе подключения состоит в том, чтобы настроить ServerSocket на известном IP-адресе и порте и заблокировать его вызов приема, который (после успешного подключения попытка) возвращает новый сокет с портом, определенным реализацией (отличным от порта ServerSocket ). Обычно возвращенный сокет передается обработчику, реализующему Runnable . Обработчики временно связаны с определенным подключением. Обработчики могут использоваться повторно и обычно связаны с конкретным потоком на время существования соединения. Блокирующая природа классического ввода-вывода сокетов Java очень затрудняет соединение двух сокетов, обслуживаемых одним потоком.

Однако возможно, и это не является необычным, обрабатывать как входные, так и выходные потоки сокета в одном потоке, а поддержка одного соединения за раз позволяет отказаться от требования Runnable , т. Е.для обработчика не требуется дополнительный поток, и вызов ServerSocket accept откладывается до закрытия текущего соединения.

Фактически, если вы используете NIO , вы можете легко обрабатывать множество соединений одновременно в одном потоке с помощью механизма выбора. Это одна из наиболее важных особенностей NIO , неблокирующий ввод-вывод для отделения потоков от соединений (позволяющий обрабатывать очень большое количество соединений небольшими пулами потоков).

Что касается топологии вашей системы, извините, я еще не понял, что вам нужно, но это похоже на работу либо для службы NAT , либо для какого-то прокси-сервера, соединяющего общедоступный IP-адрес на частный IP-адрес.

-1
ответ дан 1 December 2019 в 20:56
поделиться

Если вам нужно одноранговое соединение, вы можете рассмотреть возможность использования UDP.

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

Надеюсь, это помогло.

0
ответ дан 1 December 2019 в 20:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: