Java NIO - проблемы с получением данных с помощью SocketChannels - PullRequest
0 голосов
/ 19 апреля 2011


Я пишу простой мессенджер с использованием Java NIO. Он работает нормально, за исключением того, что клиент не получает сообщения с контактами, которые уже вошли в систему.
Это основной метод процесса сервера:

public void process() throws IOException, ClassNotFoundException{
    while (true){
        selector.select();
        Set keys = selector.selectedKeys();
        Iterator it = keys.iterator();
        while (it.hasNext()){
            SelectionKey key = (SelectionKey)it.next();
            it.remove();
            if (key.isAcceptable()){
                /*configuring incoming channel to be non-blocking 
                and adding it to the clients set*/
            }
            if (key.isReadable()) {
                SocketChannel client = (SocketChannel)key.channel();
                /*Message class contains its type, source user name and data.
                getMessage() method reads message from SocketChannel*/
                Message m = getMessage(client);    
                switch (m.getType()) {
                    case LOGIN_REQUESTED:
                        /*Accept or reject the client log in name
                        in case of accepting add user with its status
                        to Map users*/
                        break;
                    case CONTACTS_REQUESTED:
                        /*Here is the problem, client gets only one message
                        sendMessage() writes the buffer with serialized 
                        message to clients channels*/
                        for (String name : users.keySet()) {
                            sendMessage(client, MessageType.CONTACTS_REQUESTED, 
                                name, users.get(name).toString());
                        }
                        break;
                   //Other messages are processing
                }
            }
        }
    }
}

Метод клиента, который обрабатывает входящие сообщения:

private void processIncomingMessages() 
    throws IOException, ClassNotFoundException {
    ByteArrayInputStream bais;
    ObjectInputStream ois;
    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
    while (true){
        selector.select();
        Set keys = selector.selectedKeys();
        Iterator it = keys.iterator();
        while (it.hasNext()){
            SelectionKey key = (SelectionKey)it.next();
            it.remove();
            if (key.isReadable()){
                SocketChannel sc = (SocketChannel)key.channel();
                buffer.clear();
                if (sc.read(buffer) != -1){
                    buffer.flip();
                    bais = new ByteArrayInputStream(buffer.array());
                    ois = new ObjectInputStream(bais);
                    Message m = (Message)ois.readObject();
                    /*My castom event serves to notify GUI to update 
                    its contact list. In case of sending CONTACTS_REQUESTED 
                    messages, it gets only one (first?) of them*/
                    fireNetworkEvent(m);
                }
            }
        }
    }
}

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

1 Ответ

1 голос
/ 19 апреля 2011

Я думаю, что в клиенте ...

 if (sc.read(buffer) != -1){

Не гарантируется чтение всего вашего сообщения, оно может ничего не читать, некоторые из них или несколько сообщений.

Сокеты - это потоки байтов, и если вы хотите прочитать пакет (как вы это делаете), вам нужно упаковать поток.

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

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

...