Skip to Content
Course content

115: fsockopen for Raw Socket Connections

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this happen more times than I can count: a developer wants to do something "low level"—maybe they're tired of curl or file_get_contents and want to talk directly to a server—so they reach for fsockopen. It feels powerful because you're handling the raw stream, but that power comes with a catch: you are now responsible for the protocol's punctuation.

Take a look at this snippet. This is a typical attempt to fetch a page from a web server using a raw socket.

$fp = fsockopen("example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "Error: $errstr ($errno)";
} else {
    $out = "GET / HTTP/1.1\n";
    $out .= "Host: example.com\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}

If you run this, you'll likely find that the script just... sits there. It hangs until the timeout hits, and you get nothing back. It looks like the server is ignoring you, but the server isn't the problem here. You are.

The Hanging Request: Missing Carriage Returns

The issue is that \n (a newline) is not the same thing as a "line ending" in the world of network protocols. Most raw socket protocols—HTTP, SMTP, POP3—follow the RFC standards that require CRLF (Carriage Return and Line Feed), which is \r\n.

When you send just \n, the server is still waiting for the rest of the line. It thinks you're still typing your request. Even worse, the HTTP protocol specifically requires a blank line (an extra CRLF) to signal that the headers are finished and the server should now process the request. In the code above, we never sent that final blank line, so the server is just sitting there patiently, waiting for you to finish your thought.

Closing the Header Block with CRLF

To fix this, we need to be explicit about our line endings and ensure we terminate the header section. I usually define a constant or a variable for the CRLF to keep the code readable, because typing \r\n everywhere gets tedious and error-prone.

$fp = fsockopen("example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    die("Socket Error: $errstr");
}

$crlf = "\r\n";
// We must use \r\n for every line, AND add an extra one at the end.
$out = "GET / HTTP/1.1" . $crlf;
$out .= "Host: example.com" . $crlf;
$out .= "Connection: Close" . $crlf; 
$out .= $crlf; // This empty line is the "magic" that tells the server we're done.

fwrite($fp, $out);

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

Now, the server sees the \r\n\r\n sequence and knows exactly when to stop listening and start responding. I also added Connection: Close. Without this, some modern servers keep the socket open (Keep-Alive), which would cause our while(!feof($fp)) loop to hang again because the server is waiting for us to send another request.

Managing Timeouts and Blocking

When you're working with fsockopen, you're dealing with a blocking stream by default. If the remote server is sluggish or a firewall drops your packets, your PHP script will freeze. While the fifth parameter of fsockopen handles the connection timeout, it doesn't handle the read/write timeout.

If you're building something that needs to be resilient, you should use stream_set_timeout immediately after the connection is established. This prevents a slow peer from locking up your entire application process.

$fp = fsockopen("example.com", 80, $errno, $errstr, 5);
if ($fp) {
    // Set a 2-second timeout for reading from the socket
    stream_set_timeout($fp, 2);
    
    fwrite($fp, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
    $response = fgets($fp, 128);
    
    // Check if the read timed out
    $info = stream_get_meta_data($fp);
    if ($info['timed_out']) {
        echo "The server took too long to respond.";
    }
}

Using stream_get_meta_data is the only reliable way to tell the difference between a server that sent an empty response and a server that simply timed out.




📋 Practical Task

Exercise: Build an SMTP Greeting Checker

Your task is to create a script that checks if a mail server is responding correctly. You don't need to send a full email, just perform the initial "handshake" to verify the server is alive and speaking the SMTP protocol.

Requirements:

  • Use fsockopen to connect to smtp.gmail.com on port 587 (or any other public SMTP server you have access to).
  • Set a connection timeout of 5 seconds and a stream read timeout of 3 seconds.
  • Read the first line of the response from the server using fgets.
  • The server should respond with a code starting with 220 (e.g., "220 smtp.gmail.com ESMTP...").
  • Print "Server is ready" if the response starts with 220, and "Server error or timeout" otherwise.
  • Ensure you properly close the socket connection.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.