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);
}
}
?>
----------------------------------------------------------------------
以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" );
?>
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" );
?>
- PHP socket,server,cient:模拟 post
- PHP socket,server,cient:模拟 post
- PHP的socket模拟POST
- php socket模拟post(转发)
- php+socket模拟get,post请求
- 用socket模拟php中get,post
- PHP用socket模拟post之fsocketopen
- [php]模拟socket发送GET,POST请求
- php socket模拟POST GET请求 fsockopen版
- php模拟post行为
- PHP模拟post,get
- php模拟POST表单
- php 模拟POST提交
- PHP模拟post登陆
- PHP模拟POST请求
- PHP模拟POST请求
- php模拟post方法
- PHP模拟post请求
- TCP/IP协议数据报结构详解
- J2EE学习中一些值得研究的开源项目
- apache tomcat
- GB2312 --> UTF8 BY VBSRCIPT
- PHP套接字编程
- PHP socket,server,cient:模拟 post
- 征友
- top 100 blogs
- 拉鱼啦..
- 卸载office2003遇到zlonger.com.msi无法找到的完美解决办法
- 关于innerhtml用法(转载)
- java notes
- 添加权限项
- 资源:Windows系统的消息机制详解