Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung der Socket-Kommunikation in PHP

Detaillierte Erläuterung der Socket-Kommunikation in PHP

小云云
小云云Original
2018-03-16 11:34:011809Durchsuche

Manchmal muss unser PHP-Programm mit anderen Systemen kommunizieren. Beispielsweise stellt die offizielle Website eines Unternehmens Informationen zur Rückverfolgbarkeit von Produkten bereit. Zu diesem Zeitpunkt ist ein PHP-Netzwerk erforderlich . Für die Programmierung stellt PHP eine Sockets-Erweiterung zur Verfügung. Die offizielle Website-Adresse lautet:

http://nl3.php.net/manual/zh/intro.sockets.php

Diese Erweiterung gibt uns die Möglichkeit, Sockets direkt über PHP zu manipulieren, sodass wir mit anderen Systemen kommunizieren können. Wir verwenden Sockets, um direkt über der Transportschicht des OSI-Netzwerkmodells zu arbeiten Verwenden von TCP, dem von UDP bereitgestellten Dienst, sodass Sie ihn als Client für andere Protokolle auf Anwendungsebene verwenden können, z. B. um gängige SMTP-, Pop- und FTP-Clients zu simulieren Damit Sie es verwenden und die Serverinteraktion von TELNET zulassen können, handelt es sich bei diesen Protokollen allesamt um Protokolle auf Anwendungsebene, sodass für die benutzerdefinierte Kommunikation zwischen Systemen benutzerdefinierte Protokolle erforderlich sind.

Hier ist ein Beispiel für die Entwicklung eines einfachen Dateiempfangsservers mit PHP. Ein weiteres PHP-Clientprogramm zeigt, wie mit diesem Serverprogramm kommuniziert wird.

Serverprogramm:

<?php
/**
 * 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展
 * 这是一个简易的服务器程序,接收客户端发送的文件,保存后关闭
 * 通讯协议约定为:文件大小::文件扩展名::有效数据
 * 通讯结束标识符为:“-end-”
 * 在命令行或浏览器中执行均可,推荐命令行
 * 作者:云客【云游天下,作客四方】
 */

/****配置****/
//要绑定监听的本机ip地址和端口
$address = &#39;127.0.0.1&#39;;
$port = 81;

error_reporting(E_ALL);

//防止超时
set_time_limit(0);

//开启绝对刷送,禁止缓冲内容
ob_implicit_flush();

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
//绑定套接字到端口
if (socket_bind($sock, $address, $port) === false) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
//开始监听端口,参数5表示可以让5个连接请求在缓冲中排队
//排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将直接忽略,客户端显示无法链接
//注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队
if (socket_listen($sock, 5) === false) {
    echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

do {
    //程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源
    if (($client_sock = socket_accept($sock)) === false) {
        echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break;
    }
    echo "[client connect start]\n";
    $data = ""; //接收的数据
    $data_size = -1; //接收的数据大小
    $received_size = 0; //实际接收的数据
    $ext_name = "txt"; //默认文件扩展名

    //开始与客户端交互
    do {
        //运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容
        if (false === ($buf = socket_read($client_sock, 2048))) {
            echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($client_sock)) . "\n";
            break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器
        }
        if ($data_size === -1) {//第一次接受数据,解析通讯协议
            $arr = explode("::", $buf, 3);
            $data_size = (int)$arr[0];
            $ext_name = $arr[1];
            $data .= $arr[2];
            $received_size += strlen($data);
            continue;
        }
        $data .= $buf;
        $received_size += strlen($buf);
        if ($data_size <= $received_size) {
            if ($data_size < $received_size) {
                $data = substr($data, 0, $data_size);
            }

            echo "received:" . $received_size . "/" . "total:" . $data_size;
            $talkback = "-end-"; //发送通讯结束标志
            socket_write($client_sock, $talkback, strlen($talkback));
            file_put_contents("servertest." . $ext_name, $data);
            break 2; //关闭服务器
        }

        echo $talkback = "received:" . $received_size . "\n";
        socket_write($client_sock, $talkback, strlen($talkback));


    } while (true);
    socket_close($client_sock);

} while (true);

socket_close($sock);
?>

Clientprogramm:

<?php
/**
 * 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展
 * 这是一个简易的客户端程序,向服务器发送文件
 * 通讯协议约定为:文件大小::文件扩展名::有效数据
 * 通讯结束标识符为:“-end-”
 * 在命令行或浏览器中执行均可,推荐命令行,需先开启服务器端
 * 作者:云客【云游天下,作客四方】
 */

/****配置****/
$file = "yunke.jpg"; //待发送的文件
//服务器ip和端口号
$address = &#39;127.0.0.1&#39;;
$port = 81;


error_reporting(E_ALL);

// 防止超时 
set_time_limit(0);

// 开启绝对刷送,不要缓冲输出
ob_implicit_flush(true);

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    exit();
}
//打开到远程主机的链接
if (socket_connect($sock, $address, $port) === false) {
    echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
    exit();
}

//构造一个很大的待发送数据 大约10MB
/*
$data = "";
for ($i = 1; $i <= 10000000; $i++) {
    $data .= "data:" . $i;
}
$data = $data . $data . $data;
*/

$data = file_get_contents($file);
$data_size = strlen($data);
$arr_temp = explode(&#39;.&#39;, $file);
$ext_name = end($arr_temp);
$sock_data = $data_size . "::" . $ext_name . "::" . $data;

//发送数据给远程主机
while (true) {
    $sock_data_size = strlen($sock_data);
    $send_size = socket_write($sock, $sock_data, $sock_data_size);
    if ($send_size === false) {
        echo "send false:" . socket_strerror(socket_last_error($sock)) . "\n";
        socket_close($sock);
        echo "[client shutdown]\n";
        exit();
    }
    if ($send_size == $sock_data_size) {
        break;
    }
    $sock_data = substr($sock_data, $send_size);
}

//读返回数据
while (true) {
    if (false === ($out = socket_read($sock, 2048))) {
        echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break 1;
    }

    echo "server: " . $out . "\n";
    if (substr($out, -5) == "-end-") { //约定结束标志
        break;
    }
}
//关闭套接字资源
socket_close($sock);
echo "[client shutdown]\nsend data:" . format($data_size);

function format($byte = 0)
{
    if ($byte > 1024 * 1024) {
        return ceil($byte / (1024 * 1024)) . "MB";
    } elseif ($byte > 1024) {
        return ceil($byte / 1024) . "KB";
    } else {
        return $byte . " Byte";
    }
}


Dieses Beispiel zeigt die Übertragung einer Datei an den Server über eine TCP-Kurzverbindung. In tatsächlichen Projekten ist das Kommunikationsmodul nicht so einfach und es muss mehr berücksichtigt werden. Fragen

Zum Beispiel:

Ob eine lange Verbindungsmethode erforderlich ist (Daten werden in einer TCP-Verbindung mehrmals hin und her übertragen), Datenüberprüfung Verhindern Sie Schäden, unterschiedliche Endianness-Probleme zwischen Systemen, benutzerdefinierte Kommunikationsprotokolle, Packet-Sticking-Probleme, Timeout-Verarbeitung, gleichzeitigen Zugriff, Flusskontrolle, TCP-Paket-Entpacken usw.

Wenn Sie mehr erfahren möchten Die oben erwähnte PHP-Netzwerkprogrammierung erfordert systematisches Lernen. Es wird empfohlen, einen Blick auf die Implementierung von Workerman zu werfen.

Es handelt sich um ein in PHP geschriebenes Socket-Server-Framework, das bei der Lösung von Socket-Kommunikationsproblemen hilft it Sie können einen benutzerdefinierten Server usw. erstellen.
workermanDie offizielle Website-Adresse lautet: http://www.workerman.net/

Im Folgenden werden zwei Beispielprogramme zum Anzeigen von Server-HTTP-Headern und Browser-Headern bereitgestellt:

Das folgende Beispiel zeigt die vom Server zurückgegebenen Header-Informationen an und ändert die Serveradresse muss angezeigt werden, und dann im Browser. Greifen Sie einfach auf das Skript zu:

<?php
error_reporting(E_ALL);

// 防止超时 
set_time_limit(0);

// 开启绝对刷送,不要缓冲输出
ob_implicit_flush(true);

$address = &#39;www.qq.com&#39;; //要查看的服务器
$port=80;

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    exit();
}
//打开到远程主机的链接
if (socket_connect($sock, gethostbyname($address), $port) === false) {
    echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
    exit();
}

