PHP中提供了Socket扩展功能,能够很方便的实现Socket编程。在PHP手册中有两段示范代码,分别实现了TCP
/IP的Server和Client。在这两个例子中,使用Socket发送或接收的都是字符串。众所周知,在PHP中有很多字符串相关的函数,处理字符串是非常方便的。但是,如果是在PHP中使用Socket函数和另一个程序通信时,不是直接用字符串来传递消息,而是用特定的数据结构比如整型甚至结构体怎么办呢?

  PHP中变量的类型通常不是由程序员设定的,确切地说,是由PHP根据该变量使用的上下文在运行时决定的。PHP在变量定义中不需要(或不支持)明示的类型定义,变量类型是根据使用该变量的上下文所决定的。也就是说,如果把一个字符串值赋给变量var,var就成了一个字符串。如果又把一个整型值赋给var,那它就成了一个整数。而且PHP中的整型数的字长和平台有关(通常是32 位有符号),不支持无符号整数,那么如果要发送一个32位的无符号整数又怎么来构造它呢?

  看完下面的例子,这些问题就都可以回答了。

  例如要在PHP中用Socket发送或者接收一个结构体:

#define UINT8 unsigned char
#
define UINT32 unsigned long

typedef struct _test_struct
{
    UINT8 type;
    UINT8 name[
10];
    UINT32 ip;
} test_struct;

说明:
1. type是一个字节的无符号字符,可以取值1, 2, 3等。
2. name是一个字符串(字符数组),以’\0’结束,长度为10个字节。
3. ip是一个32位的无符号整型变量,保存IP地址。
4. 为了处理方便,假设结构体都是按1个字节对齐的(很多C编译器默认都是按4个字节对齐的),否则要根据对齐方式来计算结构体成员的偏移。

  首先来看一下怎么在PHP中构造这个结构体并使用Socket发送出去。构造的结构体保存在变量$ buf中。

  由于PHP中没有明确的类型定义,所以只能按字节来构造这个结构体,也就是说,只能一个字节一个字节来构造。

  第一个字节是type,可以使用chr函数根据ASCII码产生一个字节的字符。例如:

$ buf 
= chr(1);

  接着是10个字节的字符串,这个就比较容易了,直接赋值就行,但是要注意的是,如果字符串的长度不够的话,必须在后面补上’\
0’来保证它是10个字节。例如:

$ name 
= ‘Dennis’;
while (strlen($ name) < 10)
{
 $ name 
.= chr(0);
}
$ buf 
.= $ name;

  最后4个字节的无符号整型就比较麻烦了,要将四个字节的数拼在一起。例如:

$ ip 
= ‘192.168.0.1’;
$ iparr 
= array();
$ iparr 
= explode('.', $ ip);
$ buf 
.= sprintf('%c%c%c%c', $ iparr[0], $ iparr[1], $ iparr[2], $ iparr[3]);

  这里用了sprintf来格式化字符串,同样也可以使用chr函数来处理。

  构造完这个结构体就要通过Socket把它发送出去了,以TCP为例,发送代码如下:

// socket param
$ sockip = ‘192.168.0.254’;
$ sockport 
= 8000;

// create a udp socket
$ socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if ($ socket < 0)
{
 
die("Socket Error!");
}

// connect to UDP port
$ result = socket_connect($ socket, $ sockip, $ sockport);

if ($ result < 0)
{
 
die("Socket Error!");
}

// send data
$ sent = socket_write($ socket, $ buf, strlen($ buf));

// close socket
socket_close($ socket);

  那么如果是接收这个结构体又怎么处理呢?可能你已经想到了,和发送类似,就是按字节去还原。下面就是具体的方法,假设Socket接受的数据保存在变量$ buf中。这里用到了和chr函数互补的另一个函数ord,它可以获得字符对应的ASCII码。

// get type
$ type = ord($ buf{0});

// get name
$ name = substr($ buf, 1, 10);

// get ip
$ ipstr = ord($ buf{14}) . '.' . ord($ buf{13}) . '.' . ord($ buf{12}) . '.' . ord($ buf{11});

  上面的代码获得的是IP地址的字符串形式,如果要获得对应的长整型,可以用下面的方法:

$ iplong 
= (ord($ buf{14}) << 24+ (ord($ buf{13}) << 16+ (ord($ buf{12}) << 8+ ord($ buf{11});

  看到这里,上面的问题也都基本上解决了,更复杂的结构都可以通过这种“逐字节”的方法来处理。虽然之前用PHP开发比较多,但是一直没用过它来进行Socket编程,当时曾经在网上找过相关的文章,但是没有找到。上面的这种方法是我自己想出来的,也许不是最好最简单的实现方法,还是写出来,希望对大家能从中受益