如果多次调用,TCP客户端的PHP stream_get_contents将挂起

时间:2016-01-16 13:56:16

标签: php tcp

我正在编写一个程序,用php中的stream_socket_client连接到tcp服务器。我的问题是,如果我在执行任何额外的fwrites之前回显$ client对象的内容,页面就会挂起。目前,如果我在调用stream_get_contents之前发送所有请求,它会工作,但是一旦我调用stream_get_contents,它就像客户端不再响应一样?我会感谢任何帮助。

**编辑 这是我使用的API: https://www.onlinenic.com/cp_english/template_api/download/ONLINENIC_API2.0.pdf

**编辑

//see full code below
//------Client creation code, precho function
function getTESTClient($address, $port)
{
    $client = stream_socket_client("$address:$port", $errno, $errorMessage);
    if ($client === false) {
        throw new UnexpectedValueException("Failed to connect: $errorMessage");
    }
    return $client;
}
function precho($s)
{
    echo "<pre>";
    echo $s;
    echo "</pre>";
}
//-------Problem code
//$loginRequest, $domainAvailableRequest, and $logoutRequest - see full code below
//-----------------------------------------
$address = "www.somesite.com";//This is where I would fill in the address
$port = "12345";//This is where I would fill in the port
$client = getTESTClient($address, $port);
//-----------------------------------------
fwrite($client, $loginRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fwrite($client, $domainAvailableRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fwrite($client, $logoutRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fclose($client);  
//-------Alternative working code, if I read all responses at once...
//*But I want to be able to read each response individually...

fwrite($client, $loginRequest);
fwrite($client, $domainAvailableRequest);
fwrite($client, $logoutRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fclose($client); 

完整代码:

<?php        
    function getDomainType($ext)
    {
        $domaintypes = ['com'=>0, 'net'=>0, 'org'=>807, 'biz'=>800, 'info'=>805, 'us'=>806, 'in'=>808, 
                        'mobi'=>903, 'eu'=>902, 'asia'=>905, 'me'=>906, 'name'=>804, 'tel'=>907, 'cc'=>[600,610], 'tv'=>400, 
                        'tw'=>302, 'uk'=>901, 'co'=>908, 'xxx'=>930, 'pw'=>940, 'club'=>740, 'bike'=>2001, 'clothing'=>2002, 
                        'guru'=>2003, 'holdings'=>2004, 'plumbing'=>2005, 'singles'=>2006, 'ventures'=>2007, 'camera'=>2008, 
                        'equipment'=>2009, 'estate'=>2010, 'gallery'=>2011, 'graphics'=>2012, 'lighting'=>2013, 'photography'=>2014, 
                        'construction'=>2015, 'contractors'=>2016, 'site'=>950, 'online'=>951, 'sex'=>936,
                        'directory'=>2017, 'kitchen'=>2018, 'land'=>2019, 'technology'=>2020, 'today'=>2021, 'diamonds'=>2022, 
                        'enterprises'=>2023, 'tips'=>2024, 'voyage'=>2025, 'careers'=>2026, 'photos'=>2027, 
                        'recipes'=>2028, 'shoes'=>2029, 'cab'=>2030, 'company'=>2031, 'domains'=>2032, 'limo'=>2033, 
                        'academy'=>2034, 'center'=>2035, 'computer'=>2036, 'management'=>2037, 'systems'=>2038, 
                        'builders'=>2039, 'email'=>2040, 'solutions'=>2041, 'support'=>2042, 'training'=>2043, 'camp'=>2044, 
                        'education'=>2045, 'glass'=>2046, 'institute'=>2047, 'repair'=>2048, 'coffee'=>2049, 'florist'=>2050, 
                        'house'=>2051, 'international'=>2052, 'solar'=>2053, 'marketing'=>2054, 'viajes'=>2055, 'farm'=>2056, 
                        'codes'=>2057, 'cheap'=>2058, 'zone'=>2059, 'agency'=>2060, 'bargains'=>2061, 'boutique'=>2062, 
                        'cool'=>2063, 'watch'=>2064, 'works'=>2065, 'expert'=>2066, 'foundation'=>2067, 'exposed'=>2068, 
                        'villas'=>2069, 'flights'=>2070, 'rentals'=>2071, 'cruises'=>2072, 'vacations'=>2073, 'condos'=>2074, 
                        'properties'=>2075, 'maison'=>2076, 'tienda'=>2077, 'dating'=>2078, 'events'=>2079, 'partners'=>2080, 
                        'productions'=>2081, 'community'=>2082, 'catering'=>2083, 'cards'=>2084, 'cleaning'=>2085, 'tools'=>2086, 
                        'industries'=>2087, 'parts'=>2088, 'supplies'=>2089, 'supply'=>2090, 'report'=>2091, 'vision'=>2092, 
                        'fish'=>2093, 'services'=>2094, 'capital'=>2095, 'engineering'=>2096, 'exchange'=>2097, 'gripe'=>2098, 
                        'associates'=>2099, 'lease'=>2100, 'media'=>2101, 'pictures'=>2102, 'reisen'=>2103, 'toys'=>2104, 
                        'university'=>2105, 'town'=>2106, 'wtf'=>2107, 'fail'=>2108, 'financial'=>2109, 'limited'=>2110, 
                        'care'=>2111, 'clinic'=>2112, 'surgery'=>2113, 'dental'=>2114, 'tax'=>2115, 'cash'=>2116, 
                        'fund'=>2117, 'investments'=>2118, 'furniture'=>2119, 'discount'=>2120, 'fitness'=>2121, 'schule'=>2122, 
                        'sexy'=>2500, 'tattoo'=>2501, 'link'=>2502, 'guitars'=>2503, 'gift'=>2504, 'pics'=>2505, 'photo'=>2506, 
                        'christmas'=>2507, 'blackfriday'=>2508, 'hiphop'=>2509, 'juegos'=>2510, 'audio'=>2511, 'click'=>2512, 
                        'hosting'=>2513, 'property'=>2514, 'top'=>770, 'porn'=>932, 'adult'=>934, 'city'=>2129, 'ceo'=>742];                    
        if(isset($domaintypes[strtolower($ext)]))
        {
            return $domaintypes[strtolower($ext)];
        }
        return "unknown";
    }
    function getTCPClient()
    {

    }
    function getTESTClient($address, $port)
    {
        $client = stream_socket_client("$address:$port", $errno, $errorMessage);
        if ($client === false) {
            throw new UnexpectedValueException("Failed to connect: $errorMessage");
        }
        return $client;
    }
    function uniqueInvoiceID()
    {
        $legend = "0123456789";
        $len = 22;
        $result = "";
        for($i=0;$i<$len;$i++)
        {
            $result .= $legend[rand(0,9)];
        }
        return "client".$result;
    }
    function precho($s)
    {
        echo "<pre>";
        echo $s;
        echo "</pre>";
    }
    $clid = "135610";//OnlineNic's test account username
    $clpass = "654123";//OnlineNic's test account password
    $cltrid = uniqueInvoiceID();//Client Record ID/Invoice
    echo $cltrid."<br><br>";
    //md5(clid + md5(clpass) + cltrid + “login”)
    $checkSumLogin = md5($clid.md5($clpass).$cltrid."login");//Checksum
    $checkSumLogout = md5($clid.md5($clpass).$cltrid."logout");//Checksum
    $loginRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
                        <request>
                            <category>client</category>
                            <action>Login</action>
                            <params>
                                <param name=\"clid\">$clid</param>
                            </params>
                            <cltrid>$cltrid</cltrid>
                            <chksum>$checkSumLogin</chksum>
                        </request>";
    $logoutRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
                        <request>
                            <category>client</category>
                            <action>Logout</action>
                            <params>
                                <param name=\"clid\">$clid</param>
                            </params>
                            <cltrid>$cltrid</cltrid>
                            <chksum>$checkSumLogout</chksum>
                        </request>";
    $checkDomain = "somedomain.com";
    $ext = "com";
    $dType = getDomainType($ext);
    $requestID = uniqueInvoiceID();
    $domainAvailChecksum = md5($clid.md5($clpass).$requestID."checkdomain".$dType.$checkDomain);
    $domainAvailableRequest = "<?xml version=\"1.0\"?>
                                <request>
                                    <category>domain</category>
                                    <action>CheckDomain</action>
                                    <params>
                                        <param name=\"domaintype\">$dType</param>
                                        <param name=\"domain\">$checkDomain</param>
                                    </params>
                                    <cltrid>$requestID</cltrid>
                                    <chksum>$domainAvailChecksum</chksum>
                                </request>";
    $client = getTESTClient("tcp://ote.onlinenic.com", "30009");
    fwrite($client, $loginRequest);
    precho(htmlspecialchars(stream_get_contents($client)));
    fwrite($client, $domainAvailableRequest);
    precho(htmlspecialchars(stream_get_contents($client)));
    fwrite($client, $logoutRequest);
    precho(htmlspecialchars(stream_get_contents($client)));
    fclose($client);          
?>

1 个答案:

答案 0 :(得分:2)

stream_get_contents的第二个参数是$maxlength,即要读取的最大字节数。如果未设置(或传递默认值-1),则会读取整个流,直到流的末尾

这意味着stream_get_contents将继续读取流中的所有数据,直到流不被另一端关闭为止。 (stream_get_contents不知道你的协议的细节,所以它不能知道它“应该”在服务器的“一个响应”之后停止阅读;它没有任何关于甚至意味着什么的概念。)

这意味着,如果您的TCP连接上有查询响应协议,则必须stream_get_contents() 完全您要接收的数据长度(如果您确切知道的话)响应大小将是什么),或者你必须从服务器读取数据并自己处理它。

你究竟是怎么做的,这在很大程度上取决于你的协议。如果它是面向行的,您可以使用fgets($client)一次检索一行。如果是二进制协议,则可以使用stream_get_contents($client, $sizeOfYourPacket)


由于您有一个基于XML的协议,您可能希望创建一个新函数来处理读取响应并理解XML,以便它知道要读取多少数据以及何时可以将响应(可能已经处理)发送回你的申请。

如果没有凭据,就很难看出服务器的确切行为,所以除此之外,您还需要进行实验。