public class NetCon extends Thread {
String address = "127.0.0.1";
int port = 3458;
private DataInputStream in;
private DataOutputStream out;
public ArrayList
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
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. В принципе, такой метод вполне жизнеспособен, и, возможно, полностью пропитан духом ООП, но мне он кажется через чур странным... Ведь одио из основных негласных принципов ООП является отсутствие хардкода, а в таком решении размер пакета захардкожен.
Комментариев нет:
Отправить комментарий