研究了一下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打包成二进制进行通讯.下面贴一下代码
本地测试主要应用为:发送账号和密码给服务器端
- <?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();
- }
- /*
- *设置表头部分
- *表头=length+code+flag
- *length是总长度(4字节) code操作标志(2字节) flag暂时无用(4字节)
- */
- 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++服务器也返回了相应的包,我在按照同理进行解包就可以了!