发布日期

PHP使用Socket发送字节流与C++通讯

在ThinkPHP extend目录 创建 lib/Socket.php Socket.php

<?php
namespace lib;
define("CONNECTED", true);
define("DISCONNECTED", false);
/**
* Socket class
*
*
* @author Seven
*/
Class Socket
{
private static $instance;
private $connection = null;
private $connectionState = DISCONNECTED;
private $defaultHost = "127.0.0.1";
private $defaultPort = 80;
private $defaultTimeout = 10;
public  $debug = false;
function __construct()
{
}
/**
    * Singleton pattern. Returns the same instance to all callers
    *
    * @returnSocket
    */
public static function singleton()
{
if (self::$instance == null || ! self::$instance instanceof Socket)
{
self::$instance = new Socket();
}
return self::$instance;
}
/**
    * Connects to the socket with the given address and port
    *
    * @returnvoid
    */
public function connect($serverHost=false, $serverPort=false, $timeOut=false)
{
if($serverHost == false)
{
$serverHost = $this->defaultHost;
}
if($serverPort == false)
{
$serverPort = $this->defaultPort;
}
$this->defaultHost = $serverHost;
$this->defaultPort = $serverPort;
if($timeOut == false)
{
$timeOut = $this->defaultTimeout;
}
$this->connection = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
if(socket_connect($this->connection,$serverHost,$serverPort) == false)
{
$errorString = socket_strerror(socket_last_error($this->connection));
$this->_throwError("Connecting to {$serverHost}:{$serverPort} failed.<br>Reason: {$errorString}");
}else{
$this->_throwMsg("Socket connected!");
}
$this->connectionState = CONNECTED;
}
/**
    * Disconnects from the server
    *
    * @returnTrue on succes, false if the connection was already closed
    */
public function disconnect()
{
if($this->validateConnection())
{
socket_close($this->connection);
$this->connectionState = DISCONNECTED;
$this->_throwMsg("Socket disconnected!");
return true;
}
return false;
}
/**
    * Sends a command to the server
    *
    * @returnstring Server response
    */
public function sendRequest($command)
{
if($this->validateConnection())
{
$result = socket_write($this->connection,$command,strlen($command));
return $result;
}
$this->_throwError("Sending command \"{$command}\" failed.<br>Reason: Not connected");
}
public function isConn()
{
return $this->connectionState;
}
public function getUnreadBytes()
{
$info = socket_get_status($this->connection);
return $info['unread_bytes'];
}
public function getConnName(&$addr, &$port)
{
if ($this->validateConnection())
{
socket_getsockname($this->connection,$addr,$port);
}
}
/**
    * Gets the server response (not multilined)
    *
    * @returnstring Server response
    */
public function getResponse()
{
$read_set = array($this->connection);
while (($events = socket_select($read_set, $write_set = NULL, $exception_set = NULL, 0)) !== false)
{
if ($events > 0)
{
foreach ($read_set as $so)
{
if (!is_resource($so))
{
$this->_throwError("Receiving response from server failed.<br>Reason: Not connected");
return false;
}elseif ( ( $ret = @socket_read($so,4096,PHP_BINARY_READ) ) == false){
$this->_throwError("Receiving response from server failed.<br>Reason: Not bytes to read");
return false;
}
return $ret;
}
}
}
return false;
}
public function waitForResponse()
{
if($this->validateConnection())
{
return socket_read($this->connection, 2048);
}
$this->_throwError("Receiving response from server failed.<br>Reason: Not connected");
return false;
}
/**
    * Validates the connection state
    *
    * @returnbool
    */
private function validateConnection()
{
return (is_resource($this->connection) && ($this->connectionState != DISCONNECTED));
}
/**
    * Throws an error
    *
    * @returnvoid
    */
private function _throwError($errorMessage)
{
echo "Socket error: " . $errorMessage;
}
/**
    * Throws an message
    *
    * @returnvoid
    */
private function _throwMsg($msg)
{
if ($this->debug)
{
echo "Socket message: " . $msg . "\n\n";
}
}
/**
    * If there still was a connection alive, disconnect it
    */
public function __destruct()
{
$this->disconnect();
}
}
?>

在ThinkPHP extend目录 创建 lib/Bytes.php Bytes.php

<?php
namespace lib;
/**
* byte数组与字符串转化类
* @author
* Created on 2011-7-15
*/
class Bytes {
/**
    * 转换一个String字符串为byte数组
    * @param $str 需要转换的字符串
    * @param $bytes 目标byte数组
    * @author Zikie
    */
public static function getBytes($str) {
$len = strlen($str);
$bytes = array();
for($i=0;$i<$len;$i++) {
if(ord($str[$i]) >= 128){
$byte = ord($str[$i]) - 256;
}else{
$byte = ord($str[$i]);
}
$bytes[] =  $byte ;
}
return $bytes;
}
/**
    * 将字节数组转化为String类型的数据
    * @param $bytes 字节数组
    * @param $str 目标字符串
    * @return 一个String类型的数据
    */
public static function toStr($bytes) {
$str = '';
foreach($bytes as $ch) {
$str .= chr($ch);
}
return $str;
}
/**
    * 转换一个int为byte数组
    * @param $byt 目标byte数组
    * @param $val 需要转换的字符串
    * @author Zikie
    */
public static function integerToBytes($val) {
$byt = array();
$byt[0] = ($val & 0xff);
$byt[1] = ($val >> 8 & 0xff);    //   >>:移位    &:与位
$byt[2] = ($val >> 16 & 0xff);
$byt[3] = ($val >> 24 & 0xff);
return $byt;
}
/**
    * 从字节数组中指定的位置读取一个Integer类型的数据
    * @param $bytes 字节数组
    * @param $position 指定的开始位置
    * @return 一个Integer类型的数据
    */
public static function bytesToInteger($bytes, $position) {
$val = 0;
$val = $bytes[$position + 3] & 0xff;
$val <<= 8;
$val |= $bytes[$position + 2] & 0xff;
$val <<= 8;
$val |= $bytes[$position + 1] & 0xff;
$val <<= 8;
$val |= $bytes[$position] & 0xff;
return $val;
}
/**
    * 转换一个shor字符串为byte数组
    * @param $byt 目标byte数组
    * @param $val 需要转换的字符串
    * @author Zikie
    */
public static function shortToBytes($val) {
$byt = array();
$byt[0] = ($val & 0xff);
$byt[1] = ($val >> 8 & 0xff);
return $byt;
}
/**
    * 从字节数组中指定的位置读取一个Short类型的数据。
    * @param $bytes 字节数组
    * @param $position 指定的开始位置
    * @return 一个Short类型的数据
    */
public static function bytesToShort($bytes, $position) {
$val = 0;
$val = $bytes[$position + 1] & 0xFF;
$val = $val << 8;
$val |= $bytes[$position] & 0xFF;
return $val;
}
}
?>

使用

本次研究的协议算是当今国际化的一个标准做法.length+flag+body(长度+类型+内容)的方式,

total_length    codeM   codeS   string1
总长度(高位低位)   主命令 子命令 字符串1
字节  1字节 1字节(暂时无用)   x字节

php实现方式,也很容易,通过pack打包成二进制进行通讯.下面贴一下代码

/**
    * 通知结果到服务器
    * @param $params
    * @param string $userId 数据包内容userid
    * @param int $codeM 主命令
    * @param int $codeS 子命令
    * @returnstring
    */
public function send_msg($userId,$codeM,$codeM)
{
$socketAddr = "";  //socket服务ip
$socketPort = "";//socket服务端口
try {
$bytes = new \lib\Bytes();
$headType = 0;
// $userId=32267;
$headLength = 4 + intval(strlen($userId));
//var_dump($headLength);exit();
$headType = $bytes->integerToBytes(intval($headType));
$headLength = $bytes->integerToBytes(intval($headLength));
$codeM = $bytes->integerToBytes(intval($codeM)); //
$codeS = $bytes->integerToBytes(intval($codeS));
$userId = $bytes->getBytes($userId);
$headType = [$headType[0]];
$headLength = [$headLength[0]];
$codeM = [$codeM[0]];
$codeS = [$codeS[0]];
$return_betys = array_merge($headType, $headLength, $codeM, $codeS, $userId);
//var_dump($return_betys);
$msg = $bytes->toStr($return_betys);
$strLen = strlen($msg);
//var_dump($msg);
$packet = pack("a{$strLen}", $msg);
$pckLen = strlen($packet);
//var_dump($packet);
$socket = \lib\Socket::singleton();
$socket->connect($socketAddr, $socketPort); //连服务器
$sockResult = $socket->sendRequest($packet); // 将包发送给服务器
//var_dump($sockResult);
sleep(3);
$socket->disconnect(); //关闭链接
} catch (Exception $e) {
var_dump($e);
$this->log_error("pay order send to server" . $e->getMessage());
}
}