Я использую SocketChannel
и Selector
для записи сервера. Задача серверов - позволить клиентам подключаться, читать сообщения от клиентов и писать сообщения клиентам.
У меня возникают трудности с различиями между сообщениями, отправленными от клиента, и другими данными, которые отключают инструкцию чтения.
ех. Когда клиент подключается, я заметил, что инструкция чтения выполнена без отправки клиентом каких-либо данных. Это важно, потому что после того, как сервер прочитал сообщение от клиента, он должен добавить это сообщение в очередь сообщений. Это сообщение будет удалено и обработано из очереди внешним приложением. Проблема в том, что каждый раз, когда происходит чтение, эти еще UFM (неопознанные сообщения) нарушают внешнее приложение при попытке их декодирования.
Прошу прощения, если на этот вопрос уже был дан ответ. Я не нашел полного ответа.
Вот мой метод accept, который, если я не ошибаюсь, сообщает селектору уведомлять нас, когда данные доступны для чтения.
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
// Accept the connection and make it non-blocking
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(this.selector, SelectionKey.OP_READ);
}
Вот метод выбора, который ждет события.
while (should_run) {
try {
//Load all pending operations to key
while (!operations.isEmpty()) {
SelectionKey key = client.keyFor(getSelector());
if (!key.isValid()) {
operations.remove();
throw new Exception("Key not valid");
}
key.interestOps(operations.remove());
}
selector.select();
Iterator selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = (SelectionKey) selectedKeys.next();
//Check if key is valid
if (!key.isValid()) {
throw new Exception("Key not valid");
}
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
} else if (key.isWritable()) {
write(key);
}
selectedKeys.remove();
}
} catch (Exception e) {
System.err.println("Something went wrong: " + e.getMessage());
e.printStackTrace();
}
}
Вот функция чтения, называемая if key.isReadable()
private void read(SelectionKey key) {
System.out.println("Read fired");
//Clear the buffer for new Data
this.readBuffer.clear();
int count;
try {
count = this.client.read(this.readBuffer);
if (count == -1) {
throw new IOException("Socket closed");
}
} catch (IOException e) {
key.cancel();
return;
}
//PROBLEM OCCURRING HERE
this.worker.give(this.readBuffer.array(), count);
}
Чтение должно читать сообщения и передавать сообщение в рабочий поток, где сообщение декодируется, и с ним происходят другие приятные вещи.
Метод read вызывается после каждого подключения нового клиента. Счет обычно мал, между 4 и 10, и когда я декодирую с использованием new String(data[], "UTF-8")
, он оказывается японским или что-то в этом роде.
Я проверил это, просто распечатывая count
каждый раз, когда вызывается чтение.
Эту проблему можно решить, просто проверив размер каждого входящего сообщения и игнорируя те, которые являются малыми. Но похоже, что это может иметь неприятные последствия, если происходит фрагментация.
EDIT: Код примера клиента:
Socket socket = new Socket("localhost", 3000);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
while (true) {
String input = JOptionPane.showInputDialog("Send something to the server");
if (input == null)
break;
oos.writeObject(input);
}
ПРИМЕЧАНИЕ. У меня нет исходного кода для однорангового узла, но этот пример VERY SIMPLE дает точные результаты. Прежде чем MessageDialog даже предложит отправить сообщение на сервер, сообщение будет получено сервером, либо японским, либо рядом вопросительных знаков (??????).
и другие данные, которые отключают инструкцию чтения.
Других данных нет. Только данные от клиента.
'new ObjectOutputStream()' записывает заголовок потока, который начинается с 0xAC. Это то, что вы читаете.
Поскольку peer использует потоки объектов, вы должны использовать блокирующие сокеты, потоки и потоки ввода объектов. Не NIO.
Член 'this.client' должен быть удален в соответствии с моими комментариями выше. Предполагается, что у вас есть только один клиент.
Закрытие канала отменяет ключ. Вы редко когда-либо нуждаетесь в key.cancel(). Я никогда не использую его.
key.isReadable()
имеет значениеtrue
и вызывает функцию, которую я использую для чтения изSocketChannel