gameChat.php 6.4 KB

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