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

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

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

Contributors (1)