gameChat.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. ini_set('display_errors', 'on');
  3. error_reporting(E_ALL);
  4. $sock = stream_socket_client("tcp://115.28.44.100:8282");
  5. if(!$sock)exit("can not create sock\n");
  6. $buf = new GameBuffer();
  7. $buf->body = rand(1,100000000);
  8. fwrite($sock, $buf->getBuffer());
  9. $ret = fread($sock, 1024);
  10. $ret = GameBuffer::decode($ret);
  11. if(isset($ret['to_uid']))
  12. {
  13. echo "chart room login success , your uid is [{$ret['to_uid']}]\n";
  14. echo "use uid:words send message to one user\n";
  15. echo "use words send message to all\n";
  16. }
  17. stream_set_blocking($sock, 0);
  18. stream_set_blocking(STDIN, 0);
  19. $read = array(STDIN, $sock);
  20. $write = $ex = array();
  21. while(1)
  22. {
  23. $read_copy = $read;
  24. if($ret = stream_select($read_copy, $write, $ex, 1000))
  25. {
  26. foreach($read as $fd)
  27. {
  28. // 接收消息
  29. if((int)$fd === (int)$sock)
  30. {
  31. $ret = fread($fd, 102400);
  32. if(!$ret){continue;exit("connection closed\n ");}
  33. $ret = GameBuffer::decode($ret);
  34. echo $ret['from_uid'] , ':', $ret['body'], "\n";
  35. continue;
  36. }
  37. // 向某个uid发送消息 格式为 uid:xxxxxxxx
  38. $ret = fgets(STDIN, 10240);
  39. if(!$ret)continue;
  40. if(preg_match("/(\d+):(.*)/", $ret, $match))
  41. {
  42. $uid = $match[1];
  43. $words = $match[2];
  44. $buf = new GameBuffer();
  45. $buf->header['cmd'] = GameBuffer::CMD_USER;
  46. $buf->header['sub_cmd'] = GameBuffer::SCMD_SAY;
  47. $buf->header['to_uid'] = $uid;
  48. $buf->body = $words;
  49. fwrite($sock, $buf->getBuffer());
  50. continue;
  51. }
  52. // 向所有用户发消息
  53. $buf = new GameBuffer();
  54. $buf->header['cmd'] = GameBuffer::CMD_USER;
  55. $buf->header['sub_cmd'] = GameBuffer::SCMD_BROADCAST;
  56. $buf->body = trim($ret);
  57. fwrite($sock, $buf->getBuffer());
  58. continue;
  59. }
  60. }
  61. }
  62. /**
  63. * 二进制协议
  64. *
  65. * struct BufferProtocol
  66. * {
  67. * unsigned char version,//版本
  68. * unsigned short series_id,//序列号 udp协议使用
  69. * unsigned short cmd,//主命令字
  70. * unsigned short sub_cmd,//子命令字
  71. * int code,//返回码
  72. * unsigned int from_uid,//来自用户uid
  73. * unsigned int to_uid,//发往的uid
  74. * unsigned int pack_len,//包长
  75. * char[pack_length-HEAD_LEN] body//包体
  76. * }
  77. *
  78. * @author walkor <worker-man@qq.com>
  79. */
  80. class Buffer
  81. {
  82. /**
  83. * 版本
  84. * @var integer
  85. */
  86. const VERSION = 0x01;
  87. /**
  88. * 包头长度
  89. * @var integer
  90. */
  91. const HEAD_LEN = 23;
  92. /**
  93. * 序列号,防止串包
  94. * @var integer
  95. */
  96. protected static $seriesId = 0;
  97. /**
  98. * 协议头
  99. * @var array
  100. */
  101. public $header = array(
  102. 'version' => self::VERSION,
  103. 'series_id' => 0,
  104. 'cmd' => 0,
  105. 'sub_cmd' => 0,
  106. 'code' => 0,
  107. 'from_uid' => 0,
  108. 'to_uid' => 0,
  109. 'pack_len' => self::HEAD_LEN
  110. );
  111. /**
  112. * 包体
  113. * @var string
  114. */
  115. public $body = '';
  116. /**
  117. * 初始化
  118. * @return void
  119. */
  120. public function __construct($buffer = null)
  121. {
  122. if($buffer)
  123. {
  124. $data = self::bufferToData($buffer);
  125. $this->body = $data['body'];
  126. unset($data['body']);
  127. $this->header = $data;
  128. }
  129. else
  130. {
  131. if(self::$seriesId>=65535)
  132. {
  133. self::$seriesId = 0;
  134. }
  135. else
  136. {
  137. $this->header['series_id'] = self::$seriesId++;
  138. }
  139. }
  140. }
  141. /**
  142. * 判断数据包是否都到了
  143. * @param string $buffer
  144. * @return int int=0数据是完整的 int>0数据不完整,还要继续接收int字节
  145. */
  146. public static function input($buffer, &$data = null)
  147. {
  148. $len = strlen($buffer);
  149. if($len < self::HEAD_LEN)
  150. {
  151. return self::HEAD_LEN - $len;
  152. }
  153. $data = unpack("Cversion/Sseries_id/Scmd/Ssub_cmd/icode/Ifrom_uid/Ito_uid/Ipack_len", $buffer);
  154. if($data['pack_len'] > $len)
  155. {
  156. return $data['pack_len'] - $len;
  157. }
  158. $data['body'] = '';
  159. $body_len = $data['pack_len'] - self::HEAD_LEN;
  160. if($body_len > 0)
  161. {
  162. $data['body'] = substr($buffer, self::HEAD_LEN, $body_len);
  163. }
  164. return 0;
  165. }
  166. /**
  167. * 设置包体
  168. * @param string $body_str
  169. * @return void
  170. */
  171. public function setBody($body_str)
  172. {
  173. $this->body = (string) $body_str;
  174. }
  175. /**
  176. * 获取整个包的buffer
  177. * @param string $data
  178. * @return string
  179. */
  180. public function getBuffer()
  181. {
  182. $this->header['pack_len'] = self::HEAD_LEN + strlen($this->body);
  183. return pack("CSSSiIII", $this->header['version'], $this->header['series_id'], $this->header['cmd'], $this->header['sub_cmd'], $this->header['code'], $this->header['from_uid'], $this->header['to_uid'], $this->header['pack_len']).$this->body;
  184. }
  185. /**
  186. * 从二进制数据转换为数组
  187. * @param string $buffer
  188. * @return array
  189. */
  190. public static function decode($buffer)
  191. {
  192. $data = unpack("Cversion/Sseries_id/Scmd/Ssub_cmd/icode/Ifrom_uid/Ito_uid/Ipack_len", $buffer);
  193. $data['body'] = '';
  194. $body_len = $data['pack_len'] - self::HEAD_LEN;
  195. if($body_len > 0)
  196. {
  197. $data['body'] = substr($buffer, self::HEAD_LEN, $body_len);
  198. }
  199. return $data;
  200. }
  201. }
  202. /**
  203. *
  204. * 命令字相关
  205. * @author walkor <worker-man@qq.com>
  206. *
  207. */
  208. class GameBuffer extends Buffer
  209. {
  210. // 系统命令
  211. const CMD_SYSTEM = 128;
  212. // 连接事件
  213. const SCMD_ON_CONNECT = 1;
  214. // 关闭事件
  215. const SCMD_ON_CLOSE = 2;
  216. // 发送给网关的命令
  217. const CMD_GATEWAY = 129;
  218. // 给用户发送数据包
  219. const SCMD_SEND_DATA = 3;
  220. // 根据uid踢人
  221. const SCMD_KICK_UID = 4;
  222. // 根据地址和socket编号踢人
  223. const SCMD_KICK_ADDRESS = 5;
  224. // 广播内容
  225. const SCMD_BROADCAST = 6;
  226. // 通知连接成功
  227. const SCMD_CONNECT_SUCCESS = 7;
  228. // 用户中心
  229. const CMD_USER = 1;
  230. // 登录
  231. const SCMD_LOGIN = 8;
  232. // 发言
  233. const SCMD_SAY = 9;
  234. }