Backends PHP

PHP 스크립트에서의 HTTP 통신 #2 – fsockopen

fsockopen 소개

PHP의 fsockopen 함수는 소켓 통신을 위한 간단하고 유용하며 특이한 함수입니다. 호스트명과 포트 번호를 입력받은 fsockopen 함수의 반환값은 재미있게도 파일 포인터입니다. 이후부터는 fwrite와 fread 등의 함수로 마치 파일입출력을 하듯이 통신을 할 수 있습니다.

물론 fsockopen은 좀더 많은 프로토콜에 범용적으로 사용하는 만큼, 앞서 소개한 cURL에 비해 무척 번거롭습니다. fsockopen으로 통신을 하기 위해서는 반환받은 파일 포인터에 fwrite로 보낼 데이터를 프로토콜 스펙에 따라 정확하게 입력해야 하기 때문입니다. 스펙과 글자 하나라도 달라지면 응답을 받을 수 없는 것은 물론입니다.

HTTP 스펙에 대해서는 W3C에 공식문서가 있습니다만, 필요한 내용을 발췌독하기 어렵기 때문에 PHP 매뉴얼에 세계의 여러 개발자들이 올려놓은 예제들을 참고하는 것이 빠르게 문제를 해결하는 데에 더 큰 도움이 됩니다.

 

fsockopen으로 작성된 HTTP 통신 함수

fsockopen을 사용하여 코드를 작성하면 프로토콜 스펙이 투명하게 드러나는 장점은 있지만, cURL 라이브러리에서 curl_opt 함수가 해주는 여러가지 조건과 분기, 예외처리를 스스로 해야 하므로 코드는 복잡하고 더 많은 신경을 써야 합니다. 아래는 1편의 cURL을 이용한 getFromUrl() 함수와 동일한 기능을 하는 함수를 fsockopen으로 작성한 것입니다.

function getFromUrl($url, $method = 'GET')
{	
	// Initialize
	$info	= parse_url($url);
	$req	= '';
	$data	= '';
	$line	= '';
	$agent	= 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)';
	$linebreak	= "\r\n";
	$headPassed	= false;

	// Setting Protocol
	switch($info['scheme'] = strtoupper($info['scheme']))
	{
		case 'HTTP':
			$info['port']	= 80;
			break;

		case 'HTTPS':
			$info['ssl']	= 'ssl://';
			$info['port']	= 443;
			break;

		default:
			return false;
	}

	// Setting Path
	if(!$info['path'])
	{
		$info['path'] = '/';
	}

	// Setting Request Header
	switch($method = strtoupper($method))
	{
		case 'GET':
			if($info['query'])
			{
				$info['path'] .= '?' . $info['query'];
			}

			$req .= 'GET ' . $info['path'] . ' HTTP/1.1' . $linebreak;
			$req .= 'Host: ' . $info['host'] . $linebreak;
			$req .= 'User-Agent: ' . $agent . $linebreak;
			$req .= 'Referer: ' . $url . $linebreak;
			$req .= 'Connection: Close' . $linebreak . $linebreak;
			break;

		case 'POST':
			$req .= 'POST ' . $info['path'] . ' HTTP/1.1' . $linebreak;
			$req .= 'Host: ' . $info['host'] . $linebreak;
			$req .= 'User-Agent: ' . $agent . $linebreak; 
			$req .= 'Referer: ' . $url . $linebreak;
			$req .= 'Content-Type: application/x-www-form-urlencoded'.$linebreak; 
			$req .= 'Content-Length: '. strlen($info['query']) . $linebreak;
			$req .= 'Connection: Close' . $linebreak . $linebreak;
			$req .= $info['query']; 
			break;
	}

	// Socket Open
	$fsock	= @fsockopen($info['ssl'] . $info['host'], $info['port']);
	if ($fsock)
	{
		fwrite($fsock, $req);
		while(!feof($fsock))
		{
			$line = fgets($fsock, 128);
			if($line == "\r\n" && !$headPassed)
			{
				$headPassed = true;
				continue;
			}
			if($headPassed)
			{
				$data .= $line;
			}
		}
		fclose($fsock);
	}

	return $data;
}

핵심은 fsockopen 함수로부터 반환받은 파일포인터를 가지고, fwrite로 $req에 저장해두었던 HTTP Request Header를 전송하고, fgets로 HTTP Response를 전송받는 것입니다. 이 때 Response Header와 Response Body를 함께 전송받게 되므로, Response Header가 모두 전송된 후에는 $headPassed 값을 true로 변경하여 Response Body만 따로 반환할 수 있도록 하였습니다.

함수의 파라메터는 앞서와 동일합니다만, 이쪽은 코드를 작성하던 당시에 HTTPS에도 대응해야 할 필요가 있어 HTTPS에서도 사용이 가능합니다.

4 thoughts on “PHP 스크립트에서의 HTTP 통신 #2 – fsockopen”

  1. 정말 감사합니다.
    어제부터 씨름하던걸 한번에 해결했습니다.

    1. 안녕하세요. 제 글이 도움이 되어서 다행입니다. 앞으로도 좋은 글로 찾아뵙겠습니다.

Leave a Reply to epiloum Cancel reply

Your email address will not be published. Required fields are marked *