PHP套接字编程

来源:互联网 发布:杭州他山网络 编辑:程序博客网 时间:2024/05/16 19:00
PHP套接字编程

套接字编程,一般使用c或c++。特别的在web应用程序开发中,常用perl实现套接字。除此以外,用php进行套接字编程也是一个选择。Php可以胜任吗?当然可以。Php是一门高质量的web应用程序开发语言,他的许多特性可以处理众多的任务,网络编程也不例外。 

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 

3 <?php 
/* 
* We don't want any time-limit for how the long can hang 
* around, waiting for connections:(使用set_time_limit设定程序执行时间为无限以等待连接) 
*/ 
8 set_time_limit(0); 

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: (把创建的套接字与IP及端口绑定)*/ 
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 ?> 

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 Aut

hentication 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"; 
?> 

5.2.3. > 读取新闻 

读取新闻的命令是article,具体用法请参考RFC977,这里就不提供例程了。 

6. 后记 

我以为上次写了一篇,这次就可以免了。离交稿日期没几天了,于荣赋来约稿。程稿仓促,难免有错,请见谅,并且指出。 

7. 参考文献: 

廖斌,《php的守护程序编程》; 
w3c,《RFC977》; 
Daniel Solin,Introduction to Socket Programming with PHP;

原创粉丝点击