PHP socket,server,cient:模拟 post

来源:互联网 发布:淘宝里的花呗怎么关闭 编辑:程序博客网 时间:2024/06/07 14:57
fsockopen
以socket方式打开一个连接,最常用的是模拟post。模拟get方式,直接用 file_get_content就行了.代码见后。

stream_socket_client, 代码见后。这个和fsockopenfsockopen一样.以socket方式打开一个连接,只是参数不同

stream_socket_server
建立一个socket server端, 代码见后。
如果是建立的是tcp的server 就用stream_socket_accept进行通讯
如果是建立的是udp的server 就用stream_socket_recvfrom和stream_socket_sendto进行通讯,而且stream_socket_server中需要加个参数STREAM_SERVER_BIND

还有个socket扩展,这个是最基础的建立socket,但是从5.3.0开始就放到pecl中了.

---------- stream_socket client-------------------

〈?php
/**
 * @name test.php
 * @date Sun Jan 27 00:49:00 CST 2008
 * @copyright 马永占(MyZ)
 * @author 马永占(MyZ)
 * @link http://blog.csdn.net/mayongzhan/
 */

$xport = “tcp“;
$port = “8001“;
$ip = “127.0.0.1“;
$address = “{$xport}://{$ip}:{$port}“;
$fp = stream_socket_client($address, $errno, $errstr, 1);
if (!$fp) {
    echo “$errstr ($errno)〈br /〉/n“;
} else {
    fwrite($fp, “/n“);
    echo fread($fp, 1024);
    fclose($fp);
}
?〉

----------------- streamSocketServer------------------

〈?php
/**
 * @name test2.php
 * @date Sun Jan 27 00:45:57 CST 2008
 * @copyright 马永占(MyZ)
 * @author 马永占(MyZ)
 * @link http://blog.csdn.net/mayongzhan/
 */
header(“Content-type: text/html;charset=utf-8“);
//设置不超时.服务端当然不能超时
set_time_limit(0);
//得到可用socket
$xportlist = stream_get_transports();
echo “transports:/n“;
foreach ($xportlist as $value) {
 echo “{$value}/n“;
}
//定义一些东西
$xport = “tcp“;
$port = “8001“;
$address = “{$xport}://0.0.0.0:{$port}“;
//建立socketserver
if ($xport==’tcp’) {
 $socket = stream_socket_server($address, $errno, $errstr);
}
elseif ($xport==’udp’) {
 $socket = stream_socket_server($address, $errno, $errstr, STREAM_SERVER_BIND);
}
if (!$socket) {
 echo “{$errstr} ({$errno})/n“;
}
else {
 echo “listening {$xport}:{$port} .../n“;
 if ($xport==’tcp’) {
  //许可一个socket连接,-1超时
  while ($conn = stream_socket_accept($socket,-1)) {
   //得到访问的端口
   $peer = stream_socket_get_name($conn,true);
   echo “$peer/n“;
      fwrite($conn, ’The local time is ’ . date(“Y-m-d H:i:s/n“));
      fclose($conn);
  }
 }
 elseif ($xport==’udp’) {
  do {
   //得到访问的端口
      $pkt = stream_socket_recvfrom($socket, 1, 0, $peer);
      echo “$peer/n“;
      stream_socket_sendto($socket, date(“Y-m-d H:i:s/n“), 0, $peer);
  } while ($pkt !== false);
 }
 //关闭socket
 fclose($socket);
}
?〉

------------------ fsockopen(post)-----------------

〈?php
/**
 * @name test.php
 * @date Sat Jan 26 23:01:23 CST 2008
 * @copyright 马永占(MyZ)
 * @author 马永占(MyZ)
 * @link http://blog.csdn.net/mayongzhan/
 */

/**
 * php 发送POST请求
 *
 * @param string $url 提交到的地址
 * @param array $data 要提交的参数 array(’a’=〉’’,’b’=〉’’);
 * @return string
 */
