1. 预备知识
一直以来很少看到有多少人使用php的socket模块来做一些事情,大概大家都把它定位在脚本语言的范畴内吧,但是其实php的socket模块可以做很多事情,包括做ftplist,http post提交,smtp提交,组包并进行特殊报文的交互(如smpp协议),whois查询。这些都是比较常见的查询。
特别是php的socket扩展库可以做的事情简直不会比c差多少。
php的socket连接函数
1、集成于内核的socket
这个系列的函数仅仅只能做主动连接无法实现端口监听相关的功能。而且在4.3.0之前所有socket连接只能工作在阻塞模式下。
此系列函数包括
fsockopen,pfsockopen
这两个函数的具体信息可以查询php.net的用户手册
他们均会返回一个资源编号对于这个资源可以使用几乎所有对文件操作的函数对其进行操作如fgets(),fwrite(), fclose()等单注意的是所有函数遵循这些函数面对网络信息流时的规律,例如:
fread() 从文件指针 handle 读取最多 length 个字节。 该函数在读取完 length 个字节数,或到达 EOF 的时候,或(对于网络流)当一个包可用时就会停止读取文件,视乎先碰到哪种情况。
可以看出对于网络流就必须注意取到的是一个完整的包就停止。
2、php扩展模块带有的socket功能。
php4.x 以后有这么一个模块extension=php_sockets.dll,Linux上是一个extension=php_sockets.so。
当打开这个此模块以后就意味着php拥有了强大的socket功能,包括listen端口,阻塞及非阻塞模式的切换,multi-client 交互式处理等
这个系列的函数列表参看http://www.php.net/manual/en/ref.sockets.php
看过这个列表觉得是不是非常丰富呢?不过非常遗憾这个模块还非常年轻还有很多地方不成熟,相关的参考文档也非常少:(
我也正在研究中,因此暂时不具体讨论它,仅给大家一个参考文章
http://www.zend.com/pecl/tutorials/sockets.php
2. 使用PHP socket扩展
服务器端代码:
-
<?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 "OK\nBinding the socket on $address:$port ... ";
-
echo "OK\nNow 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或者\0字符.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, SOCK_STREAM, SOL_TCP)or die("Could not create socket\n"); // 创建一个Socket
-
$connection = socket_connect($socket, $host, $port) or die("Could not connet server\n"); // 连接
-
socket_write($socket, "hello socket") or die("Write failed\n"); // 数据传送 向服务器发送消息
-
while ($buff = socket_read($socket, 1024, PHP_NORMAL_READ)) {
-
echo("Response was:" . $buff . "\n");
-
}
-
socket_close($socket);
使用cli方式启动server:
php server.php
这里注意socket_read函数:
可选的类型参数是一个命名的常数:
PHP_BINARY_READ - 使用系统recv()函数。用于读取二进制数据的安全。 (在PHP>“默认= 4.1.0)
PHP_NORMAL_READ - 读停在\ n或\r(在PHP <= 4.0.6默认)
针对参数PHP_NORMAL_READ ,如果服务器的响应结果没有\ n。造成socket_read(): unable to read from socket
3.PHP的并发IO编程
原文:http://rango.swoole.com/archives/508
1) 多进程/多线程同步阻塞
最早的服务器端程序都是通过多进程、多线程来解决并发IO的问题。进程模型出现的最早,从Unix系统诞生就开始有了进程的概念。最早的服务器端程序一般都是Accept一个客户端连接就创建一个进程,然后子进程进入循环同步阻塞地与客户端连接进行交互,收发处理数据。
多线程模式出现要晚一些,线程与进程相比更轻量,而且线程之间是共享内存堆栈的,所以不同的线程之间交互非常容易实现。比如聊天室这样的程序,客户端连接之间可以交互,比聊天室中的玩家可以任意的其他人发消息。用多线程模式实现非常简单,线程中可以直接读写某一个客户端连接。而多进