You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

feiqengine.cpp 24 kB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. #include "feiqengine.h"
  2. #include "protocol.h"
  3. #include "ipmsg.h"
  4. #include <memory>
  5. #include "utils.h"
  6. #include <fstream>
  7. #include "defer.h"
  8. #include <arpa/inet.h>
  9. #include <unistd.h>
  10. #include <iostream>
  11. #include <iomanip>
  12. class ContentSender : public SendProtocol
  13. {
  14. public:
  15. void setContent(const Content* content)
  16. {
  17. mContent = content;
  18. }
  19. protected:
  20. const Content* mContent;
  21. };
  22. class SendTextContent : public ContentSender
  23. {
  24. public:
  25. int cmdId() override{return IPMSG_SENDMSG|IPMSG_SENDCHECKOPT;}
  26. void write(ostream& os) override
  27. {
  28. auto content = static_cast<const TextContent*>(mContent);
  29. if (content->format.empty())
  30. {
  31. os<<encOut->convert(content->text);
  32. }
  33. else
  34. {
  35. os<<encOut->convert(content->text)
  36. <<"{"
  37. <<encOut->convert(content->format)
  38. <<"}";
  39. }
  40. }
  41. };
  42. class SendKnockContent : public ContentSender
  43. {
  44. public:
  45. int cmdId() override{return IPMSG_KNOCK;}
  46. void write(ostream &os) override {(void)os;}
  47. };
  48. class SendFileContent : public ContentSender
  49. {
  50. public:
  51. int cmdId() override {return IPMSG_SENDMSG|IPMSG_FILEATTACHOPT;}
  52. void write(ostream& os) override
  53. {
  54. auto content = static_cast<const FileContent*>(mContent);
  55. char sep = HLIST_ENTRY_SEPARATOR;
  56. auto filename = content->filename;
  57. stringReplace(filename, ":", "::");//估摸着协议不会变,偷懒下
  58. os<<(char)0
  59. <<to_string(content->fileId)
  60. <<sep
  61. <<encOut->convert(filename)
  62. <<sep
  63. <<std::hex<<content->size
  64. <<sep
  65. <<content->modifyTime
  66. <<sep
  67. <<content->fileType
  68. <<sep
  69. <<FILELIST_SEPARATOR;
  70. }
  71. };
  72. class SendImOnLine : public SendProtocol
  73. {
  74. public:
  75. SendImOnLine(const string& name):mName(name){}
  76. int cmdId() override{return IPMSG_BR_ENTRY;}
  77. void write(ostream &os) override
  78. {
  79. os<<encOut->convert(mName);
  80. }
  81. private:
  82. string mName;
  83. };
  84. class SendImOffLine : public SendProtocol
  85. {
  86. public:
  87. SendImOffLine(const string& name):mName(name){}
  88. int cmdId() override {return IPMSG_BR_EXIT;}
  89. void write(ostream &os) override
  90. {
  91. os<<encOut->convert(mName);
  92. }
  93. private:
  94. string mName;
  95. };
  96. /**
  97. * @brief The AnsSendCheck class 发送消息我收到了
  98. */
  99. class SendSentCheck : public SendProtocol
  100. {
  101. public:
  102. SendSentCheck(const string& packetNo)
  103. :mPacketNo(packetNo){}
  104. int cmdId() override{return IPMSG_RECVMSG;}
  105. void write(ostream& os) override
  106. {
  107. os<<mPacketNo;
  108. }
  109. private:
  110. string mPacketNo;
  111. };
  112. /**
  113. * @brief The SendReadCheck class 发送消息我已经读了
  114. */
  115. class SendReadCheck : public SendProtocol
  116. {
  117. public:
  118. SendReadCheck(const string& packetNo)
  119. :mPacketNo(packetNo){}
  120. public:
  121. int cmdId() override {return IPMSG_READMSG;}
  122. void write(ostream& os) override
  123. {
  124. os<<mPacketNo;
  125. }
  126. private:
  127. string mPacketNo;
  128. };
  129. /**
  130. * @brief The AnsBrEntry class 回复好友上线包
  131. */
  132. class AnsBrEntry : public SendProtocol
  133. {
  134. public:
  135. AnsBrEntry(const string& myName):mName(myName){}
  136. public:
  137. int cmdId() override { return IPMSG_ANSENTRY;}
  138. void write(ostream &os) override {
  139. os<<encOut->convert(mName);
  140. }
  141. private:
  142. const string& mName;
  143. };
  144. //定义触发器
  145. typedef std::function<void (shared_ptr<Post> post)> OnPostReady;
  146. #define DECLARE_TRIGGER(name)\
  147. public:\
  148. name(OnPostReady trigger) : mTrigger(trigger){}\
  149. private:\
  150. OnPostReady mTrigger;\
  151. void trigger(shared_ptr<Post> post){mTrigger(post);}
  152. /**
  153. * @brief The RecvAnsEntry class 好友响应我们的上线消息
  154. */
  155. class RecvAnsEntry : public RecvProtocol
  156. {
  157. DECLARE_TRIGGER(RecvAnsEntry)
  158. public:
  159. bool read(shared_ptr<Post> post)
  160. {
  161. if (IS_CMD_SET(post->cmdId, IPMSG_ANSENTRY))
  162. {
  163. auto converted = toString(encIn->convert(post->extra));
  164. post->from->setName(converted);
  165. trigger(post);
  166. return true;
  167. }
  168. return false;
  169. }
  170. };
  171. /**
  172. * @brief The RecvBrEntry class 好友上线
  173. */
  174. class RecvBrEntry : public RecvProtocol
  175. {
  176. DECLARE_TRIGGER(RecvBrEntry)
  177. public:
  178. bool read(shared_ptr<Post> post)
  179. {
  180. if (IS_CMD_SET(post->cmdId, IPMSG_BR_ENTRY))
  181. {
  182. post->from->setName(toString(encIn->convert(post->extra)));
  183. trigger(post);
  184. return true;
  185. }
  186. return false;
  187. }
  188. };
  189. /**
  190. * @brief The RecvBrExit class 好友下线
  191. */
  192. class RecvBrExit : public RecvProtocol
  193. {
  194. DECLARE_TRIGGER(RecvBrExit)
  195. public:
  196. bool read(shared_ptr<Post> post)
  197. {
  198. if (IS_CMD_SET(post->cmdId, IPMSG_BR_EXIT))
  199. {
  200. post->from->setOnLine(false);
  201. trigger(post);
  202. return true;
  203. }
  204. return false;
  205. }
  206. };
  207. /**
  208. * @brief The RecvKnock class 窗口抖动
  209. */
  210. class RecvKnock : public RecvProtocol
  211. {
  212. public:
  213. bool read(shared_ptr<Post> post)
  214. {
  215. if (IS_CMD_SET(post->cmdId, IPMSG_KNOCK))
  216. {
  217. post->contents.push_back(make_shared<KnockContent>());
  218. }
  219. return false;
  220. }
  221. };
  222. /**
  223. * @brief The AnsSendCheck class
  224. */
  225. class RecvSendCheck : public RecvProtocol
  226. {
  227. DECLARE_TRIGGER(RecvSendCheck)
  228. public:
  229. bool read(shared_ptr<Post> post)
  230. {
  231. if (IS_OPT_SET(post->cmdId, IPMSG_SENDCHECKOPT))
  232. trigger(post);
  233. return false;
  234. }
  235. };
  236. /**
  237. * @brief The RecvReadCheck class 接收到请求阅后通知
  238. */
  239. class RecvReadCheck : public RecvProtocol
  240. {
  241. DECLARE_TRIGGER(RecvReadCheck)
  242. public:
  243. bool read(shared_ptr<Post> post)
  244. {
  245. if (IS_OPT_SET(post->cmdId, IPMSG_READCHECKOPT))
  246. trigger(post);
  247. return false;
  248. }
  249. };
  250. /**
  251. * @brief The RecvText class 接收文本消息
  252. */
  253. class RecvText : public RecvProtocol
  254. {
  255. public:
  256. bool read(shared_ptr<Post> post)
  257. {
  258. if (!IS_CMD_SET(post->cmdId, IPMSG_SENDMSG))
  259. return false;
  260. auto& extra = post->extra;
  261. auto end = extra.end();
  262. auto begin = extra.begin();
  263. auto found = std::find(begin, end, 0);
  264. if (found != begin)//有找到0,且不是第一个字符
  265. {
  266. string rawText;
  267. rawText.assign(begin, found);
  268. auto content = createTextContent(encIn->convert(rawText));
  269. post->contents.push_back(shared_ptr<Content>(std::move(content)));
  270. }
  271. return false;
  272. }
  273. private:
  274. unique_ptr<TextContent> createTextContent(const string& raw)
  275. {
  276. auto content = unique_ptr<TextContent>(new TextContent());
  277. auto begin = raw.find('{');
  278. auto end = raw.find("}", begin+1);
  279. if (begin != raw.npos && end != raw.npos)
  280. {
  281. content->text = raw.substr(0, begin);
  282. content->format = raw.substr(begin+1, end-begin-1);
  283. }
  284. else
  285. {
  286. content->text = raw;
  287. }
  288. return content;
  289. }
  290. };
  291. class RecvFile : public RecvProtocol
  292. {
  293. public:
  294. bool read(shared_ptr<Post> post)
  295. {
  296. if (!IS_OPT_SET(post->cmdId, IPMSG_FILEATTACHOPT) || !IS_CMD_SET(post->cmdId, IPMSG_SENDMSG))
  297. return false;
  298. //文件任务信息紧随文本消息之后,中间相隔一个ascii 0
  299. //一个文件任务信息格式为fileId:filename:fileSize:modifyTime:fileType:其他扩展属性
  300. //多个文件任务以ascii 7分割
  301. //文件名含:,以::表示
  302. auto& extra = post->extra;
  303. auto end = extra.end();
  304. auto found = find(extra.begin(), end, 0)+1;
  305. while (found != end)
  306. {
  307. auto endTask = find(found, end, FILELIST_SEPARATOR);
  308. if (endTask == end)
  309. break;
  310. auto content = createFileContent(found, endTask);
  311. if (content != nullptr)
  312. {
  313. content->packetNo = stoul(post->packetNo);
  314. post->contents.push_back(shared_ptr<Content>(std::move(content)));
  315. }
  316. found = ++endTask;
  317. }
  318. return false;
  319. }
  320. private:
  321. unique_ptr<FileContent> createFileContent(vector<char>::iterator from,
  322. vector<char>::iterator to)
  323. {
  324. unique_ptr<FileContent> content(new FileContent());
  325. auto values = splitAllowSeperator(from, to, HLIST_ENTRY_SEPARATOR);
  326. const int fieldCount = 5;
  327. if (values.size() < fieldCount)
  328. return nullptr;
  329. content->fileId = stoi(values[0]);
  330. content->filename = encIn->convert(values[1]);
  331. content->size = stoi(values[2],0,16);
  332. content->modifyTime = stoi(values[3],0,16);
  333. content->fileType = stoi(values[4],0,16);
  334. return content;
  335. }
  336. };
  337. class Debuger : public RecvProtocol
  338. {
  339. public:
  340. bool read(shared_ptr<Post> post)
  341. {
  342. cout<<"==========================="<<endl;
  343. cout<<"cmd id : "<<std::hex<<post->cmdId<<endl;
  344. cout<<"from: "<<post->from->toString()<<endl;
  345. int count = 0;
  346. for (unsigned char ch : post->extra){
  347. cout<<setw(2)<<setfill('0')<<hex<<(unsigned int)ch<<" ";
  348. if (++count >= 8){
  349. cout<<endl;
  350. count=0;
  351. }
  352. }
  353. cout<<endl;
  354. return false;
  355. }
  356. };
  357. class RecvReadMessage : public RecvProtocol
  358. {
  359. DECLARE_TRIGGER(RecvReadMessage)
  360. public:
  361. bool read(shared_ptr<Post> post)
  362. {
  363. if (post->cmdId == IPMSG_RECVMSG)
  364. {
  365. IdType id = static_cast<IdType>(stoll(toString(post->extra)));
  366. auto content = make_shared<IdContent>();
  367. content->id = id;
  368. post->addContent(content);
  369. trigger(post);
  370. return true;
  371. }
  372. return false;
  373. }
  374. };
  375. class RecvImage : public RecvProtocol
  376. {
  377. public:
  378. bool read(shared_ptr<Post> post)
  379. {
  380. if (IS_CMD_SET(post->cmdId, IPMSG_SENDIMAGE)
  381. && IS_OPT_SET(post->cmdId, IPMSG_FILEATTACHOPT))
  382. {
  383. char id[9]={0};
  384. memcpy(id, post->extra.data(), 8);
  385. auto content = make_shared<ImageContent>();
  386. content->id = id;
  387. post->contents.push_back(content);
  388. }
  389. return false;
  390. }
  391. };
  392. /**
  393. * @brief The EndRecv class 终止解析链
  394. */
  395. class EndRecv : public RecvProtocol
  396. {
  397. DECLARE_TRIGGER(EndRecv)
  398. public:
  399. bool read(shared_ptr<Post> post)
  400. {
  401. if (!post->contents.empty())
  402. trigger(post);
  403. return true;
  404. }
  405. };
  406. //添加一条接收协议,触发时更新好友信息,并调用func
  407. #define ADD_RECV_PROTOCOL(protocol, func)\
  408. {\
  409. RecvProtocol* p = new protocol([this](shared_ptr<Post> post){\
  410. post->from = this->addOrUpdateFellow(post->from);\
  411. this->func(post);});\
  412. mRecvProtocols.push_back(unique_ptr<RecvProtocol>(p));\
  413. mCommu.addRecvProtocol(p);\
  414. }
  415. //添加一条接收协议,无触发
  416. #define ADD_RECV_PROTOCOL2(protocol)\
  417. {\
  418. RecvProtocol* p = new protocol();\
  419. mRecvProtocols.push_back(unique_ptr<RecvProtocol>(p));\
  420. mCommu.addRecvProtocol(p);\
  421. }
  422. //添加一条接收协议,触发时更新好友信息
  423. #define ADD_RECV_PROTOCOL3(protocol)\
  424. {\
  425. RecvProtocol* p = new protocol([this](shared_ptr<Post> post){\
  426. post->from = this->addOrUpdateFellow(post->from);});\
  427. mRecvProtocols.push_back(unique_ptr<RecvProtocol>(p));\
  428. mCommu.addRecvProtocol(p);\
  429. }
  430. //添加一条发送协议
  431. #define ADD_SEND_PROTOCOL(protocol, sender, args...)\
  432. {\
  433. mContentSender[protocol]=make_shared<sender>(##args);\
  434. }
  435. FeiqEngine::FeiqEngine()
  436. {
  437. ADD_RECV_PROTOCOL2(Debuger);//仅用于开发中的调试
  438. ADD_RECV_PROTOCOL3(RecvAnsEntry);
  439. ADD_RECV_PROTOCOL(RecvBrEntry, onBrEntry);
  440. ADD_RECV_PROTOCOL3(RecvBrExit);
  441. ADD_RECV_PROTOCOL(RecvSendCheck, onSendCheck);
  442. ADD_RECV_PROTOCOL(RecvReadCheck, onReadCheck);
  443. ADD_RECV_PROTOCOL(RecvReadMessage, onReadMessage);//好友回复消息已经阅读
  444. ADD_RECV_PROTOCOL2(RecvText);
  445. ADD_RECV_PROTOCOL2(RecvImage);
  446. ADD_RECV_PROTOCOL2(RecvKnock);
  447. ADD_RECV_PROTOCOL2(RecvFile);
  448. ADD_RECV_PROTOCOL(EndRecv, onMsg);
  449. ADD_SEND_PROTOCOL(ContentType::Text, SendTextContent);
  450. ADD_SEND_PROTOCOL(ContentType::Knock, SendKnockContent);
  451. ADD_SEND_PROTOCOL(ContentType::File, SendFileContent);
  452. mCommu.setFileServerHandler(std::bind(&FeiqEngine::fileServerHandler,
  453. this,
  454. placeholders::_1,
  455. placeholders::_2,
  456. placeholders::_3,
  457. placeholders::_4));
  458. }
  459. pair<bool, string> FeiqEngine::send(shared_ptr<Fellow> fellow, shared_ptr<Content> content)
  460. {
  461. if (content == nullptr)
  462. return {false, "要发送的内容无效"};
  463. auto& sender = mContentSender[content->type()];
  464. if (sender == nullptr)
  465. return {false, "no send protocol can send"};
  466. sender->setContent(content.get());
  467. auto ip = fellow->getIp();
  468. auto ret = mCommu.send(ip, *sender);
  469. if (ret.first == 0)
  470. {
  471. return {false, ret.second};
  472. }
  473. content->setPacketNo(ret.first);
  474. if (content->type() == ContentType::File){
  475. auto ptr = dynamic_pointer_cast<FileContent>(content);
  476. mModel.addUploadTask(fellow, ptr)->setObserver(mView);
  477. }
  478. else if (content->type() == ContentType::Text){
  479. auto handler = std::bind(&FeiqEngine::onSendTimeo, this, placeholders::_1, ip, content);
  480. mAsyncWait.addWaitPack(content->packetNo, handler, 5000);
  481. }
  482. return {true, ""};
  483. }
  484. pair<bool, string> FeiqEngine::sendFiles(shared_ptr<Fellow> fellow, list<shared_ptr<FileContent>> &files)
  485. {
  486. for (auto file : files) {
  487. auto ret = send(fellow, file);
  488. if (!ret.first)
  489. return ret;
  490. }
  491. return {true,""};
  492. }
  493. bool FeiqEngine::downloadFile(FileTask* task)
  494. {
  495. if (task==nullptr)
  496. return false;
  497. task->setObserver(mView);
  498. auto func = [task, this](){
  499. auto fellow = task->fellow();
  500. auto content = task->getContent();
  501. auto client = mCommu.requestFileData(fellow->getIp(), *content, 0);
  502. if (client == nullptr)
  503. {
  504. task->setState(FileTaskState::Error, "请求下载文件失败,可能好友已经取消");
  505. return;
  506. }
  507. FILE* of = fopen(content->path.c_str(), "w+");
  508. if (of == nullptr){
  509. task->setState(FileTaskState::Error, "无法打开文件进行保存");
  510. return;
  511. }
  512. // Defer{//TODO:工作异常
  513. // [of](){
  514. // cout<<"close file now"<<endl;
  515. // fclose(of);
  516. // }
  517. // };
  518. const int unitSize = 2048;//一次请求2k
  519. const int maxTimeoCnt = 3;//最多允许超时3次
  520. const int timeo = 2000;//允许超时2s
  521. int recv = 0;
  522. auto total = content->size;
  523. std::array<char, unitSize> buf;
  524. int timeoCnt = 0;
  525. task->setState(FileTaskState::Running);
  526. while (recv < total)
  527. {
  528. if (task->hasCancelPending())
  529. {
  530. task->setState(FileTaskState::Canceled);
  531. fclose(of);
  532. return;
  533. }
  534. auto left = total - recv;
  535. auto request = unitSize > left ? left : unitSize;
  536. auto got = client->recv(buf.data(), request, timeo);
  537. if (got == -1 && ++timeoCnt >= maxTimeoCnt)
  538. {
  539. task->setState(FileTaskState::Error, "下载文件超时,好友可能掉线");
  540. fclose(of);
  541. return;
  542. }
  543. else if (got < 0)
  544. {
  545. task->setState(FileTaskState::Error, "接收数据出错,可能网络错误");
  546. fclose(of);
  547. return;
  548. }
  549. else
  550. {
  551. fwrite(buf.data(), 1, got, of);
  552. recv+=got;
  553. task->setProcess(recv);
  554. }
  555. }
  556. fclose(of);
  557. task->setProcess(total);
  558. task->setState(FileTaskState::Finish);
  559. };
  560. thread thd(func);
  561. thd.detach();
  562. return task;
  563. }
  564. class GetPubKey : public SendProtocol
  565. {
  566. public:
  567. int cmdId() {return IPMSG_GETPUBKEY;}
  568. void write(ostream& os){
  569. (void)os;
  570. }
  571. };
  572. pair<bool, string> FeiqEngine::start()
  573. {
  574. if (mStarted)
  575. {
  576. return {true, "已经启动过"};
  577. }
  578. mCommu.setMyHost(encOut->convert(mHost));
  579. mCommu.setMyName(encOut->convert(mName));
  580. auto result = mCommu.start();
  581. if(result.first)
  582. {
  583. mAsyncWait.start();
  584. mMsgThd.start();
  585. mMsgThd.setHandler(std::bind(&FeiqEngine::dispatchMsg, this, placeholders::_1));
  586. mStarted = true;
  587. sendImOnLine();
  588. }
  589. return result;
  590. }
  591. void FeiqEngine::stop()
  592. {
  593. if (mStarted)
  594. {
  595. mStarted=false;
  596. SendImOffLine imOffLine(mName);
  597. mCommu.send("255.255.255.255", imOffLine);
  598. broadcastToCurstomGroup(imOffLine);
  599. mCommu.stop();
  600. mAsyncWait.stop();
  601. mMsgThd.stop();
  602. }
  603. }
  604. void FeiqEngine::addToBroadcast(const string &ip)
  605. {
  606. mBroadcast.push_back(ip);
  607. }
  608. void FeiqEngine::setMyHost(string host)
  609. {
  610. mHost=host;
  611. if (mName.empty())
  612. mName = mHost;
  613. }
  614. void FeiqEngine::setMyName(string name){
  615. mName=name;
  616. if (mName.empty())
  617. mName = mHost;
  618. }
  619. void FeiqEngine::sendImOnLine(const string &ip)
  620. {
  621. SendImOnLine imOnLine(mName);
  622. if (ip.empty())
  623. {
  624. mCommu.send("255.255.255.255", imOnLine);
  625. broadcastToCurstomGroup(imOnLine);
  626. }
  627. else
  628. {
  629. mCommu.send(ip, imOnLine);
  630. }
  631. }
  632. void FeiqEngine::enableIntervalDetect(int seconds)
  633. {
  634. thread thd([this, seconds](){
  635. while(mStarted)
  636. {
  637. sleep(seconds);
  638. if (!mStarted) break;
  639. SendImOnLine imOnLine(mName);
  640. broadcastToCurstomGroup(imOnLine);
  641. }
  642. });
  643. thd.detach();
  644. }
  645. FeiqModel &FeiqEngine::getModel()
  646. {
  647. return mModel;
  648. }
  649. void FeiqEngine::onBrEntry(shared_ptr<Post> post)
  650. {
  651. AnsBrEntry ans(mName);
  652. mCommu.send(post->from->getIp(), ans);
  653. }
  654. void FeiqEngine::onMsg(shared_ptr<Post> post)
  655. {
  656. static vector<string> rejectedImages;
  657. auto event = make_shared<MessageViewEvent>();
  658. event->when = post->when;
  659. event->fellow = post->from;
  660. auto it = post->contents.begin();
  661. auto end = post->contents.end();
  662. string reply;
  663. while (it != end)//过滤消息内容:删除不支持的包,并回复好友
  664. {
  665. bool rejected = false;
  666. if ((*it)->type() == ContentType::File)
  667. {
  668. auto fc = static_pointer_cast<FileContent>(*it);
  669. if (fc->fileType == IPMSG_FILE_REGULAR)//TODO:与飞秋的文件夹传输协议还没支持
  670. mModel.addDownloadTask(event->fellow, fc);
  671. else if (fc->fileType == IPMSG_FILE_DIR)
  672. {
  673. rejected=true;
  674. reply+="Mac飞秋还不支持接收目录:"+fc->filename+"\n";
  675. }
  676. }
  677. else if ((*it)->type() == ContentType::Text)
  678. {
  679. auto tc = static_cast<TextContent*>((*it).get());
  680. string begin = "/~#>";
  681. string end = "<B~";
  682. if (startsWith(tc->text, begin) && endsWith(tc->text, end))
  683. {
  684. rejected=true;
  685. }
  686. }
  687. else if ((*it)->type() == ContentType::Image)
  688. {
  689. //这个包还没被拒绝过,发送拒绝消息
  690. auto ic = static_cast<ImageContent*>((*it).get());
  691. if (std::find(rejectedImages.begin(), rejectedImages.end(), ic->id)==rejectedImages.end())
  692. {
  693. reply+="Mac飞秋还不支持接收图片,请用文件形式发送图片\n";
  694. rejectedImages.push_back(ic->id);
  695. }
  696. rejected=true;
  697. }
  698. if (!rejected)
  699. {
  700. event->contents.push_back(*it);
  701. }
  702. ++it;
  703. }
  704. if (!reply.empty())
  705. {
  706. SendTextContent send;
  707. TextContent content;
  708. content.text = reply;
  709. send.setContent(&content);
  710. mCommu.send(post->from->getIp(), send);
  711. }
  712. if (!event->contents.empty())
  713. mMsgThd.sendMessage(event);
  714. }
  715. void FeiqEngine::onSendCheck(shared_ptr<Post> post)
  716. {
  717. SendSentCheck reply(post->packetNo);
  718. mCommu.send(post->from->getIp(), reply);
  719. }
  720. void FeiqEngine::onReadCheck(shared_ptr<Post> post)
  721. {
  722. SendReadCheck reply(post->packetNo);
  723. mCommu.send(post->from->getIp(), reply);
  724. }
  725. void FeiqEngine::onSendTimeo(IdType packetId, const string& ip, shared_ptr<Content> content)
  726. {
  727. auto event = make_shared<SendTimeoEvent>();
  728. event->fellow = mModel.findFirstFellowOf(ip);
  729. if (event->fellow == nullptr)
  730. return;
  731. event->content = content;
  732. mMsgThd.sendMessage(event);
  733. }
  734. void FeiqEngine::onReadMessage(shared_ptr<Post> post)
  735. {
  736. if (post->contents.empty())
  737. return;
  738. auto content = dynamic_pointer_cast<IdContent>(post->contents[0]);
  739. mAsyncWait.clearWaitPack(content->id);
  740. }
  741. void FeiqEngine::fileServerHandler(unique_ptr<TcpSocket> client, int packetNo, int fileId, int offset)
  742. {
  743. auto task = mModel.findTask(packetNo, fileId);
  744. if (task == nullptr)
  745. return;
  746. auto func = [task, offset](unique_ptr<TcpSocket> client){
  747. FILE* is = fopen(task->getContent()->path.c_str(), "r");
  748. if (is == nullptr)
  749. {
  750. task->setState(FileTaskState::Error, "无法读取文件");
  751. }
  752. // Defer{
  753. // [is](){
  754. // fclose(is);
  755. // }
  756. // };
  757. if (offset > 0)
  758. fseek(is, offset, SEEK_SET);
  759. const int unitSize = 2048;//一次发送2k
  760. std::array<char, unitSize> buf;
  761. auto total = task->getContent()->size;
  762. int sent = 0;
  763. task->setState(FileTaskState::Running);
  764. while (sent < total && !feof(is))
  765. {
  766. auto left = total - sent;
  767. auto request = unitSize > left ? left : unitSize;
  768. int got = fread(buf.data(), 1, request, is);
  769. got = client->send(buf.data(), got);
  770. if (got < 0)
  771. {
  772. task->setState(FileTaskState::Error, "无法发送数据,可能是网络问题");
  773. fclose(is);
  774. return;
  775. }
  776. sent+=got;
  777. task->setProcess(sent);
  778. }
  779. if (sent != total)
  780. {
  781. task->setState(FileTaskState::Error, "文件未完整发送,可能是发送期间文件被改动");
  782. }
  783. else
  784. {
  785. task->setProcess(total);
  786. task->setState(FileTaskState::Finish);
  787. }
  788. fclose(is);
  789. };
  790. thread thd(func, std::move(client));
  791. thd.detach();
  792. }
  793. shared_ptr<Fellow> FeiqEngine::addOrUpdateFellow(shared_ptr<Fellow> fellow)
  794. {
  795. auto f = mModel.getFullInfoOf(fellow);
  796. bool shouldApdate = false;
  797. if (f == nullptr)
  798. {
  799. mModel.addFellow(fellow);
  800. f = fellow;
  801. shouldApdate = true;
  802. }
  803. else
  804. {
  805. if (f->update(*fellow))
  806. shouldApdate = true;
  807. }
  808. if (shouldApdate){
  809. auto event = make_shared<FellowViewEvent>();
  810. event->what = ViewEventType::FELLOW_UPDATE;
  811. event->fellow = f;
  812. event->when = Post::now();
  813. mMsgThd.sendMessage(event);
  814. }
  815. return f;
  816. }
  817. void FeiqEngine::dispatchMsg(shared_ptr<ViewEvent> msg)
  818. {
  819. mView->onEvent(msg);
  820. }
  821. void FeiqEngine::broadcastToCurstomGroup(SendProtocol &protocol)
  822. {
  823. for (auto ip : mBroadcast)
  824. {
  825. if (!mStarted)
  826. break;//发送过程是一个耗时网络操作,如果已经stop,则中断
  827. mCommu.send(ip, protocol);
  828. }
  829. }

mac下的“飞秋”大多数只是飞鸽传书协议,而且未发现令人满意的开源项目,所以基于c++与qt实现了基础的飞秋协议。

Contributors (1)