Perl - programowanie gniazd

Co to jest gniazdo?

Socket to mechanizm Berkeley UNIX służący do tworzenia wirtualnego połączenia dupleksowego między różnymi procesami. Zostało to później przeniesione na każdy znany system operacyjny, umożliwiając komunikację między systemami w różnych lokalizacjach geograficznych działającymi na różnych systemach operacyjnych. Gdyby nie gniazdo, większość komunikacji sieciowej między systemami nigdy by się nie wydarzyła.

Przyjrzyjmy się bliżej; typowy system komputerowy w sieci odbiera i wysyła informacje zgodnie z życzeniem przez różne działające w nim aplikacje. Informacje te są kierowane do systemu, ponieważ jest mu przypisany unikalny adres IP. W systemie ta informacja jest przekazywana odpowiednim aplikacjom, które nasłuchują na różnych portach. Na przykład przeglądarka internetowa nasłuchuje na porcie 80 w poszukiwaniu informacji otrzymanych z serwera WWW. Możemy również napisać własne aplikacje, które mogą nasłuchiwać i wysyłać / odbierać informacje na określonym numerze portu.

Na razie podsumujmy, że gniazdo to adres IP i port, umożliwiające połączenie w celu wysyłania i odbierania danych przez sieć.

Aby wyjaśnić powyższą koncepcję gniazda, weźmiemy przykład programowania klient-serwer przy użyciu Perla. Aby ukończyć architekturę serwera klienta, musielibyśmy wykonać następujące kroki -

Aby utworzyć serwer

  • Utwórz gniazdo za pomocą socket połączenie.

  • Powiąż gniazdo z adresem portu za pomocą bind połączenie.

  • Nasłuchuj gniazda pod adresem portu za pomocą listen połączenie.

  • Zaakceptuj połączenia klientów za pomocą accept połączenie.

Tworzenie klienta

  • Utwórz gniazdo za pomocą socket połączenie.

  • Połącz (gniazdo) z serwerem za pomocą connect połączenie.

Poniższy diagram przedstawia pełną sekwencję wywołań używanych przez klienta i serwer do komunikacji między sobą -

Połączenia z gniazdem po stronie serwera

Wywołanie socket ()

Plik socket()wywołanie to pierwsze wywołanie podczas nawiązywania połączenia sieciowego w celu utworzenia gniazda. To wywołanie ma następującą składnię -

socket( SOCKET, DOMAIN, TYPE, PROTOCOL );

Powyższe wywołanie tworzy SOCKET, a pozostałe trzy argumenty są liczbami całkowitymi, które powinny mieć następujące wartości dla połączeń TCP / IP.

  • DOMAINpowinno być PF_INET. Prawdopodobnie 2 na twoim komputerze.

  • TYPE powinno być SOCK_STREAM dla połączenia TCP / IP.

  • PROTOCOL Powinien być (getprotobyname('tcp'))[2]. Jest to konkretny protokół, taki jak TCP, który ma być odczytywany przez gniazdo.

Więc wywołanie funkcji gniazda wydane przez serwer będzie wyglądało mniej więcej tak -

use Socket     # This defines PF_INET and SOCK_STREAM

socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]);

Wywołanie bind ()

Gniazda utworzone przez wywołanie funkcji socket () są bezużyteczne, dopóki nie zostaną powiązane z nazwą hosta i numerem portu. Serwer używa następujących plikówbind() funkcję, aby określić port, na którym będą akceptować połączenia od klientów.

bind( SOCKET, ADDRESS );

Tutaj SOCKET jest deskryptorem zwracanym przez wywołanie funkcji socket (), a ADRES to adres gniazda (dla TCP / IP) zawierający trzy elementy -

  • Rodzina adresów (dla TCP / IP jest to AF_INET, prawdopodobnie 2 w twoim systemie).

  • Numer portu (na przykład 21).

  • Adres internetowy komputera (na przykład 10.12.12.168).

Ponieważ funkcja bind () jest używana przez serwer, który nie musi znać swojego adresu, lista argumentów wygląda następująco -

use Socket        # This defines PF_INET and SOCK_STREAM

