1. 程式人生 > >socket位元組流解析(網路抓包解析)

socket位元組流解析(網路抓包解析)

研究了一下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++伺服器也返回了相應的包,我在按照同理進行解包就可以了!