php教程socket,php中socket通讯详解

有时候我们的php程序需要和其他系统通讯,比如一个企业的官网提供产品溯源信息的查询,在后台网站就需要和企业的溯源系统或ERP系统通讯,此时就需要进行php的网络编程,php提供了一个sockets扩展,官网地址为:

http://nl3.php.net/manual/zh/intro.sockets.php

该扩展让我们有能力通过php直接操纵套接字socket,这样就可以和其他系统通讯了,我们使用socket在OSI网络模型的传输层以上工作,直接使用TCP、UDP提供的服务,因此可以使用它作为其他应用层协议的客户端,比如模拟HTTP客户端(浏览器),常见的smtp、pop、ftp都可以用它模拟,比较好玩的是可以使用它和允许TELNET的服务器交互,这些协议都是应用层协议所以它都可以交互,自定义的系统间通信就需要自定义协议了。

这里提供一个示例,用php开发一个简单的文件接收服务器,另一个php客户端程序展示如何和这个服务器程序通讯,代码如下。

服务器端程序:

/**

* 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展

* 这是一个简易的服务器程序,接收客户端发送的文件,保存后关闭

* 通讯协议约定为:文件大小::文件扩展名::有效数据

* 通讯结束标识符为:“-end-”

* 在命令行或浏览器中执行均可,推荐命令行

* 作者:云客【云游天下,作客四方】

*/

/****配置****/

//要绑定监听的本机ip地址和端口

$address = '127.0.0.1';

$port = 81;

error_reporting(E_ALL);

//防止超时

set_time_limit(0);

//开启绝对刷送,禁止缓冲内容

ob_implicit_flush();

//创建套接字资源

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {

echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";

}

//绑定套接字到端口

if (socket_bind($sock, $address, $port) === false) {

echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

}

//开始监听端口,参数5表示可以让5个连接请求在缓冲中排队

//排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将直接忽略,客户端显示无法链接

//注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队

if (socket_listen($sock, 5) === false) {

echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

}

do {

//程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源

if (($client_sock = socket_accept($sock)) === false) {

echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

break;

}

echo "[client connect start]\n";

$data = ""; //接收的数据

$data_size = -1; //接收的数据大小

$received_size = 0; //实际接收的数据

$ext_name = "txt"; //默认文件扩展名

//开始与客户端交互

do {

//运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容

if (false === ($buf = socket_read($client_sock, 2048))) {

echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($client_sock)) . "\n";

break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器

}

if ($data_size === -1) {//第一次接受数据,解析通讯协议

$arr = explode("::", $buf, 3);

$data_size = (int)$arr[0];

$ext_name = $arr[1];

$data .= $arr[2];

$received_size += strlen($data);

continue;

}

$data .= $buf;

$received_size += strlen($buf);

if ($data_size <= $received_size) {

if ($data_size < $received_size) {

$data = substr($data, 0, $data_size);

}

echo "received:" . $received_size . "/" . "total:" . $data_size;

$talkback = "-end-"; //发送通讯结束标志

socket_write($client_sock, $talkback, strlen($talkback));

file_put_contents("servertest." . $ext_name, $data);

break 2; //关闭服务器

}

echo $talkback = "received:" . $received_size . "\n";

socket_write($client_sock, $talkback, strlen($talkback));

} while (true);

socket_close($client_sock);

} while (true);

socket_close($sock);

?>

客户端程序:

/**

* 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展

* 这是一个简易的客户端程序,向服务器发送文件

* 通讯协议约定为:文件大小::文件扩展名::有效数据

* 通讯结束标识符为:“-end-”

* 在命令行或浏览器中执行均可,推荐命令行,需先开启服务器端

* 作者:云客【云游天下,作客四方】

*/

/****配置****/

$file = "yunke.jpg"; //待发送的文件

//服务器ip和端口号

$address = '127.0.0.1';

$port = 81;

error_reporting(E_ALL);

// 防止超时

set_time_limit(0);

// 开启绝对刷送,不要缓冲输出

ob_implicit_flush(true);

//创建套接字资源

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {

echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";

exit();

}

//打开到远程主机的链接

if (socket_connect($sock, $address, $port) === false) {

echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

exit();

}

//构造一个很大的待发送数据 大约10MB

/*

$data = "";

for ($i = 1; $i <= 10000000; $i++) {

$data .= "data:" . $i;

}

$data = $data . $data . $data;

*/

$data = file_get_contents($file);

$data_size = strlen($data);

$arr_temp = explode('.', $file);

$ext_name = end($arr_temp);

$sock_data = $data_size . "::" . $ext_name . "::" . $data;

//发送数据给远程主机