$port = 12345;    # The unique port used by the sever to listen requests
$server_ip_address = "10.12.12.168";
bind( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
   or die "Can't bind to port $port! \n";

Plik or die klauzula jest bardzo ważna, ponieważ jeśli serwer umrze bez zaległych połączeń, portu nie będzie można natychmiast użyć ponownie, chyba że użyjesz opcji SO_REUSEADDR za pomocą setsockopt()funkcjonować. Tutajpack_sockaddr_in() Funkcja jest używana do pakowania portu i adresu IP w formacie binarnym.

Połączenie nasłuchu ()

Jeśli jest to program serwerowy, należy wykonać wywołanie listen()na określonym porcie, aby nasłuchiwać, tj. czekać na przychodzące żądania. To wywołanie ma następującą składnię -

listen( SOCKET, QUEUESIZE );

Powyższe wywołanie używa deskryptora SOCKET zwróconego przez wywołanie funkcji socket (), a QUEUESIZE jest maksymalną liczbą oczekujących żądań połączenia dozwolonych jednocześnie.

Wywołanie accept ()

Jeśli jest to program serwera, należy wywołać plik access()funkcja akceptowania połączeń przychodzących. To wywołanie ma następującą składnię -

accept( NEW_SOCKET, SOCKET );

Wywołanie accept odbiera deskryptor SOCKET zwrócony przez funkcję socket (), a po pomyślnym zakończeniu zwracany jest nowy deskryptor gniazda NEW_SOCKET dla całej przyszłej komunikacji między klientem a serwerem. Jeśli wywołanie access () nie powiedzie się, zwraca FLASE, który jest zdefiniowany w module Socket, którego używaliśmy na początku.

Ogólnie rzecz biorąc, accept () jest używana w nieskończonej pętli. Gdy tylko nadejdzie jedno połączenie, serwer albo tworzy proces potomny, aby sobie z nim poradzić, albo sam go obsługuje, a następnie wraca, aby nasłuchiwać kolejnych połączeń.

while(1) {
   accept( NEW_SOCKET, SOCKT );
   .......
}

Teraz wszystkie wywołania związane z serwerem są zakończone i zobaczmy połączenie, które będzie wymagane przez klienta.

Połączenia po stronie klienta

Połączenie connect ()

Jeśli masz zamiar przygotować program kliencki, najpierw użyjesz socket() wywołanie, aby utworzyć gniazdo, a następnie musisz użyć connect()zadzwoń, aby połączyć się z serwerem. Widziałeś już składnię wywołania funkcji socket () i pozostanie ona podobna do wywołania funkcji socket () serwera, ale tutaj jest składniaconnect() zadzwoń -

connect( SOCKET, ADDRESS );

Tutaj SCOKET jest deskryptorem gniazda zwracanym przez wywołanie socket () wydane przez klienta, a ADDRESS jest adresem gniazda podobnym do wywołania bind , z tą różnicą, że zawiera adres IP zdalnego serwera.

$port = 21;    # For example, the ftp port
$server_ip_address = "10.12.12.168";
connect( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
   or die "Can't connect to port $port! \n";

Jeśli pomyślnie połączysz się z serwerem, możesz rozpocząć wysyłanie poleceń do serwera za pomocą deskryptora SOCKET, w przeciwnym razie klient wyjdzie, podając komunikat o błędzie.

Klient - przykład serwera

Poniżej znajduje się kod Perla do implementacji prostego programu klient-serwer przy użyciu gniazda Perla. Tutaj serwer nasłuchuje nadchodzących żądań i po ustanowieniu połączenia po prostu odpowiada na Smile z serwera . Klient czyta tę wiadomość i drukuje na ekranie. Zobaczmy, jak to zostało zrobione, zakładając, że mamy nasz serwer i klienta na tej samej maszynie.

Skrypt do tworzenia serwera

#!/usr/bin/perl -w
# Filename : server.pl

use strict;
use Socket;

# use port 7890 as default
my $port = shift || 7890;
my $proto = getprotobyname('tcp');
my $server = "localhost";  # Host IP running the server

# create a socket, make it reusable
socket(SOCKET, PF_INET, SOCK_STREAM, $proto)
   or die "Can't open socket $!\n";
setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR, 1)
   or die "Can't set socket option to SO_REUSEADDR $!\n";

# bind to a port, then listen
bind( SOCKET, pack_sockaddr_in($port, inet_aton($server)))
   or die "Can't bind to port $port! \n";

listen(SOCKET, 5) or die "listen: $!";
print "SERVER started on port $port\n";

# accepting a connection
my $client_addr;
while ($client_addr = accept(NEW_SOCKET, SOCKET)) {
   # send them a message, close connection
   my $name = gethostbyaddr($client_addr, AF_INET );
   print NEW_SOCKET "Smile from the server";
   print "Connection recieved from $name\n";
   close NEW_SOCKET;
}

Aby uruchomić serwer w tle, wprowadź następujące polecenie w monicie systemu Unix -

$perl sever.pl&

Skrypt do tworzenia klienta

!/usr/bin/perl -w
# Filename : client.pl

use strict;
use Socket;

# initialize host and port
my $host = shift || 'localhost';
my $port = shift || 7890;
my $server = "localhost";  # Host IP running the server

# create the socket, connect to the port
socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2])
   or die "Can't create a socket $!\n";
connect( SOCKET, pack_sockaddr_in($port, inet_aton($server)))
   or die "Can't connect to port $port! \n";

my $line;
while ($line = <SOCKET>) {
   print "$line\n";
}
close SOCKET or die "close: $!";

Teraz uruchommy naszego klienta w wierszu poleceń, który połączy się z serwerem i przeczyta wiadomość wysłaną przez serwer i wyświetli to samo na ekranie w następujący sposób -

$perl client.pl
Smile from the server

NOTE - Jeśli podajesz rzeczywisty adres IP w notacji kropkowej, zaleca się podanie adresu IP w tym samym formacie zarówno w kliencie, jak i na serwerze, aby uniknąć nieporozumień.