Socket.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Services\ChatMessageService;
  4. use App\Services\Common\MemberService;
  5. use App\Services\RedisService;
  6. use Illuminate\Console\Command;
  7. class Socket extends Command
  8. {
  9. public $ws;
  10. /**
  11. * The name and signature of the console command.
  12. *
  13. * @var string
  14. */
  15. protected $signature = 'swoole:socketIm {op?}';
  16. /**
  17. * The console command description.
  18. *
  19. * @var string
  20. */
  21. protected $description = 'websocket ';
  22. /**
  23. * Create a new command instance.
  24. *
  25. * @return void
  26. */
  27. public function __construct()
  28. {
  29. parent::__construct();
  30. }
  31. /**
  32. * Execute the console command.
  33. *
  34. * @return mixed
  35. */
  36. public function handle()
  37. {
  38. $op = $this->argument('op');
  39. $op = $op ? $op : 'start';
  40. if ($op == 'start') {
  41. echo "socket start ...\n";
  42. $this->start();
  43. } else if ($op == 'stop') {
  44. echo "socket stop ...\n";
  45. $this->stop();
  46. }
  47. }
  48. /**
  49. * 运行
  50. */
  51. public function start()
  52. {
  53. try {
  54. //创建websocket服务器对象,监听0.0.0.0:7104端口
  55. $this->ws = new \Swoole\WebSocket\Server("0.0.0.0", env('SOCKET_PORT', '6520'));
  56. //监听WebSocket连接打开事件
  57. $this->ws->on('open', [$this, 'open']);
  58. //监听WebSocket消息事件
  59. $this->ws->on('message', [$this, 'message']);
  60. //监听WebSocket主动推送消息事件
  61. $this->ws->on('request', [$this, 'request']);
  62. //监听WebSocket连接关闭事件
  63. $this->ws->on('close', [$this, 'close']);
  64. $this->ws->start();
  65. } catch (\Exception $exception){
  66. RedisService::set("caches:sockets:error", $exception->getMessage(), 600);
  67. }
  68. }
  69. /**
  70. * 建立连接
  71. * @param $ws
  72. * @param $request
  73. */
  74. public function open($ws, $request)
  75. {
  76. echo "连接成功:" . $request->fd . "\n";
  77. $fds = RedisService::get('chats:fds');
  78. if($fds){
  79. $fds[] = $request->fd;
  80. }else{
  81. $fds = [$request->fd];
  82. }
  83. RedisService::set('chats:fds',$fds, 86400);
  84. $this->ws->push($request->fd, json_encode(['message' => '连接成功:'.$request->fd], 256));
  85. }
  86. /**
  87. * 接收消息
  88. * @param $ws
  89. * @param $frame
  90. */
  91. public function message($ws, $frame)
  92. {
  93. try {
  94. if ($frame->data == 'ping') {
  95. $this->ws->push($frame->fd, 'pong');
  96. return false;
  97. }
  98. // 消息处理
  99. $data = $frame->data ? json_decode($frame->data, true) : [];
  100. $op = isset($data['op']) ? $data['op'] : '';
  101. $userId = isset($data['from_uid']) ? trim($data['from_uid']) : 0;
  102. $chatKey = isset($data['chat_key']) ? trim($data['chat_key']) : '';
  103. $toUid = isset($data['to_uid']) ? intval($data['to_uid']) : 0;
  104. if ($userId && $frame->fd) {
  105. $chatKey = $chatKey ? $chatKey : getChatKey($userId, $toUid);
  106. RedisService::set("chats:bind:{$userId}", ['fd' => $frame->fd, 'user_id' => $userId, 'chat_key' => $chatKey], 86400);
  107. }
  108. if ($data) {
  109. switch ($op) {
  110. case 'chat': // 默认聊天
  111. $toUid = isset($data['to_uid']) ? intval($data['to_uid']) : 0;
  112. $fromUid = isset($data['from_uid']) ? intval($data['from_uid']) : 0;
  113. $messageType = isset($data['message_type']) ? $data['message_type'] : 1;
  114. if ($toUid <= 0 || $fromUid <= 0) {
  115. return false;
  116. }
  117. if (!ChatMessageService::make()->saveData($data)) {
  118. $data = ['message' => lang(ChatMessageService::make()->getError())];
  119. $this->ws->push($frame->fd, json_encode($data, 256));
  120. return false;
  121. }
  122. $toInfo = MemberService::make()->getInfo($toUid);
  123. $fromInfo = MemberService::make()->getInfo($fromUid);
  124. $message = isset($data['message']) ? $data['message'] : '';
  125. $chatKey = getChatKey($fromUid, $toUid);
  126. $data = [
  127. 'type' => isset($data['type']) ? $data['type'] : 1,
  128. 'message_type' => $messageType,
  129. 'from_uid' => $fromUid,
  130. 'from_username' => isset($fromInfo['username']) ? $fromInfo['username'] : '客服',
  131. 'from_username_text' => isset($fromInfo['username']) ? format_account($fromInfo['username']) : '客服',
  132. 'to_uid' => isset($data['to_uid']) ? $data['to_uid'] : 0,
  133. 'to_username' => isset($toInfo['username']) ? $toInfo['username'] : '客服',
  134. 'to_username_text' => isset($toInfo['username']) ? format_account($toInfo['username']) : '客服',
  135. 'chat_key' => $chatKey,
  136. 'message' => $message,
  137. 'message_url' => $messageType == 2 && $message ? get_image_url($message) : '',
  138. 'create_time' => time(),
  139. 'time_text' => format_time(time() - 1),
  140. 'is_read' => 1,
  141. 'status' => 1,
  142. ];
  143. $this->ws->push($frame->fd, json_encode($data, 256));
  144. $toBindData = RedisService::get("chats:bind:{$toUid}");
  145. $toFd = isset($toBindData['fd']) ? $toBindData['fd'] : 0;
  146. $toChatKey = isset($toBindData['chat_key']) ? $toBindData['chat_key'] : '';
  147. if ($toBindData && $toFd && $toChatKey == $chatKey) {
  148. $this->ws->push($toFd, json_encode($data, 256));
  149. }
  150. break;
  151. case 'order':
  152. $toBindData = RedisService::get("chats:bind:od_{$toUid}");
  153. $toFd = isset($toBindData['fd']) ? $toBindData['fd'] : 0;
  154. if($toFd){
  155. $this->sendMsg($toFd, $frame->data);
  156. }
  157. $data['fd'] = $frame->fd;
  158. $data['to_fd'] = $toFd;
  159. $this->ws->push($frame->fd, json_encode($data, 256));
  160. break;
  161. default:
  162. $this->ws->push($frame->fd, json_encode(['message' => $frame->data], 256));
  163. break;
  164. }
  165. } else {
  166. $this->ws->push($frame->fd, json_encode(['message' => 'no data'], 256));
  167. }
  168. } catch (\Exception $exception) {
  169. $this->ws->push($frame->fd, json_encode(['message' => $exception->getMessage()], 256));
  170. }
  171. }
  172. /**
  173. * 接收请求
  174. * @param $request
  175. * @param $response
  176. */
  177. public function request($request, $response)
  178. {
  179. }
  180. /**
  181. * 发送消息
  182. * @param $fd
  183. * @param $data
  184. * @return false
  185. */
  186. public function sendMsg($fd, $data)
  187. {
  188. try {
  189. $this->ws->push($fd, $data);
  190. } catch (\Exception $exception){
  191. return false;
  192. }
  193. }
  194. /**
  195. * 关闭连接
  196. * @param $ws
  197. * @param $fd
  198. */
  199. public function close($ws, $fd = '')
  200. {
  201. // var_dump($ws);
  202. echo '连接关闭:' . $fd . "\n";
  203. $this->ws->close($fd);
  204. }
  205. /**
  206. * 停止运行
  207. */
  208. public function stop()
  209. {
  210. if($this->ws){
  211. $this->ws->close();
  212. }
  213. }
  214. }