zoukankan      html  css  js  c++  java
  • 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 也很强大,但是比较偏底层。

  • 相关阅读:
    mysql主从原理
    mysql锁和事物隔离
    mysql索引查找原理及调优
    MySQL高级查询
    周总结04
    冲刺第六天
    冲刺第五天
    冲刺第四天
    典型用户模板和用户场景模版
    冲刺第三天
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/10938579.html
Copyright © 2011-2022 走看看