You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Протокол за изпращане на datagrams (header-payload структури) в мрежата.
Connectionless протокол. Не гарантира доставянето на пакетите. Не гарантира реда на получаване на пакетите. (Best-effort delivery).
Проверка за грешки се прави само за хедърите.
Не разбира от портове.
Рутирането/маршрутизиране е негово задължение (с помощта на други протоколи.)
Изпращате данни към адрес 35.129.109.24 без да се интересувате какъв е маршрутът до този адрес.
IPv4 (32 бита) и IPv6 (128 бита) версии.
Никога не го имплементирате вие.
IP (Internet Protocol)
Всеки хост в мрежата има IP адрес.
Да си свързан с мрежата означава да имаш IP адрес и да можеш да достигаш до други хостове чрез техния IP адрес.
Единственото нещо, което IP прави, е да доставя datagrams на някой хост.
➜ ~ traceroute -I elixir-lang.bg # Показва някой възможен път до крайната дестинация
traceroute to elixir-lang.bg (165.227.142.119), 64 hops max, 72 byte packets
1 192.168.100.1 (192.168.100.1) 10.702 ms 3.244 ms 2.937 ms
2 78-83-64-2.spectrumnet.bg (78.83.64.2) 7.807 ms 4.186 ms 4.599 ms
3 ***
4 * de-cix.spnet.net (80.81.192.229) 30.842 ms 30.122 ms
5 fra2-edge1.digitalocean.com (80.81.195.151) 32.423 ms 31.958 ms 32.689 ms
6 ***
7 ***
8 ***
9 ***
10 themeddle.com (165.227.142.119) 34.735 ms 30.794 ms 30.474 ms
➜ ~ traceroute -I elixir-lang.bg # С пуснат VPN. Познайте държавата
traceroute to elixir-lang.bg (165.227.142.119), 64 hops max, 72 byte packets
1 10.5.0.1 (10.5.0.1) 294.600 ms 301.037 ms 290.308 ms
2 86.48.12.253 (86.48.12.253) 290.676 ms 290.853 ms 348.011 ms
3 vl203.tyo-eq8-core-1.cdn77.com (185.229.188.146) 291.204 ms 290.459 ms 291.060 ms
4 vl250.tyo-eq8-core-2.cdn77.com (138.199.0.217) 298.274 ms 311.895 ms 348.898 ms
5 63-217-103-9.static.pccwglobal.net (63.217.103.9) 307.997 ms 306.715 ms 308.902 ms
6 ae-16.a00.tokyjp09.jp.bb.gin.ntt.net (129.250.9.101) 304.970 ms 301.934 ms 307.026 ms
7 ae-1.r31.tokyjp05.jp.bb.gin.ntt.net (129.250.7.39) 294.160 ms 290.743 ms 290.470 ms
8 ae-4.r25.lsanca07.us.bb.gin.ntt.net (129.250.3.193) 402.408 ms 394.059 ms *
9 ***
10 ae-7.r21.londen12.uk.bb.gin.ntt.net (129.250.2.110) 602.453 ms 511.247 ms 512.267 ms
11 ae-3.a02.londen14.uk.bb.gin.ntt.net (129.250.3.191) 529.022 ms 614.286 ms 513.600 ms
12 ae-0.a03.londen14.uk.bb.gin.ntt.net (129.250.4.251) 617.506 ms 613.340 ms 512.530 ms
13 62.73.179.138 (62.73.179.138) 613.974 ms 517.075 ms 607.136 ms
14 138.197.244.91 (138.197.244.91) 613.742 ms 583.656 ms 614.136 ms
15 ***
16 ***
17 ***
18 ***
19 themeddle.com (165.227.142.119) 586.333 ms 614.011 ms 614.794 ms
Port
Elixir Port != Networking Port.
Използва се от протоколите на транспортния слой.
16 битово число, което участва в идентификацията на connection endpoint.
Само един процес може да слуша на даден адрес:порт по едно време.
Често може да забравите в някой друг таб да се изпълнява някоя програма, която заема порта.
Ако това се случи, ще видите грешка :eaddrinuse
Грешки, ако портът вече се използва
** (Mix) Could not start application pento: Pento.Application.start(:normal, []) returned an error: shutdown: failed to start child: PentoWeb.Endpoint
** (EXIT) shutdown: failed to start child: {:ranch_listener_sup, PentoWeb.Endpoint.HTTP}
** (EXIT) shutdown: failed to start child: :ranch_acceptors_sup
** (EXIT) {:listen_error, PentoWeb.Endpoint.HTTP, :eaddrinuse}
** (Mix) Could not start application presentem: Presentem.Application.start(:normal, []) returned an error: shutdown: failed to start child: PresentemWeb.Endpoint
** (EXIT) shutdown: failed to start child: {:ranch_listener_sup, PresentemWeb.Endpoint.HTTP}
** (EXIT) an exception was raised:
** (ArgumentError) errors were found at the given arguments:
* 2nd argument: not a key that exists in the table
(stdlib 4.3) :ets.lookup_element(:ranch_server, {:addr, PresentemWeb.Endpoint.HTTP}, 2)
Кой слуша на порт - решение
~ lsof -nP -i4TCP:4000 | grep LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
beam.smp 82109 ivan 51u IPv4 0x767589897efaeaed 0t0 TCP 127.0.0.1:4000 (LISTEN)
# kill 82109 # Убиваме процеса, за да освободим порта# Други команди с подобен ефект# lsof -i -P -n | grep LISTEN# netstat -tulpn | grep LISTEN# ss -tulpn | grep LISTEN# lsof -i:22# nmap -sTU -O IP-address-Here
Транспортен протокол. (Почти винаги) използва IP за преноса на данните.
Също като IP не гарантира доставяне на пакетите или техния ред.
Надгражда с няколко функционалности:
Port - IP не знае какво е порт.
(По желание) Проверка с чексума на данните.
Полезен е, когато данните са нужни в реално време и е безполезно да се изпращат наново на по-късен етап (Пример: First Person Shooter игри, Video streaming)
Транспортен протокол. (Почти винаги) използва IP за преноса на данните.
Connection-based. Една връзка се определя от четворката (source ip, source port, destination ip, destination port).
Позволява да се адресират отделни процеси на сървъра.
Добавя функционалност за:
Connection - Чрез handshake процес се установява постоянна връзка.
Наредба - Голямо съобщение може да се разбие в много IP пакети, които могат да бъат получени в разбъркан ред. TCP ги подрежда в правилния ред.
Надежност - Ако съдържанието на някое съобщение е повредено, TCP се грижи то да бъде изпратено отново. Ако някое съобщение не се получи, то се изпраща отново.
Никога не го имплементирате вие.
DNS (Domain Name System)
Не е протокол.
Дистрибутирана и йерархична система за имена.
Отговаря на въпроси като "Какъв е IP адресът на google.com?"
Използва UDP и/или TCP за комуникация.
Може да използва допълнителни протоколи, ако има нужда от сигурност.
Служи като като крайна точка за получаване и изпращане на данни.
Ако работите с API-та от по-ниско ниво, ще работите директно със socket.
Ще говорим повече за сокети на лекцията за LiveView.
Сървърът изпраща данни като "пише" в socket и получава данните като "чете" от socket
Low-level networking
defmoduleKVServerdorequireLoggerdefaccept(port)do{:ok,socket}=:gen_tcp.listen(port,[:binary,packet: :line,active: false,reuseaddr: true])Logger.info("Accepting connections on port #{port}")loop_acceptor(socket)enddefploop_acceptor(socket)do{:ok,client}=:gen_tcp.accept(socket)serve(client)loop_acceptor(socket)enddefpserve(socket)dosocket|>read_line()|>write_line(socket)serve(socket)enddefpread_line(socket)do{:ok,data}=:gen_tcp.recv(socket,0)dataenddefpwrite_line(line,socket)do:gen_tcp.send(socket,line)endend
KVServer.accept(4001)
➜ telnet localhost 4001
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello
Hello
Bye
Bye
Socket Server
Не е приятно да се пише ръчно accept/listen/bind/send/recv/etc.
Решение: Socker server/TCP acceptor pool библиотеки
Когато сървърът иска да изпраща съобщения на клиента без клиентът да е направил заявка.
Нотификации, чат, Publish/subscribe системи, високо интерактивни системи и др.
Възможни решения:
Клиентът също така да е сървър.
Long Polling - Сървърът не отговаря и не затваря връзката, а чака докато данните са готови. Когато клиентът получи данни, веднага изпраща заявка, която чака. Трябва reconnect след всяко съобщение.
Keep Alive - Изпращане на много заявки/отговори в една TCP връзка.
WebSocket
Не е Socket.
Протокол, предоставящ full-duplex комуникация над TCP.
Клиентът и сървърът могат да си изпращат съобщения взаимно в една връзка.
Използва портовете на HTTP(S): 80 и 443.
За изграждане на връзка се използва HTTP Upgrade хедър, за да се смени комуникацията от HTTP към WebSocket.
URL и URI
URI - Universal Resource Identifier. Суперсет на URN и URL
Макро, което се използва за изграждане на pipeline (converters)
plugPlug.MethodOverrideplugPlug.HeadplugPlug.Session,@session_optionsplugPentoWeb.RouterplugPlug.Loggerplug:matchplug:dispatch# Ако не използваме метапрогрмиране, то горното е почти еквивалентно на:conn|>Plug.MethodOverride.call()|>Plug.Head.call()|>Plug.Session.call(@session_options)|>PentoWeb.Router.call()|>match()|>dispatch()
Грешки и Early send
plug/{1,2} добавя механизми за обработка на грешки (Plug.ErrorHandler, Plug.Exception)
Всеки plug може да извика halt и да изпрати резултат
Ако някой plug направи това, останалите след него не се изпълняват.
Пример: Ако AuthenticataionPlug установи, че паролата е сгрешена, то не трябва да продължаваме нататък.
Plug.Router
Router е стандартен термин в уеб програмирането.
Рутерът определя коя част от кода ще обработи една заявка.
Рутирането става по path(!!!) частта от URL.
query получаваме като параметър на функцията, която ще обработи заявката.
defmoduleMyPlugTestdouseExUnit.Case,async: trueusePlug.Test@optsMyRouter.init([])test"returns hello world"do# Create a test connectionconn=conn(:get,"/hello")# Invoke the plugconn=MyRouter.call(conn,@opts)# Assert the response and statusassertconn.state==:sentassertconn.status==200assertconn.resp_body=="world"endend