PHP中socket的使用入门

一、客户端socket


1. 创建socket

$socket = socket_create(AF_INET, SOCK_STREAM, 0);

该函数返回socket描述符,三个参数分别是:
地址协议:AF_INET (这里是ipv4)
连接接类型:SOCK_STREAM(面向连接的TCP协议)
协议:0|IPPROTO_IP  (IP协议)

【错误处理】
如果任何socket函数失败,可以使用 socket_last_errr 和  socket_strerror 函数检索错误信息

if(!($socket = socket_create(AF_INET,SOCK_STREAM,0))){
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("Couldn't create socket");
}

【注意】
除了 SOCK_STREAM  类型的socket协议之外, 还有 SOCK_DGRAM 的类型 表示 UDP协议,因此需要知道远程服务器的 ip 地址

2. 连接到服务器

if(!socket_connect($socket,'104.193.88.77',80)){
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("connect failed !");
}

到这里,我们运行 
$ php socket.php 
就能检测 ip 为104.193.88.77 端口号为 80 是否开启,由此可以构建一个端口扫描程序。

注意:这里连接必须是一个ip地址,有时候我们只知道域名,可以使用如下方式转换:

$ip_address = gethostbyname('www.baidu.com');

连接的概念适用于 SOCK_STREAM/TCP , UDPICMPARP等其他基于非连接的通信没有连接的概念

3. 发送数据

函数 send 只会发送数据,它需要socket描述符,发送的数据及其大小。

$message = "GET / HTTP/1.1\r\n\r\n";
if(!socket_send($socket,$message,strlen($message),0)){
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("send failed !");
}

该消息实际上是一个获取网站主页的http命令

【注意】
将数据发送到socket时,您基本上是将数据写入socket 类似于将数据写入文件,因此还可以使用 write 函数 将数据发送到socket。

4. 接收数据

使用 recv 函数接收套接字上的数据

if(socket_recv($socket,$buf,2045,MSG_WAITALL) === false){
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("receive failed !");
}
echo $buf;

5. 关闭socket

socket_close($socket);

总结一下:客户端发起socket请求流程
》创建socket
》连接到远程服务器
》发送数据
》接收数据

》关闭socket

其实就是类似于打开浏览器访问www.baidu.com一样的整个流程


二、服务端socket

1. 创建一个服务端master socket

if(!($socket = socket_create(AF_INET,SOCK_STAREAM,0))){
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("create socket failed ");
}

2. 绑定到地址(和端口)

函数 bind 可用于套接字绑定到特定的地址和端口,他需要一个类似 connect 函数的结构 服务socket、ip 和端口

if(!socket_bind($socket,'127.0.0.1',5001)){
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("bind socket failed ");
}

如果你想接收所有的 ip 可以将 ip 改为 0.0.0.0

3. 监听连接

socket_listen($socket, 10);

第二个参数是控制程序在已经很忙碌的时候保持等待的传入连接数,比如 已经有10个连接等待处理,第11个连接请求将会被拒绝


4. 接受连接,关闭连接

$client = socket_accept($socket);
if(socket_getpeername($client,$address,$port)){
    echo "";
}
socket_close($client);
socket_close($socket);

$ php server.php

此时该程序正在等待端口5001上的传入连接我们新开一个窗口

$ telnet localhost 5001

【注意】
socket_getpeername 函数用于获取有关通过特定socket连接到服务器的客户端详细信息

我们建立了连接,并立即关闭了他,这并没有什么实际的用处,那我们回复客户端,我们可以使用socket_write 函数向传入的套接字
写入内容

5. 读取/发送数据

$input = socket_read($client,102400);
$response = "ok ... $input";
socket_write($client,$response);
socket_close($client);

在终端中运行
$ telnet localhost 5001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
happy
OK .. h
Connection closed by foreign host

我们收到回复之后,连接被立即关闭了,而像百度这样的服务器总是一直在接收传入的链接,这意味着服务器应该一直在
运行,所以我们想让自己的服务器一直运行,最简单的方法是将 accept 置于一个循环中以便他能一直接收传入的连接

6. 在线服务器

$address = '0.0.0.0';
$port = '5001';
while(true){
    $client = socket_accept($socket);
    if(socket_getpeername($client,$address,$port)){
      echo ''; 
    }
    $input = socket_read($client, 1024000);
    $response = "OK .. $input";
    socket_write($client, $response);
}

这里我们将 accpet 加入了循环,但是每个发起的客户端,仍然没有有效的通信,他无法一次处理多个连接

7. 多连接处理

为了处理每个连接,我们需要一个单独的处理代码来与主服务器接受连接一起运行。实现此目的的一种方法是使用线程。主服务器程序接受连接并创建一个新线程来处理连接的通信,然后服务器返回以接受更多连接。但是php不直接支持线程。

另一种方法是使用 select 函数。select 函数基本上'轮询'或观察一组套接字用于某些事件,比如它是否可读,可写或有问题等等。
因此 select 函数可用于监视多个客户端并检查哪个客户端发送了一条消息。

error_reporting(~E_NOTICE);
set_time_limit (0);
 
$address = "0.0.0.0";
$port = 5000;
$max_clients = 10;
//1. 创建连接 
if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);     
    die("Couldn't create socket: [$errorcode] $errormsg \n");
}
 
echo "Socket created \n";
 
// 绑定连接
if( !socket_bind($sock, $address , 5000) )
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
     
    die("Could not bind socket : [$errorcode] $errormsg \n");
}
 
echo "Socket bind OK \n";

//监听 
if(!socket_listen ($sock , 10))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
     
    die("Could not listen on socket : [$errorcode] $errormsg \n");
}
 
echo "Socket listen OK \n";
 
echo "Waiting for incoming connections... \n";
 
//array of client sockets
$client_socks = array();
 
//array of sockets to read
$read = array();
 
//开始循环接收进来的连接和已经存在的连接
while (true) 
{
    //准备一个存储socket的数组
    $read = array();
     
    //第一个是主服务的 socket
    $read[0] = $sock;
     
    //然后添加存在的 client sockets
    for ($i = 0; $i < $max_clients; $i++)
    {
        if($client_socks[$i] != null)
        {
            $read[$i+1] = $client_socks[$i];
        }
    }
     
    //调用 select - 遇到错误 终止调用
    if(socket_select($read , $write , $except , null) === false)
    {
        $errorcode = socket_last_error();
        $errormsg = socket_strerror($errorcode);
     
        die("Could not listen on socket : [$errorcode] $errormsg \n");
    }
     
    //如果包含了主服务的 socket, 那么就可以就收新的客户端socket连接
    if (in_array($sock, $read)) 
    {
        for ($i = 0; $i < $max_clients; $i++)
        {
            if ($client_socks[$i] == null) 
            {
                $client_socks[$i] = socket_accept($sock);//接受连接
                 
                //显示连接信息
                if(socket_getpeername($client_socks[$i], $address, $port))
                {
                    echo "Client $address : $port is now connected to us. \n";
                }
                 
                //发送语句给客户端
                $message = "Welcome to php socket server version 1.0 \n";
                $message .= "Enter a message and press enter, and i shall reply back \n";
                socket_write($client_socks[$i] , $message);
                break;
            }
        }
    }
 
    //检查每个客户端是否有数据发送
    for ($i = 0; $i < $max_clients; $i++)
    {
        if (in_array($client_socks[$i] , $read))
        {
            $input = socket_read($client_socks[$i] , 1024);//读取数据
             
            if ($input == null) 
            {
                //如果输入为空 那么意味着客户端失去连接 关闭客户端并移除
                unset($client_socks[$i]);
                socket_close($client_socks[$i]);
            }
 
            $n = trim($input);
 
            $output = "OK ... $input";
             
            echo "Sending output to client \n";
             
            //发送回复信息给客户端
            socket_write($client_socks[$i] , $output);
        }
    }
}

运行上述服务器并像以前一样打开3个终端。现在,服务器将为连接到它的每个客户端创建一个线程。

总结一下:  服务端创建一个socket服务需要如下几步

》1. 打开socket
》2. 绑定到地址(和端口)
》3. 监听传入的连接
》4. 接受数据
》5. 读取数据/发送数据

原文来自 :https://www.binarytides.com/php-socket-programming-tutorial/

猜你喜欢

转载自blog.csdn.net/wujiangwei567/article/details/81144852