Скажем, Java имеет два вида сокетов:
Вообразите ситуацию двух процессов:
X = Клиент
Y = Сервер
Серверный процесс Y: имеет "ServerSocket", который слушает порт TCP
Клиентский процесс X: отправляет запрос на установление соединения через "Сокет" к Y.
Y: Затем accept()
метод возвращает новый клиентский тип "Сокет",
когда это происходит, два Сокета "взаимосвязаны",
Так: сокет в клиентском процессе, соединен с сокетом в серверном процессе.
Затем: чтение/запись через сокет X похоже на чтение/запись через сокет Y.
Теперь, два Клиентских Сокета взаимосвязаны!!
Но...
Что, если я создаю два Клиентских сокета в том же процессе, и я хочу получить их "взаимосвязанный"?
... даже возможный?
Скажем, то, как иметь два клиентских сокета, взаимосвязано, не используя промежуточный ServerSocket?
Я решил его путем создания двух Потоков для того, чтобы непрерывно читать A и писать B и другой для чтения B и записи...
Но я думаю, мог быть лучший путь... (Те потоки мирового энергетического потребления не необходимы с подходом клиент-сервер),
Любая справка или совет ценились бы!!Спасибо
Править:
Пример приложения: "Существующее серверное приложение могло быть преобразовано в клиент один", Например, сервер VNC, клиентские подключения сокета к серверу VNC и другому клиентскому сокету создаются (для соединения со средним сервером), затем приложение взаимосвязывает два клиента, заканчивающиеся, сервер VNC является клиентским приложением! И затем, никакой общедоступный IP не необходим.
VNCServer---MyApp---> |middle сервер | <---Пользователь
ServerSocket позволяет вам прослушивать соединения на определенном порту. Когда серверный сокет принимает соединение, он порождает другой поток и перемещает соединение на другой порт, поэтому исходный порт все еще может прослушивать дополнительные соединения.
Клиент инициирует соединение через известный порт. Затем, как правило, клиент отправляет некоторый запрос, и сервер отвечает. Это будет повторяться до тех пор, пока связь не будет завершена. Это простой подход клиент / сервер, который использует Интернет.
Если вам не нужен этот механизм, и запросы могут приходить из любого сокета в любое время, тогда реализация потоков чтения и записи так, как вы это делаете, кажется подходящей.
Внутри они по-прежнему используют механизмы ожидания, поэтому вы не должны видеть большой загрузки ЦП, пока они ждут прибытия данных.
Я думаю, вам все еще нужен один конец, чтобы быть серверным сокетом, потому что я не думаю, что клиентский сокет может принимать соединение. ClientSocket подразумевает TCP, для которого требуется соединение. Если вы использовали DatagramSocket, что подразумевает UDP, у вас могло бы быть взаимодействие клиента с клиентом без соединения.
Зачем вам это нужно?
Если вы хотите иметь систему типа «одноранговая», то у вас просто должен каждый клиент запускать и клиентский, и серверный сокет - серверный сокет для приема подключений от других клиентов и клиентский сокет для установления подключений. другим.
ETA: Было не совсем понятно, о чем вы спрашивали в исходном вопросе, но после вашего редактирования похоже, что вы хотите создать своего рода прокси-сервер .
В вашем примере ваше приложение создаст два клиентских сокета, один из которых подключается к VNCServer, а другой - к «среднему серверу». Тогда «средний сервер» будет иметь два серверных сокета (один для подключения вашего приложения, а другой для подключения пользователя. Затем внутренне ему необходимо знать, как сопоставить эти сокеты и передавать данные между ними.
]Вы пытаетесь создать фиктивный сокет? Если это так, издевательство над обеими сторонами трубы может быть немного сложнее, чем необходимо.
С другой стороны, если вы просто хотите создать канал данных между двумя потоками, вы можете использовать PipedInputStream и PipedOutputStream.
Однако, не имея дополнительной информации о том, чего вы пытаетесь достичь, я не могу сказать вам, подходит ли какой-либо из этих вариантов или лучше другое.
Зачем нам нужен промежуточный сервер? Если вы хотите просто выставить 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.
Сокет
(с точки зрения сети) состоит из 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);
}
Таким образом, вы можете легко удалить всю сеть (включая кодирование и декодирование ваших объектов и т.д.) и минимизировать потоки.
Если вам нужны двоичные данные,вместо этого вы можете использовать каналы или реализовать свои собственные потоки, чтобы предотвратить многопоточность. Бизнес-логика или логика обработки не должны знать о сокетах, потоки достаточно низкоуровневые и, возможно, слишком много.
Но в любом случае: многопоточность - это неплохо, если вы не злоупотребляете ею.
Прежде всего, не называйте принятый клиент (на стороне сервера) его сокет клиентским сокетом
. Это очень сбивает с толку.
Скажем, как соединить два клиентских сокета без использования промежуточного ServerSocket?
Это невозможно. Всегда нужно делать серверную часть, которая может принимать клиентов. Теперь вопрос: какая сторона соединения должна быть серверной?
Приняв это решение, вы должны обдумать следующие моменты:
Средний сервер
Почему бы сразу не установить соединение с 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 - это вам.
В C вы можете вызвать socketpair (2) , чтобы получить пару подключенных сокетов, но я не уверен, есть ли в Java какой-либо встроенный способ выполнения то же самое.
Вообще говоря, клиентский TCP-сокет имеет два конца (локальный и "удаленный"), а серверный TCP-сокет имеет один конец (поскольку он ожидает подключения клиента). Когда клиент подключается к серверу, сервер внутренне порождает клиентский сокет для формирования соединенной пары клиентских сокетов, которые представляют канал связи; это пара, потому что каждый сокет просматривает канал с одного конца. Вот как работает TCP (на высоком уровне).
Вы не можете соединить два клиентских сокета друг с другом в TCP, поскольку низкоуровневый протокол соединения не работает таким образом. (Вы можете иметь соединенную пару сокетов, созданную таким образом в Unix, но в Java это не реализовано, и это не сокеты TCP). Что вы можете сделать, так это закрыть сокет сервера, как только вы приняли соединение; для простых случаев этого может быть достаточно.
Сокеты UDP, конечно, отличаются, но они работают с дейтаграммами, а не с потоками. Это совсем другая модель коммуникации.
Я понимаю, что вам нужно - мне приходилось решать ту же проблему в ситуациях, когда сервер находился за маскирующим брандмауэром с динамическим IP. Я использовал небольшую бесплатную программу javaProxy , чтобы найти решение. Это заставляет сервер выглядеть как клиентский сокет - внутри он по-прежнему является сервером, но javaProxy предоставляет программу пересылки - в примере My App - которая создает клиентские соединения «с» сервера. Он также предоставляет прокси-сервер посередине (в примере средний сервер) для соединения двух клиентских концов вместе - клиентского сокета, перенаправленного с сервера, и клиентского сокета от реального клиента, пытающегося подключиться к серверу.
Средний сервер размещается за пределами брандмауэра на известном IP-адресе. (Несмотря на то, что мы можем притвориться, что делаем это без серверных сокетов, каждое соединение должно включать в себя клиента и сервер, поэтому мы должны убедиться, что средний сервер находится на IP-адресе, доступном для клиентов.) В моем случае я просто использовал простой хостинг-провайдер, который позволил мне запускать java из оболочки.
С такой настройкой я мог предоставить доступ к удаленному рабочему столу и другим службам, работающим за брандмауэром NAT с динамическим IP, с доступом с моей домашней машины, которая также находилась за NAT с динамическим IP. Единственный IP-адрес, который мне нужно было знать, - это IP-адрес среднего сервера.
Что касается потоковой передачи, то библиотека javaproxy почти наверняка реализована с использованием потоков для перекачки данных между клиентскими сокетами, но они не потребляют никаких ресурсов ЦП (или мощности), пока они блокируют ожидание ввода-вывода.Когда java 7 выпускается с поддержкой асинхронного ввода-вывода, один поток на пару клиентских сокетов не нужен, но это больше касается производительности и избежания ограничений на максимальное количество потоков (пространство стека), а не энергопотребления.
Что касается реализации этого самостоятельно с двумя клиентскими сокетами в одном процессе, требуется использование потоков, если Java зависит от блокировки ввода-вывода. Модель извлекается из конца чтения и проталкивается в конец записи, поэтому для извлечения из конца чтения требуется поток. (Если бы у нас был толчок со стороны чтения, то есть асинхронный ввод-вывод, тогда не понадобился бы отдельный поток для каждой пары сокетов.)
Классический подход Java к обмену данными через сокеты на основе подключения состоит в том, чтобы настроить ServerSocket на известном IP-адресе и порте и заблокировать его вызов приема, который (после успешного подключения попытка) возвращает новый сокет с портом, определенным реализацией (отличным от порта ServerSocket ). Обычно возвращенный сокет передается обработчику, реализующему Runnable . Обработчики временно связаны с определенным подключением. Обработчики могут использоваться повторно и обычно связаны с конкретным потоком на время существования соединения. Блокирующая природа классического ввода-вывода сокетов Java очень затрудняет соединение двух сокетов, обслуживаемых одним потоком.
Однако возможно, и это не является необычным, обрабатывать как входные, так и выходные потоки сокета в одном потоке, а поддержка одного соединения за раз позволяет отказаться от требования Runnable , т. Е.для обработчика не требуется дополнительный поток, и вызов ServerSocket accept откладывается до закрытия текущего соединения.
Фактически, если вы используете NIO , вы можете легко обрабатывать множество соединений одновременно в одном потоке с помощью механизма выбора. Это одна из наиболее важных особенностей NIO , неблокирующий ввод-вывод для отделения потоков от соединений (позволяющий обрабатывать очень большое количество соединений небольшими пулами потоков).
Что касается топологии вашей системы, извините, я еще не понял, что вам нужно, но это похоже на работу либо для службы NAT , либо для какого-то прокси-сервера, соединяющего общедоступный IP-адрес на частный IP-адрес.
Если вам нужно одноранговое
соединение, вы можете рассмотреть возможность использования UDP
.
UDP
может получать от чего угодно без предварительного установления соединения, однако вам все равно понадобится сервер, чтобы сообщать клиентам, от кого они получают данные.
Надеюсь, это помогло.