概括
UNIX域套接字用于在同一台机器上运行的进程之间的通信。虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高。UNIX域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。
套接字的属性
套接字的特性由3个属性确定,它们分别是:域、类型和协议。
套接字的域
它指定套接字通信中使用的网络介质,最常见的套接字域是AF_INET,它指的是Internet网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。另一个域AF_UNIX表示UNIX文件系统,它就是文件输入/输出,而它的地址就是文件名。
套接字类型
因特网提供了两种通信机制:流(stream)和数据报(datagram),因而套接字的类型也就分为流套接字和数据报套接字。这里主要讲流套接字。
流套接字由类型SOCK_STREAM指定,它们是在AF_INET域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
与流套接字相对的是由类型SOCK_DGRAM指定的数据报套接字,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
套接字协议
只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。通常只需要使用默认值。
TCP和UDP套接字类型
TCP 是一种面向连接的字节流套接字,所以服务端需要通过 listen() 转变为被动 socket,通过 accept() 等待连接。
而对于 UDP 来说,就比较简单了,因为它是一种无连接的数据报套接字,实际上,客户/服务端的概念也弱化了。
上PHP代码
无命名套接字
用于有血缘的进程间通信
<?php
$fd = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$pid = pcntl_fork();
if($pid == 0){
while (1){
$data = fread($fd[0], 128);
if($data){
fprintf(STDOUT, "读取到了:%s \n", $data);
}
}
exit();
}
while (1){
fwrite($fd[1],'hello-'.time(), 128);
sleep(1);
}
命名TCP套接字
服务端
<?php
// 创建
$socket = socket_create(AF_UNIX, SOCK_STREAM, IPPROTO_IP);
// 绑定
socket_bind($socket, 'unix_socket_c');
// 监听
socket_listen($socket, 5);
// 等待连接-阻塞
$connect = socket_accept($socket);
if($connect){
while (1){
$data = socket_read($connect, 1024);
if($data){
fprintf(STDOUT, "接受到 %s \n", $data);
if(strncasecmp($data, "exit", 4) == 0){
socket_write($connect, "exit", 1024);
break;
}
else{
socket_write($connect, "已收到" . $data, 1024);
}
}
}
}
// 关闭连接
socket_close($connect);
// 关闭监听
socket_close($socket);
客户端
<?php
// 创建
$socket = socket_create(AF_UNIX, SOCK_STREAM, IPPROTO_IP);
// 连接
$connect = socket_connect($socket,'unix_socket_c');
if($connect){
fprintf(STDOUT, "连接成功 \n");
//子进程用来接收发送结果
$pid = pcntl_fork();
if($pid == 0){
while (1){
$data = socket_read($socket, 1024);
if($data){
fprintf(STDOUT, "--%s \n", $data);
if(strncasecmp($data, "exit", 4) == 0){
fprintf(STDOUT, "子进程退出 \n");
break;
}
}
}
exit();
}
while (1){
$data = fread(STDIN, 1024);
if($data){
socket_send($socket, $data, strlen($data), 0);
if(strncasecmp($data, "exit", 4) == 0){
fprintf(STDOUT, "父进程退出 \n");
break;
}
}
}
}
$pid = pcntl_wait($status);
if($status){
fprintf(STDOUT, "子进程退出 %d \n", $pid);
}
UDP命名套接字
服务端
<?php
// 创建
$socket = socket_create(AF_UNIX, SOCK_DGRAM, IPPROTO_IP);
// 绑定
socket_bind($socket, 'unix_socket_x');
while (1){
$len = socket_recvfrom($socket,$data, 1024, 0, $file);
if($len){
fprintf(STDOUT, "接收到 %s \n", $data);
}
}
客户端
<?php
// 创建
$socket = socket_create(AF_UNIX, SOCK_DGRAM, IPPROTO_IP);
while (1){
$data = fread(STDIN, 1024);
socket_sendto($socket,$data, strlen($data), 0, 'unix_socket_x');
fprintf(STDOUT, "发送:%s", $data);
}