PHP
Gniazda
Szukaj…
Gniazdo klienta TCP
Tworzenie gniazda korzystającego z protokołu TCP (Transmission Control Protocol)
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
Upewnij się, że gniazdo zostało pomyślnie utworzone. Funkcja onSocketFailure
pochodzi z przykładu Obsługa błędów gniazda w tym temacie.
if(!is_resource($socket)) onSocketFailure("Failed to create socket");
Podłącz gniazdo do określonego adresu
Druga linia kończy się niepowodzeniem w przypadku awarii połączenia.
socket_connect($socket, "chat.stackoverflow.com", 6667)
or onSocketFailure("Failed to connect to chat.stackoverflow.com:6667", $socket);
Wysyłanie danych na serwer
Funkcja socket_write
wysyła bajty przez gniazdo. W PHP tablica bajtów jest reprezentowana przez ciąg znaków, który zwykle nie jest wrażliwy na kodowanie.
socket_write($socket, "NICK Alice\r\nUSER alice 0 * :Alice\r\n");
Odbieranie danych z serwera
Poniższy fragment socket_read
odbiera niektóre dane z serwera za pomocą funkcji socket_read
.
Przekazanie PHP_NORMAL_READ
jako trzeciego parametru odczytuje aż do bajtu \r
/ \n
, a ten bajt jest zawarty w wartości zwracanej.
PHP_BINARY_READ
, PHP_BINARY_READ
odczytuje wymaganą ilość danych ze strumienia.
Jeśli socket_set_nonblock
został wezwany przed i PHP_BINARY_READ
służy, socket_read
zwróci false
natychmiast. W przeciwnym razie metoda blokuje się do momentu odebrania wystarczającej ilości danych (aby osiągnąć długość drugiego parametru lub osiągnąć koniec linii) lub gniazdo zostanie zamknięte.
Ten przykład czyta dane z rzekomo serwera IRC.
while(true) {
// read a line from the socket
$line = socket_read($socket, 1024, PHP_NORMAL_READ);
if(substr($line, -1) === "\r") {
// read/skip one byte from the socket
// we assume that the next byte in the stream must be a \n.
// this is actually bad in practice; the script is vulnerable to unexpected values
socket_read($socket, 1, PHP_BINARY_READ);
}
$message = parseLine($line);
if($message->type === "QUIT") break;
}
Zamykanie gniazda
Zamknięcie gniazda zwalnia gniazdo i związane z nim zasoby.
socket_close($socket);
Gniazdo serwera TCP
Tworzenie gniazda
Utwórz gniazdo korzystające z protokołu TCP. To samo co tworzenie gniazda klienta.
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
Oprawa gniazdowa
Powiąż połączenia z danej sieci (parametr 2) dla określonego portu (parametr 3) z gniazdem.
Drugi parametr to zazwyczaj "0.0.0.0"
, który akceptuje połączenie ze wszystkich sieci. Może też
Jedną z częstych przyczyn błędów z socket_bind
jest to, że podany adres jest już powiązany z innym procesem . Inne procesy są zwykle zabijane (zwykle ręcznie, aby zapobiec przypadkowemu zabiciu procesów krytycznych), aby uwolnić gniazda.
socket_bind($socket, "0.0.0.0", 6667) or onSocketFailure("Failed to bind to 0.0.0.0:6667");
Ustaw gniazdo do słuchania
Spraw, aby gniazdo nasłuchiwało połączeń przychodzących za pomocą socket_listen
. Drugi parametr to maksymalna liczba połączeń, które umożliwiają kolejkowanie przed ich zaakceptowaniem.
socket_listen($socket, 5);
Obsługa połączenia
Serwer TCP to tak naprawdę serwer, który obsługuje połączenia podrzędne. socket_accept
tworzy nowe połączenie potomne.
$conn = socket_accept($socket);
Przesyłanie danych dla połączenia z socket_accept
jest takie samo jak dla gniazda klienta TCP .
Kiedy to połączenie powinno zostać zamknięte, wywołaj socket_close($conn);
bezpośrednio. Nie wpłynie to na oryginalne gniazdo serwera TCP.
Zamykanie serwera
Z drugiej strony socket_close($socket);
należy wywoływać, gdy serwer nie jest już używany. Spowoduje to również zwolnienie adresu TCP, umożliwiając innym procesom powiązanie z tym adresem.
Obsługa błędów gniazd
socket_last_error
może być użyty do uzyskania identyfikatora błędu ostatniego błędu z rozszerzenia gniazd.
socket_strerror
może być użyty do konwersji identyfikatora na ciągi czytelne dla człowieka.
function onSocketFailure(string $message, $socket = null) {
if(is_resource($socket)) {
$message .= ": " . socket_strerror(socket_last_error($socket));
}
die($message);
}
Gniazdo serwera UDP
Serwer UDP (protokół datagramu użytkownika), w przeciwieństwie do TCP, nie jest oparty na strumieniu. Opiera się na pakietach, tzn. Klient wysyła dane w jednostkach zwanych „pakietami” do serwera, a klient identyfikuje klientów na podstawie ich adresu. Nie ma wbudowanej funkcji, która dotyczy różnych pakietów wysyłanych z tego samego klienta (w przeciwieństwie do TCP, gdzie dane z tego samego klienta są obsługiwane przez określony zasób utworzony przez socket_accept
). Można to uznać za nowe połączenie TCP akceptowane i zamykane za każdym razem, gdy przybywa pakiet UDP.
Tworzenie gniazda serwera UDP
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
Powiązanie gniazda z adresem
Parametry są takie same jak dla serwera TCP.
socket_bind($socket, "0.0.0.0", 9000) or onSocketFailure("Failed to bind to 0.0.0.0:9000", $socket);
Wysyłanie pakietu
Ta linia wysyła $data
w pakiecie UDP na $address
: $port
.
socket_sendto($socket, $data, strlen($data), 0, $address, $port);
Odbieranie pakietu
Poniższy fragment próbuje zarządzać pakietami UDP w sposób indeksowany przez klienta.
$clients = [];
while (true){
socket_recvfrom($socket, $buffer, 32768, 0, $ip, $port) === true
or onSocketFailure("Failed to receive packet", $socket);
$address = "$ip:$port";
if (!isset($clients[$address])) $clients[$address] = new Client();
$clients[$address]->handlePacket($buffer);
}
Zamykanie serwera
socket_close
może być używany w zasobie gniazda serwera UDP. Spowoduje to zwolnienie adresu UDP, umożliwiając innym procesom powiązanie z tym adresem.