while (true) {

$sock_data_size = strlen($sock_data);

$send_size = socket_write($sock, $sock_data, $sock_data_size);

if ($send_size === false) {

echo "send false:" . socket_strerror(socket_last_error($sock)) . "\n";

socket_close($sock);

echo "[client shutdown]\n";

exit();

}

if ($send_size == $sock_data_size) {

break;

}

$sock_data = substr($sock_data, $send_size);

}

//读返回数据

while (true) {

if (false === ($out = socket_read($sock, 2048))) {

echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

break 1;

}

echo "server: " . $out . "\n";

if (substr($out, -5) == "-end-") { //约定结束标志

break;

}

}

//关闭套接字资源

socket_close($sock);

echo "[client shutdown]\nsend data:" . format($data_size);

function format($byte = 0)

{

if ($byte > 1024 * 1024) {

return ceil($byte / (1024 * 1024)) . "MB";

} elseif ($byte > 1024) {

return ceil($byte / 1024) . "KB";

} else {

return $byte . " Byte";

}

}

该示例展示通过一个TCP短链接的方式向服务器传送一个文件,在实际的项目中通讯模块不会这么简单,需要考虑更多的问题

比如:

是否需要长连接的方式(一个tcp链接里面多次来回传送数据)、数据校验防止损坏、不同系统间的大小端字节序问题、自定义通讯协议、粘包问题、超时处理、并发访问、流量控制、TCP封包解包等等

如果想深入了解php网络编程以上提到的这些都需要系统学习,推荐看一看workerman的实现

它是一个php写的socket服务器框架,帮助解决socket通讯问题,使用它可以建立一个自定义服务器等等

workerman官网地址为:http://www.workerman.net/

以下再提供两个查看服务器HTTP头和浏览器头的示例程序:

以下示例查看服务器返回的头信息,修改需要查看的服务器地址,然后在浏览器中访问该脚本即可:

error_reporting(E_ALL);

// 防止超时

set_time_limit(0);

// 开启绝对刷送,不要缓冲输出

ob_implicit_flush(true);

$address = 'www.qq.com'; //要查看的服务器

$port=80;

//创建套接字资源

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {

echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";

exit();

}

//打开到远程主机的链接

if (socket_connect($sock, gethostbyname($address), $port) === false) {

echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

exit();

}

//构造http头

$msg = "GET / HTTP/1.1 \r\n";

$msg .= "Host: {$address}\r\n";

$msg .= "Connection: Close\r\n\r\n";

//指示服务器发回完毕就断开,不必等等其他资源的链接

//如果没有该行会,服务器会一直等待我方继续发送数据,直到超时关闭

//而我方不会再发送,这个会让下面的socket_read函数互为等待,等很久

//发送给远程主机

socket_write($sock, $msg, strlen($msg));

$str="";

while ($out = @socket_read($sock, 2048*4)) {

$str.=$out;

}

$str=explode("\r\n\r\n", $str);

$str=$str[0];

if($str)

{

echo "

\r\n".$str."\r\n
";

}else{

echo "nothing";

}

//关闭套接字资源

socket_close($sock);

以下是查看浏览器头的示例,可以方便的查看浏览器发送的会话信息

首先确定关闭了80端口,然后修改本机的host文件,将想访问的网址定向到本机127.0.0.1地址,使用php命令行模式启动此脚本,然后使用浏览器访问已设置定向的网址即可,该程序会一直开启,如需关闭请在控制台使用ctrl+c组合键,程序如下:

error_reporting(E_ALL);

//防止超时

set_time_limit(0);

//开启绝对刷送,禁止缓冲内容

ob_implicit_flush();

$address = '127.0.0.1';

$port = 80;

//创建套接字资源

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {

echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";

}

//绑定套接字到端口

if (socket_bind($sock, $address, $port) === false) {

echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

}

//开始监听端口,参数5表示可以让5个连接请求在缓冲中排队

//排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将显示无法链接

//注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队

if (socket_listen($sock, 5) === false) {

echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

}

do {

//程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源

//可以使用socket_set_nonblock函数设置非阻塞模式

if (($msgsock = socket_accept($sock)) === false) {

echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";

break;

}

//链接成功后在控制台显示提示

echo "[client start ".date("Y-m-d H:i:s")."]\n\n";

do {

//运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容

if (false === ($buf = socket_read($msgsock, 2048*6))) {

echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";

break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器

}

$talkback ="HTTP/1.1 200 OK\r\n\r\n";

$talkback .= date("Y-m-d H:i:s")."\nBrowser HTTP Headers:\n\n".$buf."\n";

//向客户端显示该内容

socket_write($msgsock, $talkback, strlen($talkback));

//向服务器控制台显示该内容

echo "$buf\n";

break ; //中断本次与浏览器的链接

} while (true);

socket_close($msgsock);

} while (true);

socket_close($sock);

?>

相关推荐:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值