Interfejs API gniazd jest de facto standardem komunikacji TCP / IP i UDP / IP (czyli znanym nam kodem sieciowym). Jednak jedna z jego podstawowych funkcji accept()
jest nieco magiczna.
Aby pożyczyć definicję półformalną:
accept () jest używana po stronie serwera. Akceptuje odebraną próbę utworzenia nowego połączenia TCP od klienta zdalnego i tworzy nowe gniazdo skojarzone z parą adresów gniazda tego połączenia.
Innymi słowy, accept
zwraca nowe gniazdo, przez które serwer może komunikować się z nowo podłączonym klientem. Stare gniazdo (na którym accept
zostało wywołane) pozostaje otwarte, na tym samym porcie, nasłuchując nowych połączeń.
Jak to accept
działa? Jak to jest realizowane? W tym temacie jest wiele nieporozumień. Wiele osób twierdzi, że akceptacja otwiera nowy port i za jego pośrednictwem komunikujesz się z klientem. Ale to oczywiście nieprawda, ponieważ żaden nowy port nie jest otwierany. W rzeczywistości możesz komunikować się przez ten sam port z różnymi klientami, ale jak? Kiedy kilka wątków łączy się recv
z tym samym portem, skąd dane mają wiedzieć, gdzie iść?
Wydaje mi się, że jest to coś w rodzaju adresu klienta związanego z deskryptorem gniazda, a ilekroć dane przechodzą recv
, są kierowane do właściwego gniazda, ale nie jestem pewien.
Byłoby wspaniale uzyskać dokładne wyjaśnienie wewnętrznego działania tego mechanizmu.
źródło
Odpowiedzi:
Twoje zamieszanie polega na myśleniu, że gniazdo jest identyfikowane przez adres IP serwera: port serwera. W rzeczywistości gniazda są jednoznacznie identyfikowane przez kwartet informacji:
Client IP : Client Port
iServer IP : Server Port
Tak więc podczas gdy adres IP serwera i port serwera są stałe we wszystkich akceptowanych połączeniach, informacje po stronie klienta pozwalają mu śledzić, dokąd wszystko zmierza.
Przykład wyjaśnienia rzeczy:
Powiedzmy, że mamy serwer pod adresem
192.168.1.1:80
i dwóch klientów10.0.0.1
i10.0.0.2
.10.0.0.1
otwiera połączenie na porcie lokalnym1234
i łączy się z serwerem. Teraz serwer ma jedno gniazdo zidentyfikowane w następujący sposób:Teraz
10.0.0.2
otwiera połączenie na porcie lokalnym5678
i łączy się z serwerem. Teraz serwer ma dwa gniazda zidentyfikowane w następujący sposób:źródło
Wystarczy dodać do odpowiedzi udzielonej przez użytkownika „17 z 26”
Gniazdo w rzeczywistości składa się z 5 krotek - (źródłowy adres IP, źródłowy port, docelowy adres IP, docelowy port, protokół). Tutaj protokół może TCP lub UDP lub dowolny protokół warstwy transportowej. Ten protokół jest identyfikowany w pakiecie w polu „protokół” w datagramie IP.
W ten sposób możliwe jest, aby różne aplikacje na serwerze komunikowały się z tym samym klientem na dokładnie tych samych 4-krotkach, ale różniących się w polu protokołu. Na przykład
Apache po stronie serwera rozmawia przez (server1.com:880-client1:1234 na TCP) i World of Warcraft rozmawia przez (server1.com:880-client1:1234 na UDP)
Zarówno klient, jak i serwer będą obsługiwać to, ponieważ pole protokołu w pakiecie IP w obu przypadkach jest różne, nawet jeśli wszystkie pozostałe 4 pola są takie same.
źródło
To, co mnie zdezorientowało, kiedy się tego uczyłem, to fakt, że terminy
socket
iport
sugerują, że są czymś fizycznym, podczas gdy w rzeczywistości są to tylko struktury danych, których jądro używa do abstrakcji szczegółów sieci.W związku z tym struktury danych są zaimplementowane w celu oddzielenia połączeń od różnych klientów. Jeśli chodzi o sposób ich implementacji, odpowiedź brzmi: a.) Nie ma znaczenia, celem API gniazd jest właśnie to, że implementacja nie powinna mieć znaczenia lub b.) Po prostu spójrz. Oprócz wysoce polecanych książek Stevensa zawierających szczegółowy opis jednej implementacji, sprawdź źródła w Linuksie lub Solarisie lub jednym z BSD.
źródło
Jak powiedział drugi facet, gniazdo jest jednoznacznie identyfikowane przez 4 krotki (IP klienta, port klienta, adres IP serwera, port serwera).
Proces serwera działający na serwerze IP utrzymuje bazę danych (co oznacza, że nie obchodzi mnie, jakiego rodzaju tabeli / listy / drzewa / tablicy / magicznej struktury danych używa) aktywnych gniazd i nasłuchuje na porcie serwera. Kiedy otrzyma wiadomość (za pośrednictwem stosu TCP / IP serwera), sprawdza IP klienta i port w bazie danych. Jeśli adres IP klienta i port klienta zostaną znalezione we wpisie bazy danych, wiadomość jest przekazywana do istniejącej procedury obsługi, w przeciwnym razie zostanie utworzony nowy wpis bazy danych i zostanie utworzony nowy program obsługi do obsługi tego gniazda.
We wczesnych dniach ARPAnet, niektóre protokoły (jeden z nich to FTP) nasłuchiwały na określonym porcie żądań połączenia i odpowiadały portem przekazywania. Dalsza komunikacja dla tego połączenia przebiegałaby przez port przekazywania. Zrobiono to w celu poprawy wydajności na pakiet: w tamtych czasach komputery były o kilka rzędów wielkości wolniejsze.
źródło