//构造http头
$msg = "GET / HTTP/1.1 \r\n";
$msg .= "Host: {$address}\r\n";
$msg .= "Connection: Close\r\n\r\n";  
//指示服务器发回完毕就断开,不必等等其他资源的链接
//如果没有该行会,服务器会一直等待我方继续发送数据,直到超时关闭
//而我方不会再发送,这个会让下面的socket_read函数互为等待,等很久

//发送给远程主机
socket_write($sock, $msg, strlen($msg));

$str="";
while ($out = @socket_read($sock, 2048*4)) {
    $str.=$out;
}
$str=explode("\r\n\r\n", $str);
$str=$str[0];
if($str)
{
    echo "<pre class="brush:php;toolbar:false">\r\n".$str."\r\n
"; }else{ echo "nothing"; } //关闭套接字资源 socket_close($sock);

Das Folgende ist ein Beispiel für die Anzeige Browser-Header. Sie können leicht sehen, was der Browser sendet.
Stellen Sie zunächst sicher, dass Port 80 geschlossen ist, ändern Sie dann die Hostdatei des Computers und leiten Sie die URL, auf die Sie zugreifen möchten, an die Adresse 127.0.0.1 weiter Verwenden Sie den PHP-Befehlszeilenmodus, um dieses Skript zu starten, und verwenden Sie dann den Browser, um auf die Einstellungen zuzugreifen. Klicken Sie einfach auf die angegebene URL. Wenn Sie es schließen müssen, verwenden Sie bitte die Tastenkombination Strg+C auf der Konsole. Das Programm lautet wie folgt:

<?php
error_reporting(E_ALL);

//防止超时
set_time_limit(0);

//开启绝对刷送,禁止缓冲内容
ob_implicit_flush();

$address = &#39;127.0.0.1&#39;;
$port = 80;

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
//绑定套接字到端口
if (socket_bind($sock, $address, $port) === false) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
//开始监听端口,参数5表示可以让5个连接请求在缓冲中排队
//排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将显示无法链接
//注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队
if (socket_listen($sock, 5) === false) {
    echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

do {
    //程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源
    //可以使用socket_set_nonblock函数设置非阻塞模式
    if (($msgsock = socket_accept($sock)) === false) {
        echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break;
    }
    
    //链接成功后在控制台显示提示
    echo "[client start ".date("Y-m-d H:i:s")."]\n\n";
    
    do {
        //运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容
        if (false === ($buf = socket_read($msgsock, 2048*6))) {
            echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";
            break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器
        }
        $talkback ="HTTP/1.1 200 OK\r\n\r\n";
        $talkback .= date("Y-m-d H:i:s")."\nBrowser HTTP Headers:\n\n".$buf."\n";
        //向客户端显示该内容
        socket_write($msgsock, $talkback, strlen($talkback));
        //向服务器控制台显示该内容
        echo "$buf\n";
        break ; //中断本次与浏览器的链接
    } while (true);
    socket_close($msgsock);
} while (true);

socket_close($sock);
?>

Verwandte Empfehlungen:

Wie um die lokale Quellportnummer für die Socket-Kommunikation in Linux zu erhalten

PHP-Socket-Serveraufbau und Testbeispielfreigabe

Einfache Methode zur Verwendung von Socket in PHP

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Socket-Kommunikation in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn