Использование java-сокета из кода JNI / C ++ - PullRequest
10 голосов
/ 05 февраля 2010

У меня есть Java-приложение, которое создает сокет для взаимодействия с процессом сервера, например, новый java.net.Socket (String host, int port).Это приложение включает в себя кучу устаревшего кода на C ++, который должен собирать большие объемы данных с этого сервера и обрабатывать его.В настоящее время это реализуется благодаря тому, что собственный код создает собственный сокет и подключается к серверу, например:

sock = socket(AF_INET, SOCK_STREAM, 0);
struct hostent* hp = gethostbyname(host);
if (!hp)
{
  unsigned long addr = inet_addr(host);
  hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
}

struct sockaddr_in name;
name.sin_family = AF_INET;
memcpy(&name.sin_addr, hp->h_addr, hp->h_length);
name.sin_port = htons(port);

connect(sock, (sockaddr*)&name, sizeof(name));

На компьютерах Windows Vista / 7 с несколькими сетевыми картами (например, проводные и Wi-Fi или VPN-подключения) этидва сокета могут иметь разные локальные адреса.Похоже, что Java-код выбирает «лучший» интерфейс (проводной Gb enet = выше MTU?), Нативный (наивный?) Код получает интерфейс «по умолчанию» (вставьте в USB-устройство Wi-Fi, и он станет вашим по умолчанию - гадость).

Это вызывает у меня некоторые проблемы, я не думаю, что детали актуальны.Два вопроса:

  1. Возможно ли мне повторно использовать java-сокет из кода JNI (переносимо? Предположим, Sun JDK).Это позволит полностью избежать этой проблемы, но пока я не вижу способа взаимодействия с java.net.Socket из JNI / нативного кода.

  2. Поскольку ответ наПервый вопрос, вероятно, НЕТ, как Java создает этот сокет (выбирая интерфейс)?Фрагменты кода приветствуются.Я просмотрел материал openjdk и не нашел того, что искал.

Спасибо, Крис

Ответы [ 5 ]

10 голосов
/ 06 февраля 2010

Чтобы ответить на ваш первый вопрос: если возможно повторно использовать сокет Java из нативного кода - да, это возможно, но я бы не рекомендовал это (вы привязали бы себя к внутренним компонентам конкретной реализации / версии); но если вы действительно должны: используйте отражение, чтобы получить доступ к java.io.FileDescriptor на java.net.SocketImpl, тогда используйте sun.misc. Метод get в JavaIOFileDescriptorAccess для получения собственного дескриптора сокета. Оформить заказ DualStackPlainSocketImpl.java )

Чтобы ответить на ваш второй вопрос: каков алгоритм Java для поиска интерфейса по умолчанию в Windows - проверьте метод getDefaultIPv6Interface в net_util_md.c (не позволяйте v6 обмануть вас - я полагаю, он используется для v4 также).

Я бы посоветовал вам открыть и использовать сокет либо из кода C (JNI), либо из кода Java, желательно более позднего, поскольку вы обнаружите, что очистка и обработка ошибок лучше всего обрабатывать в коде, который управляет разъем. Идея открытия сокета в Java и передачи байтовых буферов из C (JNI) совершенно нормальна, и вы не должны найти никаких проблем с кучей при разумных размерах буфера и правильном освобождении в коде JNI.

Представьте себе серверы приложений Java, которые обрабатывают огромные объемы данных без помех.

1 голос
/ 09 февраля 2010

Остерегайтесь решений, которые ориентированы на специфику реализации JVM, они могут сломаться в будущем или с ВМ других производителей. Есть способ сделать это с помощью java.nio API. Существуют методы для связи с каналами из собственного кода без копирования буферов в / из кучи Java.

Основной идеей будет создание java.nio.SocketChannel в вашем Java-коде для открытия соединения. Затем в C ++ используйте NewDirectByteBuffer для создания экземпляра java.nio.ByteBuffer, который можно передать в методы read / write экземпляра канала.

Посмотрите подробности JNI, представленные в версии 1.4 Java 2 SDK и Новые API ввода-вывода .

1 голос
/ 05 февраля 2010

Для вашего первого вопроса, если у вас есть код объекта java.net.Socket в вашем коде JNI, вы можете вызывать методы для него, чтобы вы могли читать и записывать данные через сокет.

0 голосов
/ 15 февраля 2010

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

0 голосов
/ 05 февраля 2010

Ваш второй вопрос-

Вы всегда можете «привязать» к нужному локальному интерфейсу (для этого нужен только IP-адрес)

public void bind (адрес SocketAddress) throws SocketException связывает этот DatagramSocket с определенным адресом & портом

...