Metalkia за TCP сокет за манекени
За TCP сокет за манекени
Много опростена история на контакта на TCP за тези, които не са в темата :)
Аз седнах и написах вътрешна документация за вашия проект. И това е необходимо, наред с други неща, описва прилагането на контакт от страна на клиента. Самият аз направих това изпълнение в Java за конкретен клиент, извършване на функционални и стрес сървъра тестване. Нужда е друга изпълнение на .NET приложения за единството, и това ще бъде истински клиент на моя сървър. И това осъзнаване ще пиша друг разработчик.
Така че аз написах за вашия Java гнездо, и осъзнах, че би било хубаво да се каже, на първо място, как работи TCP сокет. И разбрах, че тази история може да се изложи на публично място, защото това не е специфичен вътрешни документи. Е, това е разпространил :)
Как гнездото на ниско ниво? Ние говорим за TCP пълен дуплекс гнездо, без никакви добавки, като HTTP. Пълен дуплекс - двете тръби. Една тръба потока от данни от клиента към сървъра. Според друга тръба поток от сървъра към клиента. Те тече в малки опаковки, като например 1500 байта (в зависимост от конфигурацията на мрежата).
Тръби работят независимо един от друг. Какво тече през един, не пречи, която тече през другата.
И да се работи с него, че е необходимо да се реши два проблема.
Проблемът за извличане на данни от контакта
Ето един клиент, който се изпраща на сървъра, всеки един детайл от данни. Всичко може да се побере в един пакет. И това не може да се побере, могат да бъдат разделени в няколко пакета. TCP гарантира, че всички тези пакети достигнат, и да достигне в правилния ред. Но сървърът е по някакъв начин трябва да знаят как от тези пакети отново да се съберат солидна част от данните.
Нека да си представим, че произволно клиентът изпраща заявка към:
В момента не докосва темата за данни сериализация, те се предават във всякакъв формат. Да предположим, че имаме един обект, като е описано в езика на програмиране, където пишем част на клиента. И този обект по някакъв начин да се серийни номера за масив от байтове. Така например, в поредица форма, тя ще изглежда така:
Да предположим, че голям обект и масив от байтове, за да получите повече. В един пакет, той не се получи, е била разделена и отиде на тръбата под формата на 3 опаковки:
Следователно, сървърът чете първата част на тръбата. И правя с него? Можете да опитате да десериализиране. Ако получите съобщение за грешка, то ще бъде ясно, че данните не са пълни, и ние трябва да получат повече. И така всеки път, когато нещо излиза от тръбата, ще се опитаме да го десериализиране. Оказа се, - е, интерпретират и да изпратите по-нататъшна обработка. То не е работа - в очакване на повече данни.
Но тогава Лошото е, че допълнителните опити за десериализиране ще създаде допълнителна тежест върху процесора. Имам нужда от друга опция.
В Erlang gen_tcp модул предлага различни варианти за решаване на този проблем. Нека да използваме това, което вече е там. Например, там е изпълнение, при което сървърът приема, че всеки клиент има глава заявка. А заглавието съдържа дължината на данни, които съставляват искането.
Това означава, че заявката изглежда така:
И разделен на пакети по този начин:
Когато сървърът идва към сървъра 42 гласи заглавието, тя вижда като дължината на заявки - 42 байта, и разбира, че трябва да се изчака, докато те идват, тези 42 байта. А след това можете да десериализиране данни и тълкуване. Например, тълкуването може да се окаже, че сървърът изисква при метод за влизане с аргументи "Боб" и "123". По същия начин, той също ще извлече данните и на клиента, когато той ще ги получите от сървъра.
размер глава може да бъде 1 или 2 или 4 байта. Такива варианти предлага gen_tcp. Когато се използва в активен режим. (И в пасивен режим, ние се извлече и интерпретира този удар с глава, така че да правите каквото искате).
Какъв е размерът на заглавната част е по-добре? В един байт се побере под номер 2 ^ 8 = 256. След това искане не може да бъде по-голяма от 256 байта. Това е твърде малко. В 2 байта вписват номера 2 ^ 16 = 65536. Следователно искане може да бъде до 65536 байта. Това е достатъчно за повечето случаи.
Но, например, може да се наложи да се изпрати към сървъра големи заявки, така че 2 байта на титлата няма да бъдат достатъчни. Тук имам нужда от него, и аз взех глава на 4 байта.
Вземете нещо взе, но бях задушаване жаба :) Тези искания ще бъде малко по-голям. По принцип, всички искания са малки, но все пак те ще използват глава 4 байта. Налице е основание за оптимизация. Например, можете да използвате двете заглавия. На първо място, еднобайтовата, ще покаже продължителността на втория. Втори, 1-4 байт ще покаже дължината на пакет :) Или е възможно да се използват безразмерна вътр, заема байтове 1-4, както е направено в AMF сериализация. можете да спестите трафик, ако желаете.
Разбира се, такива дребни оптимизация смея само тези, които използват HTTP :) За HTTP не е дреболия, и всяка молба, изпратена болнав опаковка от метаданни, това не е правилния сървър, а защото прахосване на движението по скалата не е сравним с моя чист гнездо TCP :)
Проблемът на съвпадение запитвания и отговори
Тук клиентът е направил искането, а малко по-късно от другата тръба да е дошла нещо. Какъв е отговорът на последната заявка? Или отговор на някои по-рано заявка? Или няма отговор, и активното информация тласък по инициатива на сървъра? Клиентът трябва някак си знам какво да правя.
Един добър вариант - всяка заявка на клиента трябва да има уникален идентификатор. Отговор от сървъра ще има същия идентификатор. Така че можете да се определи коя е дошло искането отговора.
Като цяло, ние се нуждаем от три версии на взаимодействието на клиент-сървър:
- Клиентът изпраща заявка към сървъра и искате да получите отговор
- Клиентът изпраща заявка към сървъра и не се нуждае от отговор
- Сървърът активно избутва данни на клиента
(Всъщност, има четвърти вариант, сървърът активно изпраща заявка на клиент и искате да получите отговор. Но аз съм такъв вариант никога не е било необходимо, и аз не го осъзнават).
В първия случай ние добавяме искането за ID:
И ние получите отговор:
Във втория случай, ние не добавите към идентификатора на заявка: