php中socket系、fsockopen、stream_socket系、curl_init系的用法与区别

参考:

https://www.php.cn/php-weizijiaocheng-363026.html(socket实现长连接)

socket系列 水泥、沙子,底层的东西

fsockopen 水泥预制件,可以用来搭房子

stream_socket系列

curl系列 毛坯房,自己装修一下就能住了

curl系列函数:应用层

基本用法:

// 创建一个新cURL资源
$ch = curl_init();

// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, "http://www.example.com/");
curl_setopt($ch, CURLOPT_HEADER, 0);
//或者
curl_setopt_array($ch,$opt_arr);
// 抓取URL并把它传递给浏览器 curl_exec($ch); // 关闭cURL资源,并且释放系统资源 curl_close($ch);

辅助函数:

curl_getinfo($ch)//返回资源句柄信息
curl_error($ch)//错误字符串
curl_errno($ch)//作物代码
curl_copy_handle($ch)//复制一个句柄和它的所有选项

stream_socket系列的函数

stream_socket服务端:

重要函数:

stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr);//创建服务
stream_socket_accept($socket);//获取连接句柄
fwrite($fpcon)
fgetc($fpcon)
fgets($fpcon)
fread($fpcon)
feof($fpcon)
fclose($fpcon)

使用方法:

$socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr);
if (!$socket) {
  echo "$errstr ($errno)<br />
";
} else {
      while ($conn = stream_socket_accept($socket)) {
     $data = fread($conn);
        fwrite($conn, 'The local time is ' . date('n/j/Y g:i a') . "
"); 
        fclose($conn); 
    } 
    fclose($socket); 
}

stream_client客户端:

重要函数:

stream_socket_client("tcp://www.example.com:80", $errno, $errstr, 30);
fclose($fp)

使用方法:

$fp = stream_socket_client("tcp://www.example.com:80", $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />
";
} else {
    fwrite($fp, "GET / HTTP/1.0
Host: www.example.com
Accept: */*

");
    while (!feof($fp)) {
        echo fgets($fp, 1024);
    }
    fclose($fp);
}

fsockopen函数

fsockopen可以忽略socket里面的creat, connect, send, recv等等函数的用法,直接就open了

stream_socket_client 和 fsockopen 没有本质上的区别

stream_socket_client 和 fsockopen 分属不同流派的对 socket 的封装

fsockopen 是比较底层的调用,属于网络系统的socket调用,而curl经过的包装支持HTTPS认证,HTTP POST方法, HTTP PUT方法,FTP上传, kerberos认证,HTTP上传,代理服务器, cookies,用户名/密码认证,下载文件断点续传,上载文件断点续传,http代理服务器管道( proxy tunneling),甚至它还支持IPv6, socks5代理服务器,,通过http代理服务器上传文件到FTP服务器等等,功能十分强大。fsockopen 返回的是没有处理过的数据,包括数据的长度数据内容和数据的结束符。而curl是处理后的内容。

在用户使用时,curl 更加方便,但其参数很多,配置稍微复杂,fsockopen 则有固定的几个参数,简单,但获取结果可能需要再做处理。

tcp

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />
";
} else {
    $out = "GET / HTTP/1.1
";
    $out .= "Host: www.example.com
";
    $out .= "Connection: Close

";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}

udp

$fp = fsockopen("udp://127.0.0.1", 13, $errno, $errstr);
if (!$fp) {
    echo "ERROR: $errno - $errstr<br />
";
} else {
    fwrite($fp, "
");
    echo fread($fp, 26);
    fclose($fp);
}

socket底层函数

服务端:

重要函数:

 * 1,创建      $socket=socket_create()
 * 2,绑定端口  socket_bind($socket)
 * 3,监听连接     socket_listen($socket)
 * 4,接收     $accept_resource=socket_accept($socket) 循环接收
 * 5,读取     socket_read($accept_resource)
 * 6,写入     socket_write($accept_resource)
 * 7,关闭主机发过来的套接流        socket_close($accept_resource)
 * 8,关闭     socket_close($socket)
  socket_last_error(
$socket),参数为socket_create的返回值,作用是获取套接字的最后一条错误码号,返回值套接字code   socket_strerror($code),参数为socket_last_error函数的返回值,获取code的字符串信息,返回值也就是套接字的错误信息

socket_set_option($sfd, SOL_SOCKET, SO_REUSEADDR, 1); 
socket_set_nonblock($sfd); 
socket_select($rs, $ws, $es, 3);

使用方法(长连接):

date_default_timezone_set("Asia/Shanghai");
include_once "Db.php";
$sfd = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($sfd, "192.168.191.1", 9001);
socket_listen($sfd, 10); //监听10个
socket_set_option($sfd, SOL_SOCKET, SO_REUSEADDR, 1); //重用端口
socket_set_nonblock($sfd); //非阻塞
$rfds = array($sfd);
$wfds = array();
do{
    $rs = $rfds;
    $ws = $wfds;
    $es = array();
    $ret = socket_select($rs, $ws, $es, 3);
    //read event
    foreach($rs as $fd){

        if($fd == $sfd){
            $cfd = socket_accept($sfd);
            socket_set_nonblock($cfd);
            $rfds[] = $cfd;
            echo "new client coming, fd=$cfd
";
        }else{
            //获取客户端IP地址
            socket_getpeername($fd, $addr, $port);
            //读取客户端信息
            $msg = socket_read($fd, 1024);
            echo date("H:i:s")." $fd $msg";
            $arr=explode(",", $msg);
            //更新到数据库
            $sql = "update test set wendu = $arr[0],shidu = $arr[1], green = $arr[2],yellow=$arr[3] ,red=$arr[4],dianliu=$arr[5],             dianya=$arr[6] where ip = '$addr'" ;
            Db::query($sql);
        }
    }


}while(true);

原来的写法;只能处理一个连接

//创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);

/*绑定接收的套接流主机和端口,与客户端相对应*/
if(socket_bind($socket,'127.0.0.1',8889) == false){
    echo 'server bind fail:'.socket_strerror(socket_last_error());
    /*这里的127.0.0.1是在本地主机测试,你如果有多台电脑,可以写IP地址*/
}
//监听套接流
if(socket_listen($socket,4)==false){
    echo 'server listen fail:'.socket_strerror(socket_last_error());
}
//echo 'create success';
//让服务器无限获取客户端传过来的信息
do{
    /*接收客户端传过来的信息*/
    $accept_resource = socket_accept($socket);//这里只能接受一个连接
    /*socket_accept的作用就是接受socket_bind()所绑定的主机发过来的套接流*/

    if($accept_resource !== false){
        /*读取客户端传过来的资源,并转化为字符串*/
        while(true){//让服务器无限获取客户端传过来的信息
            $string = mb_convert_encoding(socket_read($accept_resource,1024),'utf-8','GBK');
            /*mb_convert_encoding()*/
            /*socket_read的作用就是读出socket_accept()的资源并把它转化为字符串*/

            echo 'server receive is :'.$string.PHP_EOL;//PHP_EOL为php的换行预定义常量
            if($string != false){
                $return_client = 'server receive is : '.$string.PHP_EOL;
                /*向socket_accept的套接流写入信息,也就是回馈信息给socket_bind()所绑定的主机客户端*/
                socket_write($accept_resource,$return_client,strlen($return_client));
                /*socket_write的作用是向socket_create的套接流写入信息,或者向socket_accept的套接流写入信息*/
            }else{
                echo 'socket_read is fail';
            }
        }
        /*socket_close的作用是关闭socket_create()或者socket_accept()所建立的套接流*/
        socket_close($accept_resource);
    }
}while(1)
socket_close($socket);

客户端:

重要函数

 * 1,创建      $socket=socket_create()
 * 2,连接     socket_connect()
 * 3,写入     socket_write($socket)
 * 4,读取     socket_read($socket) 循环读取
 * 5,关闭     socket_close($socket)
  socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 1, "usec" => 0));

使用方法(长连接):

//创建一个socket套接流
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
/****************设置socket连接选项,这两个步骤你可以省略*************/
//接收套接流的最大超时时间1秒,后面是微秒单位超时时间,设置为零,表示不管它
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 1, "usec" => 0));
//发送套接流的最大超时时间为6秒
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 6, "usec" => 0));
/****************设置socket连接选项,这两个步骤你可以省略*************/

//连接服务端的套接流,这一步就是使客户端与服务器端的套接流建立联系
if(socket_connect($socket,'127.0.0.1',8889) == false){
    echo 'connect fail massege:'.socket_strerror(socket_last_error());
}else{
    while(true){
        fwrite(STDOUT, "Enter the message:");
        $message = trim(fgets(STDIN));

        $message = mb_convert_encoding($message,'UTF-8','GBK');
        //向服务端写入字符串信息

        if(socket_write($socket,$message,mb_strlen($message)) == false){
            echo 'fail to write'.socket_strerror(socket_last_error());
        }else{
            echo 'client write success'.PHP_EOL;
            //读取服务端返回来的套接流信息
            while($callback = socket_read($socket,1024)){
                echo 'server return message is:'.PHP_EOL.$callback;
            }
        }
    }

}
socket_close($socket);//工作完毕,关闭套接流

总之,file_get_contents 和 curl 能干的,socket都能干。socket能干的,curl 就不一定能干了。file_get_contents 更多的时候只是去拉取数据。效率比较高也比较简单。 

只讨论 curl 与file_get_contents 的话,有这么一些结论:

1.    fopen /file_get_contents 每次请求都会重新做DNS查询,并不对DNS信息进行缓存。但是CURL会自动对DNS信息进行缓存。对同一域名下的网页或者图片的请求只需要一次DNS查询。这大大减少了DNS查询的次数。所以CURL的性能比fopen /file_get_contents 好很多。

2.    fopen /file_get_contents在请求HTTP时,使用的是http_fopen_wrapper,不会keeplive。而curl却可以。这样在多次请求多个链接时,curl效率会好一些。

3.    fopen / file_get_contents函数会受到php.ini文件中allow_url_open选项配置的影响。如果该配置关闭了,则该函数也就失效了。而curl不受该配置的影响。

4.    curl可以模拟多种请求,例如:POST数据,表单提交等,用户可以按照自己的需求来定制请求。而fopen / file_get_contents只能使用get方式获取数据。

PS:file_get_contents()函数获取https链接内容的时候,需要php 中mod_ssl的支持(或安装opensll)。

那么file_get_contents呢?

有些时候用 file_get_contents() 调用外部文件容易超时报错。curl 效率比 file_get_contents() 和 fsockopen() 高一些,原因是CURL会自动对DNS信息进行缓存。

file_get_contents / curl / fsockopen 在当前所请求环境下选择性操作,没有一概而论。

file_get_contents 需要php.ini里开启allow_url_fopen,请求http时,使用的是http_fopen_wrapper,不会keeplive的话curl是可以的。 file_get_contents()单个执行效率高,返回没有头的信息。 

这个是读取一般文件的时候并没有什么问题,但是在读取远程问题的时候有可能就会出现问题。如果是要打一个持续连接,多次请求多个页面。那么file_get_contents和fopen就会出问题。取得的内容也可能会不对。所以做一些类似采集工作的时候,肯定就有问题了。 

结论就是,curl 效率及稳定都比 file_get_contents() 要好,fsockopen 也很强大,但是比较偏底层。

原文地址:https://www.cnblogs.com/tkzc2013/p/10938579.html