您的位置:首页 > 其它

Socket进程通信机制及其应用

2016-09-11 23:59 288 查看
1. Socket简介
Socket通常称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄。应用程序通过套接字向网络发出请求或者应答网络请求。
Socket既不是一个程序,也不是一种协议,而是操作系统提供的用于进程之间通信的一组抽象API。

2. 进程通信相关概念
进程通信的概念最初来源于单机系统。由于每个进程都在自己的地址范围内运行,为了保证两个相互通信的进程之间既互不干扰又能协调一致地工作,操作系统为进程通信提供了相应的设施,如Unix BSD中的管道(pipe)、命名管道(named pipe)和软中断信号(signal)等,但这些都仅限于用在本机进程之间的通信。网间进程通信要解决的是不同主机进程间的相互通信问题。

为此,TCP/IP协议引入了端口、地址和连接的概念。
(1)端口
网络中可以被命名和寻址的通信端口,是操作系统可分配的一种资源。
按照OSI七层网络模型的描述,传输层与网络层在功能上的最大区别是传输层提供进程通信能力。从这个意义上来说,网络通信的最终地址就不仅仅是主机地址了,还包括可以描述进程的某种标识符。为此,TCP/IP协议提出了协议端口(Protocol Port,简称端口)的概念,用于标识通信的进程。

端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区),类似于文件描述符,每个端口都有一个端口号,用于区别不同的端口。由于TCP/IP传输层的TCP协议和UDP协议是两个完全独立的软件模块,因此各自的端口号也相互独立。如TCP有一个255号端口,UDP也有一个255号端口,二者并不冲突。TCP和UDP中端口的地址都是16bit,即端口号的范围是0~65535。

对于这65536个端口号,有以下规定:

端口号小于256的定义为常用端口,服务器一般都是通过常用端口号识别。任何TCP/IP实现所提供的服务都用1~1023之间的端口号,这时用IANA管理的。
客户端只需保证该端口号在本机上是唯一的,客户端端口号因存在时间很短暂,又称临时端口号。
大多数TCP/IP实现给临时端口号分配1024~5000之间的端口号。大于5000的端口号是为其他服务器预留的。
常见的端口有FTP服务的21号端口,HTTP服务的80号端口,SMTP服务的25号端口等。

(2)地址
网络通信中通信的两个进程分别处在不同的机器上。遵循以下原则:

某台主机可与多个网络相连,必须指定一个特定的网络地址。
网络上每台主机应有一个唯一的地址。
每台主机上的每个进程应有在该主机上的唯一标识符。
通常,主机地址由网络ID和主机ID组成,在TCP/IP协议中用32位整数值表示;TCP和UDP均使用16位整数的端口号标识用户进程。

(3)连接
两个进程间的通信链路称为连接。连接表现为一些缓冲区和一组协议机制。

3. Socket演示:实现服务器端与客户端的交互
在服务器端使用Socket创建一个服务,端口是8001,这样该服务就可以与多个客户端进行连接和通信了。
这里,我用PHP创建一个socket服务,在 E:\xampp\htdocs\demo 下新建一个php文件socket_server.php,代码如下:

<?php
// 设置脚本最大执行时间,单位为秒,0表示永不超时
set_time_limit(0);

$address = '127.0.0.1';
$port = 8001;   // 端口可以是1到65535之间的任何数字,前提是未被占用

// 创建并返回一个套接字(通讯节点),一个典型的网络连接由 2 个套接字构成,一个运行在服务器端,另一个运行在客户端
if( ($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo "socket_create() failed, reason: " . socket_strerror(socket_last_error()) . "\n";
}

// 绑定socket到具体的主机端口
if (socket_bind($sock, $address, $port) === false) {
echo "socket_bind() failed, reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

// 监听socket服务器上的请求连接,等待接入
if (socket_listen($sock, 5) === false) {
echo "socket_listen() failed, reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

do {
// 确认客户端的连接请求,成功后,返回一个新的子socket句柄(子线程),用于通信
if (($msgsock = socket_accept($sock)) === false) {
echo "socket_accept() failed, reason: ".socket_strerror(socket_last_error($sock)) . "\n";
break;
}

$msg = "Welcome to connect '$address'"."\n";
// 发送消息(数据)到客户端
if (false === socket_write($msgsock, $msg, strlen($msg))){
echo "socket_write() failed, reason: " . socket_strerror(socket_last_error($sock)) ."\n";
}

echo "Read client message \n";
// 读取客户端的数据
$receivedData = socket_read($msgsock, 8192);
echo "Received message: ".$receivedData."\n";;

// 将客户端发来的数据,进行处理,然后再发送数据给客户端
$responseData = '[time:'.date('Y-m-d H:i:s').']'.PHP_EOL.'[data:'.trim($receivedData).']';
if (false === socket_write($msgsock, $responseData, strlen($responseData))) {
echo "socket_write() failed, reason: " . socket_strerror(socket_last_error($sock)) ."\n";
}

// 关闭连接成功的子socket
socket_close($msgsock);
} while(true);

// 关闭等待接入的socket
socket_close($sock);
?>


然后,在cli模式下运行该php文件,命令如下:
php E:\xampp\htdocs\demo\socket_server.php

如此,就开启了一个socket服务器,注意,命令行窗口不可关闭,否则socket服务就停止了。

一旦开启socket服务器,这个服务就会注册到系统的网络服务中,占用8001端口。如果有socket客户端连接该socket服务器端,操作系统就会为客户端自动分配一个随机的临时端口,用来和服务器端8001端口进行通信。
既然此服务已经被注册到操作系统中,实际上它和腾讯QQ服务、FTP服务等是一个级别的,用它能够完成很多事情。为了验证,可以使用Telnet进行连接, 打开命令行,输入:telnet 127.0.0.1 8001。

由于socket是开放透明的、与编程语言无关的,一旦启用socket服务,任何操作socket的语言都可以访问这个开放的服务。

现在,我们再新建一个socket_client.php文件,用做客户端,代码如下:

<?php
$fp = fsockopen("127.0.0.1", 8001, $errno, $errstr, 30);  // 建立与服务器端的socket连接,返回一个句柄
if (!$fp) {
echo "$errstr ($errno)
\n";
} else {
socket_set_blocking($fp, false);  // 设置为非阻塞模式
fwrite($fp, "test data ...\r\n"); // 给socket服务器端发送消息
while (!feof($fp)) {
echo fgets($fp, 128);    // 读取socket服务器应答的消息
}
fclose($fp);
}
?>


(为了测试方便,我的socket服务器和socket客户端在同一个主机上。)
然后,在cli模式下运行 socket_client.php 或者在浏览器中访问它都行,就可以看到客户端与服务器端的交互。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: