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

在ThinkPHP extend目录 创建 lib/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

<?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_lengthcodeMcodeSstring1
总长度(高位低位)主命令子命令字符串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());
}
}