Страницы

Поиск по вопросам

среда, 3 апреля 2019 г.

Пакеты склеиваются

public class NetCon extends Thread {
String address = "127.0.0.1"; int port = 3458;
private DataInputStream in; private DataOutputStream out;
public ArrayList queue;
public Boolean Connect() { try { InetAddress ipAddress = InetAddress.getByName(address); Socket socket = new Socket(ipAddress, port);
InputStream sin = socket.getInputStream(); OutputStream sout = socket.getOutputStream();
in = new DataInputStream(sin); out = new DataOutputStream(sout);
queue = new ArrayList(); } catch (Exception e) { return false; } return true; }
public void SendText(String s) { try { out.write(s.getBytes("UTF-8")); out.flush(); } catch(Exception e) { System.out.println(e); } }
@Override public void run() { for(int i = 0; i < queue.size(); i++) { //System.out.println(queue.get(i)); //если убрать коммент. то ок SendText(queue.get(i)); } }
} В queue находится две строки "test1", "test2". Запускаю поток, идёт отправка на сервер. Проблема в том, что иногда получаю 2 пакета как и положено, первый пакет со строкой "test1", второй с "test2", а иногда проскакивает что получаю пакет "test1test2". Если перед отправкой на сервер выводить на экран, то получается маленькая задержка и работает тогда всегда правильно. Почему между пакетами нужна эта пауза? Как сделать нормально? Вот фрагмент кода сервера C# (TcpClient) NetworkStream ns = client.GetStream(); byte[] buffer = new byte[1024];
int count; while ((count = ns.Read(buffer, 0, buffer.Length)) > 0) { Console.WriteLine(BitConverter.ToString(buffer, 0, count)); Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, count)); ns.Flush(); }


Ответ

Скорость сокета - непредсказуемая штука. Бывает, что пакет пришел ещё не весь, а программа уже читает буфер - пакет получен не весь, а следующий искажен. Бывает, что программа не дернула буфер сокета вовремя, и пришел ещё один или больше пакетов. Программа дергает буфер - и получает склеившиеся пакеты. Всё дело в устройстве TCP/IP, и вытекающем из него устройстве сокета и его буфера. Сетевые пакеты ни как не обозначают свою длину, если програмист не позаботился об этом. В сокет приходит байт, и он сразу помещается в конец своего буфера. Не имеет значения, пришел один байт или миллион. Когда программа вызывает метод чтения, если не указана читаемая длина - возвращается весь буфер в обратном порядке, а если указана - то байты от нулевого до указанного в обратном порядке. Таким образом программист сам обязан обозначивать размер или диапазон байтов в пакете. Решений несколько. От самых неправильных к наиболее подходящему: 1) Фиксировать размер пакета. Встречал несколько раз в чужом коде. Наличествует ещё и свой буфер, в который до нужного заполнения сливают данные буфера сокета, и читают нужными порциями. Стоит ли говорить, что вещь ужасно хардкодная? 2) В пакете означают его конец, к примеру каким-нибудь сочетанием 00-FF-00-FF. Встречал 2 раза. Велосипедизм. Возможны ложные срабатывания детектора конца пакета. 3) Первыми двумя (или четырьмя, если пакет планируется длинным) байтам обозначают либо размер всего пакета, либо размер "полезной части. Программа в цикле ждет 2 байта в буфере сокета, и читает их. Далее программа ждет появления N байтов или больше в сокете, где N - цифра, прочтенная из первых двух байтов. Далее программа читает N байт в свой буфер, и обрабатывает их. Цикл входит в новую итерацию. Помогать конкретным кодом не стану, если автор не попросит этого прямо. Я пропагандирую самостоятельное развитие ума с возможными подсказками. Добавлено: Вспомнил, что встречал ещё одну разновидность велосипедизма, похожую на ответ ниже. В первых 4 байтах передавался идентификатор пакета (обычный int), он передавался в обработчик пакетов, где свитчем выбирался нужный, создавался экземпляр этого пакета, в конструкторе читалось нужное количество байтов, и пакет обрабатывался. Самое смешное, что такой велосипед присутствует в одном, довольно известном и, пожалуй, знаменитом, приложении. Сообщить название продукта и производителя не могу по причине NDA. В принципе, такой метод вполне жизнеспособен, и, возможно, полностью пропитан духом ООП, но мне он кажется через чур странным... Ведь одио из основных негласных принципов ООП является отсутствие хардкода, а в таком решении размер пакета захардкожен.

Комментариев нет:

Отправить комментарий