im_server.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. #include "net/im_server.h"
  2. #include <exception>
  3. #include <memory>
  4. #include <string>
  5. #include <vector>
  6. #include "common/i18n.h"
  7. #include "common/logger.h"
  8. #include "common/protocol_def.h"
  9. #include "db/friend_store.h"
  10. #include "db/redis_service.h"
  11. #include "net/packet_log.h"
  12. #include "util/json.h"
  13. #include "HPSocket/HPSocket.h"
  14. namespace im {
  15. ImServer::ImServer(const AppConfig& cfg, SessionMgr& sessions, Dispatcher& dispatcher,
  16. MysqlService& mysql, RedisService* redis)
  17. : cfg_(cfg),
  18. sessions_(sessions),
  19. dispatcher_(dispatcher),
  20. mysql_(mysql),
  21. redis_(redis),
  22. codec_(cfg.protocol().magic, cfg.protocol().max_packet)
  23. {
  24. }
  25. ImServer::~ImServer()
  26. {
  27. stop();
  28. }
  29. bool ImServer::start()
  30. {
  31. stopping_.store(false, std::memory_order_release);
  32. if (redis_ != nullptr) {
  33. redis_->set_message_handler([this](const std::string& payload) {
  34. on_cluster_payload(payload);
  35. });
  36. }
  37. if (!start_thread_pool()) {
  38. return false;
  39. }
  40. server_.on_accept([this](ylib::network::tcp::server*, uint64 connid) {
  41. on_accept(connid);
  42. });
  43. server_.on_recv(
  44. [this](ylib::network::tcp::server*, uint64 connid, const char* data, uint32 len) {
  45. on_recv(connid, data, len);
  46. });
  47. server_.on_close([this](ylib::network::tcp::server*, uint64 connid) {
  48. on_close(connid);
  49. });
  50. configure_hp();
  51. ylib::AddressPort ap;
  52. ap.address = cfg_.server().host;
  53. ap.port = cfg_.server().port;
  54. if (!server_.start(ap, ylib::PUSH_DEFAULT)) {
  55. Logger::error("tcp listen failed: " + server_.last_error());
  56. stop_thread_pool();
  57. return false;
  58. }
  59. ITcpServer* hp = server_.getHP();
  60. Logger::info("tcp listen " + cfg_.server().host + ":" +
  61. std::to_string(cfg_.server().port) +
  62. " io_workers=" +
  63. std::to_string(hp != nullptr ? hp->GetWorkerThreadCount() : 0));
  64. return true;
  65. }
  66. void ImServer::stop()
  67. {
  68. const bool first = !stopping_.exchange(true, std::memory_order_acq_rel);
  69. if (server_.started()) {
  70. server_.close();
  71. }
  72. stop_thread_pool();
  73. sessions_.clear();
  74. if (first) {
  75. Logger::info("tcp server stopped");
  76. }
  77. }
  78. void ImServer::configure_hp()
  79. {
  80. ITcpServer* hp = server_.getHP();
  81. if (hp == nullptr) {
  82. return;
  83. }
  84. hp->SetMaxConnectionCount(cfg_.server().max_connections);
  85. if (cfg_.server().worker_threads != 0) {
  86. hp->SetWorkerThreadCount(static_cast<DWORD>(cfg_.server().worker_threads));
  87. }
  88. }
  89. bool ImServer::start_thread_pool()
  90. {
  91. thread_pool_ = HP_Create_ThreadPool();
  92. if (thread_pool_ == nullptr) {
  93. Logger::error("hp thread pool create failed");
  94. return false;
  95. }
  96. if (!thread_pool_->Start(static_cast<DWORD>(cfg_.server().thread_pool),
  97. cfg_.server().thread_pool_queue, TRP_CALL_FAIL)) {
  98. Logger::error("hp thread pool start failed");
  99. HP_Destroy_ThreadPool(thread_pool_);
  100. thread_pool_ = nullptr;
  101. return false;
  102. }
  103. Logger::info("hp thread pool workers=" +
  104. std::to_string(thread_pool_->GetThreadCount()) +
  105. " queue_limit=" + std::to_string(cfg_.server().thread_pool_queue));
  106. return true;
  107. }
  108. void ImServer::stop_thread_pool()
  109. {
  110. if (thread_pool_ == nullptr) {
  111. return;
  112. }
  113. thread_pool_->Stop(INFINITE);
  114. HP_Destroy_ThreadPool(thread_pool_);
  115. thread_pool_ = nullptr;
  116. }
  117. bool ImServer::send(ConnId connid, const Packet& packet)
  118. {
  119. log_send(connid, packet);
  120. Packet out = packet;
  121. out.magic = cfg_.protocol().magic;
  122. return server_.send(connid, codec_.encode(out));
  123. }
  124. bool ImServer::send(const SessionPtr& session, const Packet& packet)
  125. {
  126. if (!conn_matches(session)) {
  127. return false;
  128. }
  129. return send(session->connid(), packet);
  130. }
  131. void ImServer::disconnect(ConnId connid)
  132. {
  133. server_.disConnect(connid);
  134. }
  135. bool ImServer::conn_matches(const SessionPtr& session) const
  136. {
  137. if (!session || session->closed() || stopping_.load(std::memory_order_acquire)) {
  138. return false;
  139. }
  140. if (!sessions_.is_live(session)) {
  141. return false;
  142. }
  143. ITcpServer* hp = const_cast<ylib::network::tcp::server&>(server_).getHP();
  144. if (hp == nullptr) {
  145. return false;
  146. }
  147. PVOID extra = nullptr;
  148. if (!hp->GetConnectionExtra(session->connid(), &extra) || extra != session.get()) {
  149. return false;
  150. }
  151. return true;
  152. }
  153. void ImServer::notify_friend_presence(UserId uid, int online)
  154. {
  155. if (uid <= 0 || uid == kFileHelperUid) {
  156. return;
  157. }
  158. ylib::json body;
  159. body["uid"] = static_cast<int64>(uid);
  160. body["online"] = online ? 1 : 0;
  161. const Packet pkt = Packet::make(Type::Friend, Cmd::PresenceNotify, 0, body.to_string());
  162. try {
  163. FriendStore store(mysql_);
  164. for (const auto& f : store.list_friends(uid)) {
  165. if (f.uid <= 0 || f.uid == kFileHelperUid) {
  166. continue;
  167. }
  168. push_to_uid(f.uid, pkt);
  169. }
  170. } catch (const std::exception& e) {
  171. Logger::warn(std::string("presence notify uid=") + std::to_string(uid) + ": " + e.what());
  172. }
  173. }
  174. void ImServer::on_accept(ConnId connid)
  175. {
  176. if (stopping_.load(std::memory_order_acquire)) {
  177. disconnect(connid);
  178. return;
  179. }
  180. auto session = std::make_shared<Session>(connid, remote_of(connid),
  181. cfg_.protocol().magic,
  182. cfg_.protocol().max_packet);
  183. sessions_.add(session);
  184. ITcpServer* hp = server_.getHP();
  185. if (hp != nullptr) {
  186. hp->SetConnectionExtra(connid, session.get());
  187. }
  188. Logger::info("accept conn=" + std::to_string(connid) + " remote=" + session->remote() +
  189. " online=" + std::to_string(sessions_.size()));
  190. }
  191. void ImServer::on_recv(ConnId connid, const char* data, uint32_t len)
  192. {
  193. if (stopping_.load(std::memory_order_acquire)) {
  194. return;
  195. }
  196. auto session = sessions_.get(connid);
  197. if (!session || session->closed()) {
  198. return;
  199. }
  200. std::vector<Packet> packets;
  201. const DecodeStatus st = session->feed(data, len, packets);
  202. if (st == DecodeStatus::BadMagic || st == DecodeStatus::TooLarge) {
  203. Logger::warn("bad packet conn=" + std::to_string(connid) +
  204. (st == DecodeStatus::BadMagic ? " magic" : " too large"));
  205. disconnect(connid);
  206. return;
  207. }
  208. for (auto& pkt : packets) {
  209. log_recv(connid, pkt);
  210. bool start_task = false;
  211. if (!session->enqueue(std::move(pkt), &start_task)) {
  212. return;
  213. }
  214. if (start_task && !submit_session(session)) {
  215. session->clear_running();
  216. Logger::error("thread pool submit failed conn=" + std::to_string(connid));
  217. disconnect(connid);
  218. return;
  219. }
  220. }
  221. }
  222. void ImServer::on_close(ConnId connid)
  223. {
  224. auto session = sessions_.get(connid);
  225. if (session) {
  226. session->mark_closed();
  227. }
  228. ITcpServer* hp = server_.getHP();
  229. if (hp != nullptr) {
  230. hp->SetConnectionExtra(connid, nullptr);
  231. }
  232. const UserId uid = session ? session->uid() : 0;
  233. const std::string remote = session ? session->remote() : "";
  234. sessions_.remove(connid);
  235. if (uid > 0 && !sessions_.find_by_uid(uid)) {
  236. unbind_cluster(uid);
  237. if (!is_online(uid)) {
  238. notify_friend_presence(uid, 0);
  239. }
  240. }
  241. Logger::info("close conn=" + std::to_string(connid) + " remote=" + remote +
  242. " online=" + std::to_string(sessions_.size()));
  243. }
  244. bool ImServer::submit_session(const SessionPtr& session)
  245. {
  246. if (thread_pool_ == nullptr || stopping_.load(std::memory_order_acquire)) {
  247. return false;
  248. }
  249. auto* task = new PoolTask{this, session};
  250. if (!thread_pool_->Submit(reinterpret_cast<Fn_TaskProc>(&ImServer::on_pool_task), task)) {
  251. delete task;
  252. return false;
  253. }
  254. return true;
  255. }
  256. void ImServer::on_pool_task(void* arg)
  257. {
  258. std::unique_ptr<PoolTask> task(static_cast<PoolTask*>(arg));
  259. if (!task || task->server == nullptr) {
  260. return;
  261. }
  262. try {
  263. task->server->drain_session(task->session);
  264. } catch (const std::exception& e) {
  265. Logger::error(std::string("thread pool task exception: ") + e.what());
  266. } catch (...) {
  267. Logger::error("thread pool task unknown exception");
  268. }
  269. }
  270. void ImServer::drain_session(const SessionPtr& session)
  271. {
  272. if (!session) {
  273. return;
  274. }
  275. while (true) {
  276. Packet pkt;
  277. if (!session->take_next(pkt)) {
  278. return;
  279. }
  280. if (!conn_matches(session)) {
  281. session->clear_running();
  282. return;
  283. }
  284. try {
  285. HandlerContext ctx;
  286. ctx.session = session;
  287. ctx.server = this;
  288. ctx.mysql = &mysql_;
  289. ctx.config = &cfg_;
  290. ctx.packet = std::move(pkt);
  291. dispatcher_.dispatch(std::move(ctx));
  292. } catch (const std::exception& e) {
  293. Logger::error(std::string("dispatch exception conn=") +
  294. std::to_string(session->connid()) + ": " + e.what());
  295. } catch (...) {
  296. Logger::error("dispatch unknown exception conn=" +
  297. std::to_string(session->connid()));
  298. }
  299. }
  300. }
  301. std::string ImServer::remote_of(ConnId connid) const
  302. {
  303. ITcpServer* hp = const_cast<ylib::network::tcp::server&>(server_).getHP();
  304. if (hp == nullptr) {
  305. return "";
  306. }
  307. char addr[256] = {0};
  308. int addr_len = static_cast<int>(sizeof(addr));
  309. USHORT port = 0;
  310. if (!hp->GetRemoteAddress(connid, addr, addr_len, port)) {
  311. return "";
  312. }
  313. return std::string(addr) + ":" + std::to_string(port);
  314. }
  315. bool ImServer::is_online(UserId uid)
  316. {
  317. if (uid <= 0) {
  318. return false;
  319. }
  320. if (sessions_.find_by_uid(uid)) {
  321. return true;
  322. }
  323. if (redis_ == nullptr || !redis_->enabled()) {
  324. return false;
  325. }
  326. return !redis_->online_node(uid).empty();
  327. }
  328. bool ImServer::push_to_uid(UserId uid, const Packet& packet)
  329. {
  330. if (uid <= 0) {
  331. return false;
  332. }
  333. if (auto local = sessions_.find_by_uid(uid)) {
  334. return send(local, packet);
  335. }
  336. if (redis_ == nullptr || !redis_->enabled()) {
  337. return false;
  338. }
  339. const std::string node = redis_->online_node(uid);
  340. if (node.empty() || node == redis_->node_id()) {
  341. return false;
  342. }
  343. ylib::json msg;
  344. msg["op"] = "send";
  345. msg["uid"] = static_cast<int64>(uid);
  346. msg["type"] = static_cast<int64>(packet.type);
  347. msg["cmd"] = static_cast<int64>(packet.cmd);
  348. msg["body"] = packet.body.to_string();
  349. return redis_->publish_to_node(node, msg.to_string());
  350. }
  351. void ImServer::bind_cluster(UserId uid)
  352. {
  353. if (redis_ == nullptr || !redis_->enabled() || uid <= 0) {
  354. return;
  355. }
  356. const std::string old = redis_->bind_online(uid);
  357. if (old.empty() || old == redis_->node_id()) {
  358. return;
  359. }
  360. ylib::json msg;
  361. msg["op"] = "kick";
  362. msg["uid"] = static_cast<int64>(uid);
  363. redis_->publish_to_node(old, msg.to_string());
  364. }
  365. void ImServer::unbind_cluster(UserId uid)
  366. {
  367. if (redis_ == nullptr || !redis_->enabled() || uid <= 0) {
  368. return;
  369. }
  370. redis_->unbind_online(uid);
  371. }
  372. void ImServer::kick_local(UserId uid)
  373. {
  374. auto session = sessions_.find_by_uid(uid);
  375. if (!session) {
  376. return;
  377. }
  378. ylib::json body;
  379. body["code"] = Err::Ok;
  380. body["msg"] = i18n::text(i18n::Msg::Kicked, session->lang());
  381. send(session, Packet::make(Type::System, Cmd::Kicked, 0, body.to_string()));
  382. disconnect(session->connid());
  383. }
  384. void ImServer::on_cluster_payload(const std::string& payload)
  385. {
  386. if (payload.empty() || payload == "{}") {
  387. return;
  388. }
  389. ylib::json msg;
  390. if (!msg.parse(payload) || !msg.exist("op")) {
  391. return;
  392. }
  393. const std::string op = msg["op"].to<std::string>();
  394. const UserId uid = msg.exist("uid") ? static_cast<UserId>(msg["uid"].to<int64>()) : 0;
  395. if (uid <= 0) {
  396. return;
  397. }
  398. if (op == "kick") {
  399. kick_local(uid);
  400. return;
  401. }
  402. if (op != "send") {
  403. return;
  404. }
  405. auto session = sessions_.find_by_uid(uid);
  406. if (!session) {
  407. return;
  408. }
  409. Packet pkt;
  410. pkt.type = static_cast<uint16_t>(msg.exist("type") ? msg["type"].to<int64>() : 0);
  411. pkt.cmd = static_cast<uint16_t>(msg.exist("cmd") ? msg["cmd"].to<int64>() : 0);
  412. if (msg.exist("body")) {
  413. pkt.body = msg["body"].to<std::string>();
  414. }
  415. send(session, pkt);
  416. }
  417. } // namespace im