当我们需要在不同服务器或者不同的语言之间做数据交换,我们有一种方法是用Socket做数据代理。Socket是源于UNIX的套接字,基于TCP/IP协议,是从UNIX早期的命令集中演化而来的,基础的模式是“连接-读写-关闭”。Socket可以应用于B/S和C/S两种不同的网络软件架构上,现在已经被广泛的引用。
php的Socket模块虽然相对比较简陋,一些复杂的应用会出现一些莫名奇妙的问题,但是单单作为基础的数据代理来讲还是经受了人们的检验。

一、Socket传输的是什么

Socket传输的是字节流,没有定义边界。只是通过调节缓冲区的大小来完成流的截断,当然设置缓冲区的大小都是必须的,否则Socket也许会一直工作下去。Socket传输的数据很容易被截取并且会直接展示出来,所以你通常需要对你的数据进行加密,比如AES,就是很好的加密算法,不要用MD5这样的散列表,否则你会死得很惨。
PHP似乎想要get字节流加上边界,在它的socket_write这个函数中声明的三个参数,它的文档里面说如果你将第三个参数设置为PHP_NORMAL_READ,那么将会读取到/r或者/n,可惜,当我这样做的时候,程序溢出了。原因至今不明。

二、用php做一个SocketServer

我们先抛弃类,这篇将没有一个类定义出现。

前面我们已经说了Socket的工作模式是“连接-读写-关闭”。作为服务器,我们要做的事情是建立一个socket,绑定一个端口,然后监视每一个连接,读取他们传来的消息,并给予他们反馈,然后将这个连接毙掉。现在我们开始

< ?php
//===================
//SockServ.php
//===================
define('SOCKIP', 'localhost');
define('SOCKPORT', '12345');
 
/**
 * Set up our socket
 */
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //创建一个socket
printf("Socket created.\r\n");
socket_bind($sock, SOCKIP, SOCKPORT); //绑定一个端口
socket_listen($sock); //开始监视了
printf("Socket has set up.\r\n\r\n");
 
while(true) {
	$conn = socket_accept($sock); //抓到一个,允许它的连接
	if ($conn) {
		printf("==========================================\r\n");
		printf("Socket connected.\r\n");
		while($data = socket_read($conn, 1024)) { 
		//千万不能加上PHP_NORMAL_READ啊
			$buffer = $data;
			printf("Data Received.\r\n");
			print_r("Buffer: ".$buffer."\r\n");
			socket_write($conn, "OK"); //告诉客户端,OK,我收到了
			printf("Successed!\r\n");
		}
		socket_close($conn); //工作完成,你可以去了
		printf("Closed the socket\r\n");
		printf("==========================================\r\n\r\n");
	}
}
?>

上面这段程序,你最好别用浏览器来跑,你可能会得到一个超时,你应该在命令行里面跑。在服务器上,让它跑在后台,然后就让它呆在那里吧,没事的。

三、还有客户端呢

我们当然还需要一个客户端,不然你执行上面程序时会一直停在“Socket has set up.”。客户端的任务就是在浏览器里打开一张页面程序,向服务器端发送那么一个字符串,然后接受服务器返回的“OK”。我们开始吧

< ?php
//===================
//SockClient.php
//===================
define('SOCKIP', 'localhost');
define('SOCKPORT', '12345');
 
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //创建一个socket
$connStat = socket_connect($sock, SOCKIP, SOCKPORT); //连接到服务器
if ($connStat) { //连接成功
	echo '<p>连接成功!';
	$buffer = 'Hi, Server!';
	socket_write($sock, $buffer);
	while($data = socket_read($sock, 1024)) {
	//依然切记不要PHP_NORMAL_READ
		if ($data == 'OK') echo '<p>OK,搞定!</p>';
	}
	socket_close($sock); //你可以安心的去了
} else { //连接失败啦 
	echo '<p>连接居然失败了,是不是服务器没开啊!</p>';
}
?>

这个程序是跑在浏览器上面的,通常呢,我们会写成一个类这个类独立在系统的架构之外,将这个类文件交给负责不同app的程序员,让他们来调用,从而实现和服务器的通信。

四、当然这是个很简单的例子

实际上,我们通常有更复杂的事情去做,但是并不代表对于socket来说我们要做更多的事情,我们只需要在客户端的数据开源(现在是直接定义的),服务器端的数据处理(现在只是打印到屏幕上),还有服务器根据客户端数据进行不同的反馈以及客户端对反馈的处理,这几个方面进行扩展,便可以完成几乎所有的需求。
Socket作为一种有效的数据代理方式,还能够为更好的程序架构提供帮助,比如我们提供更多的服务器数据交换来降低前段服务器的计算压力,Socket本身有很好的栈策略也能在服务器的部署上带来帮助。