function virtualPost($url, $data) {
 $url = parse_url($url);
 if (!$url) return “URL不能解析“;
 if (!isset($url[’port’]))  $url[’port’] = ““;
 if (!isset($url[’query’])) $url[’query’] = ““;
 $encoded = ““;
 while (list($k,$v) = each($data)) {
  $encoded .= ($encoded ? “&“ : ““);
  $encoded .= rawurlencode($k).“=“.rawurlencode($v);
 }
 //$fp = stream_socket_client($url[’host’].“:“.($url[’port’] ? $url[’port’] : 80));
 $fp = fsockopen($url[’host’], $url[’port’] ? $url[’port’] : 80);
 if (!$fp) return “不能打开到$url[host]的连接“;
 //发送
 fputs($fp, sprintf(“POST %s%s%s HTTP/1.0/n“, $url[’path’], $url[’query’] ? “?“ : ““, $url[’query’]));
 fputs($fp, “Host: $url[host]/n“);
 fputs($fp, “Content-type: application/x-www-form-urlencoded/n“);
 fputs($fp, “Content-length: “ . strlen($encoded) . “/n“);
 fputs($fp, “Connection: close/n/n“);
 fputs($fp, “$encoded/n“);
 //接受
 $line = fgets($fp,1024);
 if (!eregi(“^HTTP/1/.. 200“, $line)) return “返回结果错误“;
 //滤掉空行
 $results = ““;
 $inheader = 1;
 while(!feof($fp)) {
  $line = fgets($fp,1024);
  //把剩余的头信息过滤掉
  if ($inheader && ($line == “/n“ || $line == “/r/n“)) {
   $inheader = 0;
  }elseif (!$inheader) {
   $results .= $line;
  }
 }
 fclose($fp);
 return $results;
}
echo virtualPost(’http://127.0.0.1/test/test2.php’,array(myz=〉’马永占’));

?〉
--------------------------------------
  <?php  
  error_reporting   (E_ALL);    
   
  $service_port   =   3456;  
  $address   =   "10.3.10.99";  
   
  $socket   =   socket_create   (AF_INET,   SOCK_STREAM,   0);    
  if   ($socket   <   0)  
  {    
          echo   "socket_create()   failed:   reason:   "   .   socket_strerror   ($socket)   .   "/n";    
          exit();  
  }  
  else  
  {    
          echo   "Attempting   to   connect   to   '$address'   on   port   '$service_port'...".   "/n";    
          $result   =   socket_connect   ($socket,   $address,   $service_port);    
          if   ($result   ==   FALSE   )    
          {  
                  echo   "socket_connect()   failed./nReason:   ($result)   "   .   socket_strerror($result)   .   "/n";    
                  exit();  
          }    
          else    
          {                  
                  $send   =   '';  
                  //WORD wLength; //   数据区有效长度,从数据区aucData[0]开始  
                  $send   =   $send   .   pack("L",44);  
                  //UCHAR aucResv[TRANSFRAME_RESV_LENGTH]; //   保留  
                  $send   =   $send   .   pack("L",0);          
                  //_UL           ulSIP;                       源IP地址,业务不需要填写  
                  $send   =   $send   .   pack("L",0);  
                  //_UL           ulDIP;                       //   目的IP地址,业务不需要填写  
                  $send   =   $send   .   pack("L",0);  
                  //_US           usDProcID;               //   目的进程ID  
                  $send   =   $send   .   pack("S",88);  
                  //_US           usDProcHandle;       //   目的进程Handle,   当一个进程启动多个实例时需要判断  
                  $send   =   $send   .   pack("S",89);  
                  //_UL           ulDCmdHandle;         //   目的命令句柄,标识一个模块内发送的命令标识  
                  $send   =   $send   .   pack("L",90);  
                  //_US           usSProcID;               //   源端进程ID  
                  $send   =   $send   .   pack("S",91);  
                  //_US           usSProcHandle;       //   源端进程Handle,   当一个进程启动多个实例时需要判断  
                  $send   =   $send   .   pack("S",92);  
                  //_UL           ulSCmdHandle;         //   源命令句柄,标识一个模块内发送的命令标识  
                  $send   =   $send   .   pack("L",93);  
                  //_US           usMsgVer;                 //   消息版本号  
                  $send   =   $send   .   pack("S",94);  
                  //_US           usMsgType;               //   消息类型,具体类型附后  
                  $send   =   $send   .   pack("S",95);  
                  //_UL           ulCmdCode;               //   命令码  
                  $send   =   $send   .   pack("L",96);  
                  //_UC           ucReserve[8];         //   保留  
                  $send   =   $send   .   pack("L",0);  
                  //_UL           ulRetCode;               //   交互返回码,   应用使用,   通讯无关  
                  $send   =   $send   .   pack("L",0);  
                  //_UL           ulLength;                 //   后附数据区长度  
                  $send   =   $send   .   pack("L",0);                  
                   
                  $crc   =   0xFFFF;  
                  for   ($x   =   0;   $x   <   strlen   ($send);   $x++)    
                  {  
                          $crc   =   $crc   ^   ord($send[$x]);  
                          for   ($y   =   0;   $y   <   8;   $y++)    
                          {  
                                  if   (($crc   &   0x0001)   ==   0x0001)    
                                  {  
                                          $crc   =   (($crc   >>   1)   ^   0xA001);  
                                  }    
                                  else    
                                  {   $crc   =   $crc   >>   1;   }  
                          }  
                  }  
                   
                  $Realsend   =   '';  
                  //DWORD dwHeaderFlag; //   帧的起始标志  
                  $Realsend   =   $Realsend   .   pack("L",0xDEADDEAD);  
                  //WORD wCrcCode; //   CRC校验码,从长度字节wLength开始  
                  $Realsend   =   $Realsend   .   pack("L",$crc);  
                  $Realsend   =   $Realsend   .   $send;                  
                                   
                  echo   "the   length   is";  
                  echo   strlen($Realsend);  
                  echo   "the   message   is";  
                  echo   $Realsend;  
                   
                  echo   "Sending   Msg...";    
                  socket_write   ($socket,   $Realsend,   strlen   ($Realsend));  
                   
                  $out   ='';  
                   
                  echo   "Reading   response:/n/n";  
                  $time   =   time();  
                  $PP=TRUE;                  
                   
                  socket_set_nonblock($socket);  
                   
                  echo   time();    
                  while($PP)    
                  {  
                          $out   =socket_read($socket,8);  
                           
                          if   ((time()   -   $time)   >=   10   )  
                          {  
                                  echo   "Time   out   ,Closing   socket...";  
                                  $PP   =   FALSE;  
                                  //socket_close($socket);      
                          }  
                          else  
                          {  
                                  sleep(1);  
                                  continue;  
                          }  
                           
                  }  
                  echo   time();  
   
                  echo   "Closing   socket...";  
                  socket_close($socket);  
          }  
  }  
   
  ?>  
----------------------------------------------------------------------
看看下面的文章比较好  
  PHP套接字编程  
  1.   理解套接字  
          Mail、ftp、telnet、name和finger这些服务都是在一个专用的公开的端口上提供的,通过连接到这些端口,客户程序就能够访问这些服务。这与现实生活是相似的——当需要干洗衣服的时候,找干洗店;当需要取钱的时候,去银行,等等。除了专用于特定服务器的端口外,计算机还有其它的端口让程序员创建他们自己的服务器。  
  端口一般是编号的,通过指定服务器的端口号,客户程序可以连接到该端口上。每种服务器或端口要有特定的协议,为了让客户的请求能够被理解和响应,客户必须以这种服务器特有的方式形成客户请求。  
  Socket是网络上运行的两个程序间双向通信连接的一端。Socket这个词的一般意义是自然的或人工的插口,如家用电器的电源插口等。  
  客户程序可以向Socket写请求,服务器将处理此请求,然后通过Socket把结果返回给客户。  
  Socket是一种底层连接。客户机和服务器通过写入到Socket的字节流进行通信。它们必须有共同的协议,也就是说,通过Socket相互传送信息时所用的语言必须是协定好的。  
   
  2.   Socket建立连接的过程  
  建立过程如下:(connection-oriented)  
  server   方过程   client   方过程  
   
  socket()   socket()  
  |   |  
  bind()   bind()  
  |   |  
  listen()   |  
  |   |  
  accept()<------------------connect()  
  |   |  
  recv()/send()   <---------->   send()/recv()  
   
  3.   Php   基本套接字调用:  
  3.1.   基本套接字调用  
  创建套接字--socket();  
  绑定本机端口--bind();  
  建立连接--connect(),accept();  
  侦听端口--listen();  
  数据传输--send(),recv();  
  输入/输出多路复用--select();  
  关闭套接字--closesocket()  
  3.2.   php提供的套接字调用:  
  接受连接-—accept   connect()    
  绑定端口—bind   ()  
  关闭套接字—close()  
  初始化连接—connect()  
  侦听端口—listen()  
  读取套接字—read()  
  创建套接字—socket()  
  写套接字—write()  
   
  4.   基本应用  
  4.1.   一个简单的TCP服务器  
  1   #!/usr/local/bin/php   -q  
  2    
  3   <?php  
  4   /*  
  5   *   We   don't   want   any   time-limit   for   how   the   long   can   hang  
  6   *   around,   waiting   for   connections:  
  7   */  
  8   set_time_limit(0);  
  9    
  10   /*   Create   a   new   socket:   */  
  11   if(   ($sock   =   socket(   AF_INET,   SOCK_STREAM,   0   ))   <   0   )  
  12   {  
  13   print   strerror(   $sock   )   .   "n";  
  14   exit(1);  
  15   }  
  16    
  17   /*   Bind   the   socket   to   an   address   and   a   port:   */  
  18   if(   ($ret   =   bind(   $sock,   "10.31.172.77",   10000   ))   <   0   )  
  19   {  
  20   print   strerror(   $ret   )   .   "n";  
  21   exit(1);  
  22   }  
  23    
  24   /*  
  25   *   Listen   for   incoming   connections   on   $sock.  
  26   *   The   '5'   means   that   we   allow   5   queued   connections.  
  27   */  
  28   if(   ($ret   =   listen(   $sock,   5   ))   <   0   )  
  29   {  
  30   print   strerror(   $ret   )   .   "n";  
  31   }  
  32    
  33   /*   Accept   incoming   connections:   */  
  34   if(   ($msgsock   =   accept_connect(   $sock   ))   <   0)  
  35   {  
  36   print   strerror(   $msgsock   )   .   "n";  
  37   exit(1);    
  38   }  
  39    
  40   /*   Send   the   welcome-message:   */  
  41   $message   =   "Welcome   to   my   TCP-server!n";  
  42   if(   ($ret   =   write(   $msgsock,   $message,   strlen($message))   )   <   0   )  
  43   {  
  44   print   strerror(   $msgsock   )   .   "n";  
  45   exit(1);  
  46   }  
  47    
  48   /*   Read/Receive   some   data   from   the   client:   */    
  49   $buf   =   '';  
  50   if(   ($ret   =   read(   $msgsock,   $buf,   128   ))   <   0   )  
  51   {  
  52   print   strerror(   $ret   )   .   "n";  
  53   exit(1);  
  54   }  
  55    
  56   /*   Echo   the   received   data   back   to   the   client:   */  
  57   if(   ($ret   =   write(   $msgsock,   "You   said:   $bufn",   strlen("You   said:   $bufn"))   )   <   0   )  
  58   {  
  59   print   strerror(   $ret   )   .   "n";  
  60   exit(1);  
  61   }  
  62    
  63   /*   Close   the   communication-socket:   */    
  64   close(   $msgsock   );  
  65    
  66   /*   Close   the   global   socket:   */  
  67   close(   $sock   );  
  68   ?>  
   
  第8行:使用set_time_limit设定程序执行时间为无限以等待连接;  
  11-15:   创建一个套接字;  
  18-22:   把创建的套接字与IP及端口绑定;  
  28-31:   侦听端口;  
  34-38:   接受连接;  
  41-46:   显示欢迎信息;  
  49-54:   读取客户端信息;  
  57-61:   向客户端回显信息;  
  63-67:   关闭套接字  
  4.2.   TCP服务器的运行  
  上边这个tcp服务器的运行要求php编译成cgi解释方式,并且编译时加入--enable-sockets。  
  如果你已经编译成cgi解释方式运行,但是使用命令php   -m列出的项目没有sockets,则说明你需要重新编译php。当这些要求达到后你就可以运行这个服务器了  
  启动服务器:  
  ./filename.php  
  然后就可以使用telnet登录了。  
  telnet   10.31.172.77   10000  
  你的终端上将显示:  
  Trying   10.31.172.77...  
  Connected   to   10.31.172.77.  
  Escape   character   is   '^]'.  
  Welcome   to   my   TCP   server!  
  然后输入一些东西,并回车:  
  Hello  
  You   said:   Hello  
  Connection   closed   by   foreign   host  
   
  你也可以修改一下这个程序,让它像phpmanual上的那个例子,只有当客户端输入“quit“的时候才关闭连接。  
   
  5.   其他应用  
  5.1.   聊天室应用  
  5.1.1.   常见的聊天室实现  
  一般的聊天室的实现常使用的方法是使用框架页面,然后对其中一个用于显示谈话内容的框架使用html的方式刷新,例如:  
  <meta   http-equiv=“refresh”   content=”3;http://www.jite.net”>  
  使用这种方式会导致浏览器端不断的向服务器端发出请求,当有大量的请求时就会使得服务器运行效率降低。这样的聊天室显然是有设计弊端的。  
  但是如果使用socket的方式实现聊天室,情况就不同了。  
  5.1.2.   使用socket实现聊天室  
  我们要讨论的聊天室非常简单,只是一个原理上的实现。  
  它是一个   client/server   结构的程序,   首先启动   server,   然后用户使用   client   进行连接.   client/server   结构的优点是速度快,   缺点是当   server   进行更新时,   client   也必需更新.    
   
  初始化   server,   使server   进入监听状态:   (以下只是实现原理,并不涉及具体程序)    
   
  $socket   =   socket(   AF_INET,SOCK_STREAM,   0);    
  //   首先建立一个   socket,   族为   AF_INET,   类型为   SOCK_STREAM.    
  //   AF_INET   =   ARPA   Internet   protocols   即使用   TCP/IP   协议族    
  //   SOCK_STREAM   类型提供了顺序的,   可靠的,   基于字节流的全双工连接.    
  //   由于该协议族中只有一个协议,   因此第三个参数为   0    
   
     
  bind   ($sock,   $address,   $port)  
  //   再将这个   socket   与某个地址进行绑定.    
   
  listen(   sockfd,   MAX_CLIENT)    
  //   地址绑定之后,   server   进入监听状态.    
  //   MAX_CLIENT   是可以同时建立连接的   client   总数.    
   
  server   进入   listen   状态后,   等待   client   建立连接。    
   
  Client端要建立连接首先也需要初始化连接:    
   
  $socket=   socket(   AF_INET,SOCK_STREAM,0))  
  //   同样的,   client   也先建立一个   socket,   其参数与   server   相同.    
   
  connect   ($socket,   $address,   $service_port)  
  //   client   使用   connect   建立一个连接.    
   
  当   client   建立新连接的请求被送到Server端时,   server   使用   accept   来接受该连接:    
   
  accept_connect($sock)  
  //   accept   返回一个新的文件描述符.    
   
  在   server   进入   listen   状态之后,   由于可能有多个用户请求连接,所以程序需要同时对这些用户进行操作,并在它们之间实现信息交换。这在实现上称为I/O多路复用技术。  
  I/O多路复用技术的方法就不是本文所要叙述的内容了,如有兴趣请参考相关书籍。  
   
  5.2.   一个基于web的新闻组浏览器  
  在php中可以使用fsockopen打开一个tcp   socket连接  
  int   fsockopen   (string   hostname,   int   port   [,   int   errno   [,   string   errstr   [,   double   timeout]]])  
  有关此函数的使用请参考php手册。  
  访问新闻组服务,需要使用一个协议叫NNTP,即Network   News   Transfer   Protocol。  
  这个协议有一个专用的RFC描述,它位于   http://www.w3.org/Protocols/rfc977/rfc977.html。  
  该文档详细的说明了如何同一个nntp服务器对话及如何使用命令完成任务。  
  5.2.1.   连接一个服务器  
  <?php  
  $cfgServer   =   "news.php.net";  
  $cfgPort   =   119;  
  $cfgTimeOut   =   10;  
   
  //   open   a   socket  
  if(!$cfgTimeOut)  
  //   without   timeout  
  $usenet_handle   =   fsockopen($cfgServer,   $cfgPort);  
  else  
  //   with   timeout  
  $usenet_handle   =   fsockopen($cfgServer,   $cfgPort,   &$errno,   &$errstr,   $cfgTimeOut);  
   
  if(!$usenet_handle)   {  
  echo   "Connexion   failedn";  
  exit();  
  }    
  else   {  
  echo   "Connectedn";  
  $tmp   =   fgets($usenet_handle,   1024);  
  }  
  ?>    
   
  5.2.2.   同服务器进行对话  
  在前面,我们已经同服务器连接上了,假如我们要从某一新闻组中选取10条最近的新闻,该怎么办呢?  
  RFC977指出,选择一个新闻组使用group命令:  
  GROUP   ggg  
  <?php  
   
  //$cfgUser   =   "xxxxxx";  
  //$cfgPasswd   =   "yyyyyy";  
  $cfgNewsGroup   =   "alt.php";  
   
  //   identification   required   on   private   server  
  if($cfgUser)   {  
  fputs($usenet_handle,   "AUTHINFO   USER   ".$cfgUser."n");  
  $tmp   =   fgets($usenet_handle,   1024);  
   
  fputs($usenet_handle,   "AUTHINFO   PASS   ".$cfgPasswd."n");  
  $tmp   =   fgets($usenet_handle,   1024);  
   
  //   check   error  
   
  if($tmp   !=   "281   Okrn")   {  
  echo   "502   Authentication   errorn";  
  exit();  
  }    
  }  
   
  //   select   newsgroup  
   
  fputs($usenet_handle,   "GROUP   ".$cfgNewsGroup."n");  
  $tmp   =   fgets($usenet_handle,   1024);  
   
  if($tmp   ==   "480   Authentication   required   for   commandrn")   {  
  echo   "$tmpn";  
  exit();  
  }    
   
  $info   =   split("   ",   $tmp);  
  $first   =   $info[2];  
  $last   =   $info[3];  
   
  print   "First   :   $firstn";  
  print   "Last   :   $lastn";  
  ?>    
-----------------------------------------
<?php
function cmd( $str, $report=true )
{
global $smtp;
$ret = fwrite( $smtp, $str."/r/n" );
if( $report === true )
fread( $smtp, 512 );
}
$message = array();
$message[] = "e-CarsDirect Car Quote Request:";
$message[] = "";
foreach( $_POST as $key=>$val )
{
$message[] = sprintf( "%-24s%s", $key.": ", $val );
}

$smtp = fsockopen( "tcp://smtp.powweb.com", 25, $errno, $errstr );
if( ! $smtp )
die( "We are sorry, but an error has occurred; your form has not been submitted:<br />
Please contact Sam Tomaino:<br />
cell: 630-936-7980<br />
fax: 630-735-2356<br />
email: <a href=/"mailto:sam@e-carsdirect.com/">sam@e-carsdirect.com</a><br />
<br />
<a href=/"javascript: history.go(-2)/">Return to Main Page</a>" );
fread( $smtp, 512 );
cmd( "EHLO {$_POST['DomainName']}" );
cmd( "STARTTLS" );
stream_socket_enable_crypto( $smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
cmd( "EHLO {$_POST['DomainName']}" );
cmd( "AUTH LOGIN" );
cmd( base64_encode( "USERNAME_HERE" ) );
cmd( base64_encode( "PASSWORD_HERE" ) );
cmd( "MAIL FROM: <website@e-carsdirect.com>" );
cmd( "RCPT TO: <sam@e-carsdirect.com>" );
cmd( "DATA" );

cmd( "Date: ".date("r"), false );
cmd( "From: e-CarsDirect Website Request Form <website@e-carsdirect.com>", false );
cmd( "Subject: e-CarsDirect Car Quote Request", false );
cmd( "To: sam@e-carsdirect.com", false );
for( $x=0; $x<sizeof($message); $x++ )
cmd( $message[$x], false );

cmd( "." );
cmd( "QUIT" );
fclose( $smtp );

header( "Location: http://www.{$_POST['DomainName']}/thanks.php" );
?>