PHP SOCKET编程详解
《PHP SOCKET编程详解》要点: PHP进修1. 预备知识 一直以来很少看到有多少人使用php的socket模块来做一些事情,大概大家都把它定位在脚本语言的范畴内吧,但是其实php的socket模块可以做很多事情,包含做ftplist,http post提交,smtp提交,组包并进行特殊报文的交互(如smpp协议),whois查询.这些都是比较常见的查询. 特别是php的socket扩展库可以做的事情简直不会比c差若干. php的socket衔接函数 PHP进修1、集成于内核的socket 这个系列的函数仅仅只能做主动连接无法实现端口监听相关的功能.而且在4.3.0之前所有socket连接只能工作在阻塞模式下. 2、php扩展模块带有的socket功效. php4.x 以后有这么一个模块extension=php_sockets.dll,Linux上是一个extension=php_sockets.so. PHP进修http://www.zend.com/pecl/tutorials/sockets.php 2. 使用PHP socket扩大 PHP进修服务器端代码: <?php /** * File name server.php * 服务器端代码 * * @author guisu.huang * @since 2012-04-11 * */ //确保在连接客户端时不会超时 set_time_limit(0); //设置IP和端口号 $address = "127.0.0.1"; $port = 2046; //调试的时候,可以多换端口来测试程序! /** * 创建一个SOCKET * AF_INET=是ipv4 如果用ipv6,则参数为 AF_INET6 * SOCK_STREAM为socket的tcp类型,如果是UDP则使用SOCK_DGRAM */ $sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP) or die("socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); //阻塞模式 socket_set_block($sock) or die("socket_set_block() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); //绑定到socket端口 $result = socket_bind($sock,$address,$port) or die("socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); //开始监听 $result = socket_listen($sock,4) or die("socket_listen() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n"); echo "OKnBinding the socket on $address:$port ... "; echo "OKnNow ready to accept connections.nListening on the socket ... n"; do { // never stop the daemon //它接收连接哀求并调用一个子连接Socket来处理客户端和服务器间的信息 $msgsock = socket_accept($sock) or die("socket_accept() failed: reason: " . socket_strerror(socket_last_error()) . "/n"); //读取客户端数据 echo "Read client data n"; //socket_read函数会一直读取客户端数据,直到遇见n,t或者 字符.PHP脚本把这写字符看做是输入的结束符. $buf = socket_read($msgsock,8192); echo "Received msg: $buf n"; //数据传送 向客户端写入返回结果 $msg = "welcome n"; socket_write($msgsock,$msg,strlen($msg)) or die("socket_write() failed: reason: " . socket_strerror(socket_last_error()) ."/n"); //一旦输出被返回到客户端,父/子socket都应通过socket_close($msgsock)函数来终止 socket_close($msgsock); } while (true); socket_close($sock); 客户端代码: <?php /** * File name:client.php * 客户端代码 * * @author guisu.huang * @since 2012-04-11 */ set_time_limit(0); $host = "127.0.0.1"; $port = 2046; $socket = socket_create(AF_INET,SOL_TCP)or die("Could not create socketn"); // 创立一个Socket $connection = socket_connect($socket,$host,$port) or die("Could not connet servern"); // 连接 socket_write($socket,"hello socket") or die("Write failedn"); // 数据传送 向服务器发送消息 while ($buff = socket_read($socket,1024,PHP_NORMAL_READ)) { echo("Response was:" . $buff . "n"); } socket_close($socket); 使用cli方式启动server: PHP进修php server.php 这里注意socket_read函数: 针对参数PHP_NORMAL_READ,如果服务器的响应成果没有 n.造成socket_read(): unable to read from socket PHP进修3. PHP socket内部源码 从PHP内部源码来看,PHP提供的socket编程是在socket,bind,listen等函数外添加了一个层,让其更加简单和方便调用.但是一些业务逻辑的程序还是需要程序员本身去实现. /* {{{ proto resource socket_create(int domain,int type,int protocol) U Creates an endpoint for communication in the domain specified by domain,of type specified by type */ PHP_FUNCTION(socket_create) { long arg1,arg2,arg3; php_socket *php_sock = (php_socket*)emalloc(sizeof(php_socket)); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"lll",&arg1,&arg2,&arg3) == FAILURE) { efree(php_sock); return; } if (arg1 != AF_UNIX #if HAVE_IPV6 && arg1 != AF_INET6 #endif && arg1 != AF_INET) { php_error_docref(NULL TSRMLS_CC,E_WARNING,"invalid socket domain [%ld] specified for argument 1,assuming AF_INET",arg1); arg1 = AF_INET; } if (arg2 > 10) { php_error_docref(NULL TSRMLS_CC,"invalid socket type [%ld] specified for argument 2,assuming SOCK_STREAM",arg2); arg2 = SOCK_STREAM; } php_sock->bsd_socket = socket(arg1,arg3); php_sock->type = arg1; if (IS_INVALID_SOCKET(php_sock)) { SOCKETS_G(last_error) = errno; php_error_docref(NULL TSRMLS_CC,"Unable to create socket [%d]: %s",errno,php_strerror(errno TSRMLS_CC)); efree(php_sock); RETURN_FALSE; } php_sock->error = 0; php_sock->blocking = 1; 1257,1-8 61% ZEND_REGISTER_RESOURCE(return_value,php_sock,le_socket); } Zend API现实对c函数socket做了包装,供PHP使用. 而在c的socket编程中,我们使用如下方式初始化socket. PHP进修
//初始化Socket
if( (socket_fd = socket(AF_INET,0)) == -1 ){
printf("create socket error: %s(errno: %d)n",strerror(errno),errno);
exit(0);
}
PHP进修4. socket函数 函数名 描述 5. PHP Socket模拟哀求 PHP进修我们使用stream_socket来模拟: /** * * @param $data= array=array('key'=>value) */ function post_contents($data = array()) { $post = $data ? http_build_query($data) : ''; $header = "POST /test/ HTTP/1.1" . "n"; $header .= "User-Agent: Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+SV1)" . "n"; $header .= "Host: localhost" . "n"; $header .= "Accept: */*" . "n"; $header .= "Referer: http://localhost/test/" . "n"; $header .= "Content-Length: ". strlen($post) . "n"; $header .= "Content-Type: application/x-www-form-urlencoded" . "n"; $header .= "rn"; $ddd = $header . $post; $fp = stream_socket_client("tcp://localhost:80",$errno,$errstr,30); $response = ''; if (!$fp) { echo "$errstr ($errno)<br />n"; } else { fwrite($fp,$ddd); $i = 1; while ( !feof($fp) ) { $r = fgets($fp,1024); $response .= $r; //处理这一行 } } fclose($fp); return $response; } 注意,以上法式可能会进入死循环; 这个PHP的feof($fp) 必要注意的地方了,我们来分析为什么进入死循环. while ( !feof($fp) ) { $r = fgets($fp,1024); $response .= $r; } 实际上,feof是可靠的,但是结合fgets函数一块使用的时候,必需要小心了.一个常见的做法是: $fp = fopen("myfile.txt","r"); while (!feof($fp)) { $current_line = fgets($fp); //对成果做进一步处理,防止进入死循环 } 当处理纯文本的时候,fgets获取最后一行字符后,foef函数返回的成果并不是TRUE.实际的运算过程如下: PHP进修1) while()继续循环. PHP进修2) fgets 获取倒数第二行的字符串 PHP进修3) feof返回false,进入下一次循环 4)fgets获取末了一行数据 5) 一旦fegets函数被挪用,feof函数仍然返回的是false.所以继续执行循环 6) fget试图获取另外一行,但实际成果是空的.实际代码没有意识到这一点,试图处理另外根本不存在的一行,但fgets被调用了,feof放回的成果仍然是false PHP进修7) ..... PHP进修8) 进入死循环 欢迎参与《PHP SOCKET编程详解》讨论,分享您的想法,编程之家 52php.cn为您提供专业教程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |