Как повторно инициализировать буфер пакета? - PullRequest
1 голос
/ 27 ноября 2011

У меня возникли проблемы при отправке пакетов UDP от netcat или моего клиента на сервер UDP, который прослушивает широковещательные пакеты UDP.Проблема в том, что я не могу переинициализировать буфер socket.receive(packet);. Когда вы проверите вывод моей консоли, вы увидите, что пакеты отправляются или принимаются дважды или даже чаще , и самое неприятное из всех, что когда я сначала отправляю пакет большей длины, следующий, меньший, является частью предыдущего !( Проблемы отмечены ЗДЕСЬ на выходе консоли ) Мой клиент и сервер расположены в одной локальной сети.

Код клиента:

DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(true);
byte[] buf = ("Hello from Client").getBytes();
byte[] buf2 = ("omg").getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, getBroadcastAddress(UDPConnection.context), Server.SERVERPORT);
DatagramPacket packet2 = new DatagramPacket(buf2, buf2.length, getBroadcastAddress(UDPConnection.context), Server.SERVERPORT);
Log.d("UDP", "C: Sending: '" + new String(buf) + "'");
socket.send(packet);
socket.send(packet2);

Код сервера:

void run(){
MulticastSocket socket = new MulticastSocket(SERVERPORT);
socket.setBroadcast(true);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
while(true){
    Log.d("UDP", "S: Receiving...");
    socket.receive(packet);
    //socket.setReceiveBufferSize(buf.length);
    packet.setData(buf);
    Log.i("BUFFER_packet",packet.getLength()+"");
    Log.i("BUFFER_socket",socket.getReceiveBufferSize()+"");
    Log.d("UDP", "S: From: " + packet.getAddress().getHostAddress());
    Log.d("UDP", "S: Received: "+getRidOfAnnoyingChar(packet));
    Log.d("UDP", "S: Done.");
}
}
    //this method is getting rid of the "questionmark in a black diamond" character
    public String getRidOfAnnoyingChar(DatagramPacket packet){
        Log.i("UDP","Inside getridofannoyingchar method.");
        String result = new String(packet.getData());
        char[] annoyingchar = new char[1];
        char[] charresult = result.toCharArray();
        result = "";
        for(int i=0;i<charresult.length;i++){
            if(charresult[i]==annoyingchar[0]){
                break;
            }
            result+=charresult[i];
        }
        return result;
    }

Консоль:

11-27 18:15:27.515: D/UDP(15242): S: Connecting...
11-27 18:15:27.519: I/ServerIP(15242): ::
11-27 18:15:27.519: I/LocalIP(15242): 192.168.0.4
11-27 18:15:27.523: D/UDP(15242): S: Receiving...
11-27 18:15:28.031: D/UDP(15242): C: Connecting...
11-27 18:15:28.039: I/BroadcastIP(15242): 192.168.0.255
11-27 18:15:28.042: I/BroadcastIP(15242): 192.168.0.255
11-27 18:15:28.070: D/UDP(15242): C: Sending: 'Hello from Client'
11-27 18:15:28.074: I/BUFFER_packet(15242): 1024
11-27 18:15:28.074: I/BUFFER_socket(15242): 110592
11-27 18:15:28.074: D/UDP(15242): S: From: 192.168.0.4
11-27 18:15:28.074: I/UDP(15242): Inside getridofannoyingchar method.
11-27 18:15:28.078: I/BUFFER_packet(15242): 1024
11-27 18:15:28.078: I/BUFFER_socket(15242): 110592
11-27 18:15:28.078: D/UDP(15242): S: From: 192.168.0.4
11-27 18:15:28.078: I/UDP(15242): Inside getridofannoyingchar method.
11-27 18:15:28.085: D/UDP(15242): S: Received: Hello from Client <------------HERE
11-27 18:15:28.085: D/UDP(15242): S: Done.
11-27 18:15:28.085: D/UDP(15242): S: Receiving...
11-27 18:15:28.085: D/UDP(15242): S: Received: Hello from Client <------------HERE
11-27 18:15:28.085: D/UDP(15242): S: Done.
11-27 18:15:28.085: D/UDP(15242): S: Receiving...
11-27 18:15:28.085: I/BUFFER_packet(15242): 1024
11-27 18:15:28.085: I/BUFFER_socket(15242): 110592
11-27 18:15:28.085: D/UDP(15242): S: From: 192.168.0.4
11-27 18:15:28.085: I/UDP(15242): Inside getridofannoyingchar method.
11-27 18:15:28.089: D/UDP(15242): S: Received: omglo from Client <------------HERE
11-27 18:15:28.089: D/UDP(15242): S: Done.
11-27 18:15:28.089: D/UDP(15242): S: Receiving...
11-27 18:15:28.089: I/BUFFER_packet(15242): 1024
11-27 18:15:28.089: I/BUFFER_socket(15242): 110592
11-27 18:15:28.089: D/UDP(15242): S: From: 192.168.0.4
11-27 18:15:28.089: I/UDP(15242): Inside getridofannoyingchar method.
11-27 18:15:28.089: D/UDP(15242): S: Received: omglo from Client <------------HERE
11-27 18:15:28.089: D/UDP(15242): S: Done.
11-27 18:15:28.089: D/UDP(15242): S: Receiving...
11-27 18:15:28.089: D/UDP(15242): C: Sent.
11-27 18:15:28.089: D/UDP(15242): C: Done.

