研究了一下PHP和C++socket通讯,用C++作为服务器端,php作为客户端进行.
socket通讯是基于协议的,因此,只要双方协议一致就行.
关于协议的选择:我看过网上大部分协议都是在应用层的协议,选用这样的协议很方便,基本上就是字符串传过来,传过去
本次研究的协议算是当今国际化的一个标准做法.length+flag+body(长度+类型+内容)的方式,
total_length |
code |
flag |
length1 |
string1 |
length2 |
string2 |
总长度 |
操作类型 |
标志 |
字符串1长度 |
字符串1 |
字符串2长度 |
字符串2 |
4字节 |
2字节 |
4字节(暂时无用) |
2字节 |
x字节 |
2字节 |
x字节 |
php实现方式,也很容易,通过pack打包成二进制进行通讯.下面贴一下代码
本地测试主要应用为:发送账号和密码给服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| <?php class Byte{ private $length=0; private $byte=''; private $code; public function setBytePrev($content){ $this->byte=$content.$this->byte; } public function getByte(){ return $this->byte; } public function getLength(){ return $this->length; } public function writeChar($string){ $this->length+=strlen($string); $str=array_map('ord',str_split($string)); foreach($str as $vo){ $this->byte.=pack('c',$vo); } $this->byte.=pack('c','0'); $this->length++; } public function writeInt($str){ $this->length+=4; $this->byte.=pack('L',$str); } public function writeShortInt($interge){ $this->length+=2; $this->byte.=pack('v',$interge); } } class GameSocket{ private $socket; private $port=9991; private $host='192.168.211.231'; private $byte; private $code; const CODE_LENGTH=2; const FLAG_LENGTH=4; public function __set($name,$value){ $this->$name=$value; } public function __construct($host='192.168.211.231',$port=9991){ $this->host=$host; $this->port=$port; $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if(!$this->socket){ exit('创建socket失败'); } $result = socket_connect($this->socket,$this->host,$this->port); if(!$result){ exit('连接不上目标主机'.$this->host); } $this->byte=new Byte(); } public function write($data){ if(is_string($data)||is_int($data)||is_float($data)){ $data[]=$data; } if(is_array($data)){ foreach($data as $vo){ $this->byte->writeShortInt(strlen($vo)); $this->byte->writeChar($vo); } } $this->setPrev(); $this->send(); }
private function getHeader(){ $length=$this->byte->getLength(); $length=intval($length)+self::CODE_LENGTH+self::FLAG_LENGTH; return pack('L',$length); } private function getCode(){ return pack('v',$this->code); } private function getFlag(){ return pack('L',24); } private function setPrev(){ $this->byte->setBytePrev($this->getHeader().$this->getCode().$this->getFlag()); } private function send(){ $result=socket_write($this->socket,$this->byte->getByte()); if(!$result){ exit('发送信息失败'); } } public function __desctruct(){ socket_close($this->socket); } } $data[]='testzouhao'; $data[]='a'; $gameSocket=new GameSocket(); $gameSocket->code=11; $gameSocket->write($data);
|
通过抓包分析,得到本次的包内容
包头等等都不用看了,主要看蓝色部分.
根据协议分析,前4个字节为表头,代表的是长度
因此:
17 00 00 00代表的是表头长度,17为16进制,转换为十进制为23,代表其余部分全部加为23字节.
0b 00代表的是操作码为11,代表是登录操作
18 00 00 00代表的是flag,暂时无用,不去理会
0a 00 代表的字符串1的长度,转为十进制为10
74 65 73 74 7a 6f 75 68 61 6f 分别转为十进制之后,是ascii码对应的字符,结果为:testzouhao,
由于C++字符串的机制是末尾是\0,所以在字符串后,00字节就是\0
然后是第二个字符串长度为01 00,也就是为1
61同理,十进制转ascii码,为a,之后的00为c++机制的\0
完美解析,发送包无措,之后c++服务器也返回了相应的包,我在按照同理进行解包就可以了!