오늘은 소켓을 사용하여 GET 및 POST 요청을 보내는 방법을 보여 드리겠습니다. 저는 Yan Shiba 선생님이 캡슐화한 Http 클래스를 사용하여 설명하겠습니다.
일상적인 프로그래밍에서는 저와 같은 많은 사람들이 대부분의 경우 브라우저를 사용하여 서버에 GET 및 POST 요청을 한다고 생각합니다. 그러면 다른 방법을 사용하여 GET 및 POST 요청을 할 수 있나요? 대답은 '예'여야 합니다. HTTP 프로토콜을 아는 사람은 요청을 제출하는 브라우저의 본질이 요청 정보를 서버에 보내는 것임을 알고 있습니다. 이 요청 정보는 요청 라인, 요청 헤더 및 요청 본문(선택 사항)으로 구성됩니다. 서버는 요청 정보를 기반으로 응답 정보를 반환합니다. 연결이 끊어졌습니다.
HTTP 요청 형식은 다음과 같습니다.
<request-line> <headers> <blank line> [<request-body>]
HTTP 응답 형식은 요청 형식과 매우 유사합니다.
<status-line> <headers> <blank line> [<response-body>]
HTTP의 원리를 사용하여 요청을 보낼 수 있으며 소켓을 사용하여 HTTP 요청을 보내는 것을 다시 고려할 수 있습니다.
소켓(Socket)의 원래 영어 의미는 "구멍" 또는 "소켓"입니다. 일반적으로 "소켓"이라고도 하며 IP 주소와 포트를 설명하는 데 사용됩니다. 이는 통신 체인에 대한 핸들이며 서로 다른 가상 머신 또는 서로 다른 컴퓨터 간의 통신을 구현하는 데 사용할 수 있습니다. 인터넷상의 호스트는 일반적으로 여러 서비스 소프트웨어를 실행하고 동시에 여러 서비스를 제공합니다. 각 서비스는 소켓을 열고 포트에 바인딩됩니다. 다른 포트는 다른 서비스에 해당합니다. 이러한 관점에서 볼 때 실제로 소켓을 사용하여 원격 파일을 작동하는 것은 로컬 파일을 읽고 쓰는 것만큼 쉽습니다. 로컬 파일은 하드웨어를 통해 전송되고, 원격 파일은 네트워크 케이블을 통해 전송되는 것으로 생각하십시오.
따라서 요청을 보내는 것은 연결 설정 -> 소켓 인터페이스 열기(fsockopen()) -> 쓰기 요청(fwrite()) -> 응답 읽기(fread() -> 파일 닫기로 간주할 수 있습니다. (fclose()) 더 이상 고민하지 않고 바로 코드로 넘어가겠습니다.
<?php interface Proto { // 连接url function conn($url); //发送get查询 function get(); // 发送post查询 function post(); // 关闭连接 function close(); } class Http implements Proto { const CRLF = "\r\n"; protected $errno = -1; protected $errstr = ''; protected $response = ''; protected $url = null; protected $version = 'HTTP/1.1'; protected $fh = null; protected $line = array(); protected $header = array(); protected $body = array(); public function __construct($url) { $this->conn($url); $this->setHeader('Host: ' . $this->url['host']); } // 此方法负责写请求行 protected function setLine($method) { $this->line[0] = $method . ' ' . $this->url['path'] . '?' .$this->url['query'] . ' '. $this->version; } // 此方法负责写头信息 public function setHeader($headerline) { $this->header[] = $headerline; } // 此方法负责写主体信息 protected function setBody($body) { $this->body[] = http_build_query($body); } // 连接url public function conn($url) { $this->url = parse_url($url); // 判断端口 if(!isset($this->url['port'])) { $this->url['port'] = 80; } // 判断query if(!isset($this->url['query'])) { $this->url['query'] = ''; } $this->fh = fsockopen($this->url['host'],$this->url['port'],$this->errno,$this->errstr,3); } //构造get请求的数据 public function get() { $this->setLine('GET'); $this->request(); return $this->response; } // 构造post查询的数据 public function post($body = array()) { $this->setLine('POST'); // 设计content-type $this->setHeader('Content-type: application/x-www-form-urlencoded'); // 设计主体信息,比GET不一样的地方 $this->setBody($body); // 计算content-length $this->setHeader('Content-length: ' . strlen($this->body[0])); $this->request(); return $this->response; } // 真正请求 public function request() { // 把请求行,头信息,实体信息 放在一个数组里,便于拼接 $req = array_merge($this->line,$this->header,array(''),$this->body,array('')); //print_r($req); $req = implode(self::CRLF,$req); //echo $req; exit; fwrite($this->fh,$req); while(!feof($this->fh)) { $this->response .= fread($this->fh,1024); } $this->close(); // 关闭连接 } // 关闭连接 public function close() { fclose($this->fh); } }
간단한 GET 요청을 보내려면 이 클래스를 사용하세요.
<?php //记得引用Http类 $url="http://home.jb51.net/u/DeanChopper/"; $http=new Http($url); $response=$http->get(); print_r($response);
반환 값은 정보입니다. 원하는 콘텐츠를 얻기 위해 응답 정보를 추가로 처리할 수 있습니다.
다음 구체적인 예를 살펴보겠습니다
<?php /** * 使用PHP Socket 编程模拟Http post和get请求 * @author koma */ class Http{ private $sp = "\r\n"; //这里必须要写成双引号 private $protocol = 'HTTP/1.1'; private $requestLine = ""; private $requestHeader = ""; private $requestBody = ""; private $requestInfo = ""; private $fp = null; private $urlinfo = null; private $header = array(); private $body = ""; private $responseInfo = ""; private static $http = null; //Http对象单例 private function __construct() {} public static function create() { if ( self::$http === null ) { self::$http = new Http(); } return self::$http; } public function init($url) { $this->parseurl($url); $this->header['Host'] = $this->urlinfo['host']; return $this; } public function get($header = array()) { $this->header = array_merge($this->header, $header); return $this->request('GET'); } public function post($header = array(), $body = array()) { $this->header = array_merge($this->header, $header); if ( !empty($body) ) { $this->body = http_build_query($body); $this->header['Content-Type'] = 'application/x-www-form-urlencoded'; $this->header['Content-Length'] = strlen($this->body); } return $this->request('POST'); } private function request($method) { $header = ""; $this->requestLine = $method.' '.$this->urlinfo['path'].'?'.$this->urlinfo['query'].' '.$this->protocol; foreach ( $this->header as $key => $value ) { $header .= $header == "" ? $key.':'.$value : $this->sp.$key.':'.$value; } $this->requestHeader = $header.$this->sp.$this->sp; $this->requestInfo = $this->requestLine.$this->sp.$this->requestHeader; if ( $this->body != "" ) { $this->requestInfo .= $this->body; } /* * 注意:这里的fsockopen中的url参数形式为"www.xxx.com" * 不能够带"http://"这种 */ $port = isset($this->urlinfo['port']) ? isset($this->urlinfo['port']) : '80'; $this->fp = fsockopen($this->urlinfo['host'], $port, $errno, $errstr); if ( !$this->fp ) { echo $errstr.'('.$errno.')'; return false; } if ( fwrite($this->fp, $this->requestInfo) ) { $str = ""; while ( !feof($this->fp) ) { $str .= fread($this->fp, 1024); } $this->responseInfo = $str; } fclose($this->fp); return $this->responseInfo; } private function parseurl($url) { $this->urlinfo = parse_url($url); } } // $url = "http://news.163.com/14/1102/01/AA0PFA7Q00014AED.html"; $url = "http://localhost/httppro/post.php"; $http = Http::create()->init($url); /* 发送get请求 echo $http->get(array( 'User-Agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36', )); */ /* 发送post请求 */ echo $http->post(array( 'User-Agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36', ), array('username'=>'发一个中文', 'age'=>22));