Любая помощь будет высоко оценена!:)

PS.в консоли могут быть некоторые выходные данные, такие как «Готово / Отправлено / Соединение / Прием», которые не добавлены в мой пример кода, но присутствуют все полученные: / BUFFER_packet / _socket / From.

1 Ответ

5 голосов
/ 28 ноября 2011

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

Вызов:

Arrays.fill (buf, (byte) 0);

на стороне сервера сбросит массив в ноль, поскольку массивы в Java передаются по ссылке, а не передаются-by-value (т. е. имеющаяся у вас ссылка на содержимое массива такая же, как и ссылка на DatagramPacket, поэтому вы можете изменить ее, не используя методы DatagramPacket).

Сказав, что высериализация / десериализация данных не идеальна.Было бы лучше использовать ByteArrayOutputStream и ByteArrayInputStream, обернутые вокруг буфера отправки и получения, а затем DataOutputStream / DataInputStream, обернутый вокруг них.Это позволит вам писать и читать строку в четко определенном формате, который, вероятно, будет хранить длину строки, так что любые оставшиеся данные в буфере будут игнорироваться в любом случае.Правильная сериализация / десериализация таким способом также устранит необходимость удаления символа «черный бриллиант».

DataOutputStream.writeUTF

Если вы заинтересованы в рассужденияхза этим стоит использование сериализации по умолчанию для java.lang.String (getBytes () и new String (byte [])), а также того, как заполняется пакет UDP.Я попытаюсь свести его к ключевым моментам:

Внутреннее представление Java для объектов String не является байтовым массивом - это массив символов.Символы Java не совпадают с байтами - один символ на самом деле составляет 2 байта, потому что он должен иметь возможность представлять больше, чем просто латинский алфавит (acbd ...), он должен поддерживать другие символы из других языков / культур, такие как вещи изКириллица, кандзи и т. Д. И одного байта недостаточно (один байт дает вам 256 возможностей, 2 байта - 65536).

В результате при вызове getBytes () Java должна использовать некоторую «схему» (кодирование), чтобы превратить этот массив символов в массив байтов (сериализация).Детали этого не имеют большого значения, но когда вы отправляете первый кусок байтов (скажем, длиной 10 байтов), вы читаете пакет в гораздо больший буфер (1024 байта).Затем вы просите Java String десериализовать весь этот буфер, а не только 10 байтов.

Схема (кодирование) не знает, что имеет дело только с первыми 10 байтами, поэтому она пытается декодировать все 1024 байта,и затем вы получите странные символы в вашей строке, такие как черный ромб, или (где вы поместили некоторые другие данные после 10 байтов, отправив «привет»), вы получите символы из предыдущего приема, смешанные в вашей строке.

Использование write / readUTF запишет длину байтового массива, а также данные, поэтому при повторном чтении он узнает, что он должен прочитать только первые 10 символов (или сколько их допустимо).

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