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.

feiqcommu.cpp 6.9 kB

8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. #include "feiqcommu.h"
  2. #include "udpcommu.h"
  3. #include "ipmsg.h"
  4. #include <arpa/inet.h>
  5. #include "fellow.h"
  6. #include <sstream>
  7. #include <QDebug>
  8. #include <limits.h>
  9. #include "utils.h"
  10. FeiqCommu::FeiqCommu()
  11. {
  12. }
  13. void FeiqCommu::setMyHost(string host){mHost=host;}
  14. void FeiqCommu::setMyName(string name){
  15. mName=name;
  16. std::replace(mName.begin(), mName.end(), HLIST_ENTRY_SEPARATOR, (char)HOSTLIST_DUMMY);
  17. }
  18. void FeiqCommu::addRecvProtocol(RecvProtocol *protocol){
  19. mRecvPrtocols.push_back(protocol);
  20. }
  21. pair<bool, string> FeiqCommu::start()
  22. {
  23. if (!mUdp.bindTo(IPMSG_PORT))
  24. {
  25. return {false, "bind failed:"+mUdp.getErrMsg()};
  26. }
  27. if (!mUdp.startAsyncRecv(
  28. std::bind(&FeiqCommu::onRecv, this, placeholders::_1,placeholders::_2)
  29. )
  30. )
  31. {
  32. mUdp.close();
  33. return {false, "start aysnc recv failed:"+mUdp.getErrMsg()};
  34. }
  35. if (!mTcpServer.start(IPMSG_PORT)){
  36. mUdp.close();
  37. return {false, "无法启动文件服务"};
  38. }
  39. mTcpServer.whenNewClient(std::bind(&FeiqCommu::onTcpClientConnected, this, placeholders::_1));
  40. //其他字段是什么意思呢?
  41. mMac = mUdp.getBoundMac();
  42. mVersion = "1_lbt6_0#128#"+mMac+"#0#0#0#4001#9";
  43. return {true, ""};
  44. }
  45. void FeiqCommu::stop()
  46. {
  47. mUdp.close();
  48. }
  49. pair<IdType, string> FeiqCommu::send(const string &ip, SendProtocol &sender)
  50. {
  51. //打包
  52. IdType packetNo=0;
  53. auto out = pack(sender, &packetNo);
  54. //发送
  55. auto ret = mUdp.sentTo(ip, IPMSG_PORT, out.data(), out.size());
  56. if (ret < 0)
  57. return {0, mUdp.getErrMsg()};
  58. return {packetNo, ""};
  59. }
  60. class SendRequestFile : public SendProtocol
  61. {
  62. public:
  63. int filetype=IPMSG_FILE_REGULAR;
  64. int fileid;
  65. int offset=0;
  66. int packetNo;
  67. int cmdId() override {return filetype == IPMSG_FILE_DIR ? IPMSG_GETDIRFILES : IPMSG_GETFILEDATA;}
  68. void write(ostream& os) override
  69. {
  70. char sep = HLIST_ENTRY_SEPARATOR;
  71. os<<std::hex<<packetNo<<sep
  72. <<fileid<<sep
  73. <<offset<<sep;
  74. }
  75. };
  76. unique_ptr<TcpSocket> FeiqCommu::requestFileData(const string &ip,
  77. const FileContent& file, int offset)
  78. {
  79. unique_ptr<TcpSocket> client(new TcpSocket());
  80. if (!client->connect(ip, IPMSG_PORT))
  81. return nullptr;
  82. SendRequestFile requestSender;
  83. requestSender.packetNo = file.packetNo;
  84. requestSender.fileid = file.fileId;
  85. requestSender.offset = offset;
  86. auto request = pack(requestSender);
  87. int ret = client->send(request.data(), request.size());
  88. if (ret < 0)
  89. return nullptr;
  90. return client;
  91. }
  92. void FeiqCommu::setFileServerHandler(FileServerHandler fileServerHandler)
  93. {
  94. mFileServerHandler = fileServerHandler;
  95. }
  96. void FeiqCommu::onRecv(const string &ip, vector<char> &data)
  97. {
  98. auto post = make_shared<Post>();
  99. post->from->setIp(ip);
  100. //解析
  101. if (!dumpRaw(data, *post))
  102. return;
  103. //尝试获取mac
  104. auto info = dumpVersionInfo(post->from->version());
  105. post->from->setMac(info.mac);
  106. //屏蔽自己的包
  107. if (mMac == post->from->getMac()//匹配mac
  108. && mName == post->from->getName())//再匹配名字,以防mac获取失败
  109. {
  110. return;
  111. }
  112. //除非收到下线包,否则都认为在线
  113. post->from->setOnLine(true);
  114. //调用协议处理
  115. for (auto& handler : mRecvPrtocols)
  116. {
  117. if (handler->read(post))
  118. break;
  119. }
  120. }
  121. vector<char> FeiqCommu::pack(SendProtocol &sender, IdType* packetId)
  122. {
  123. //搜集数据
  124. char sep = HLIST_ENTRY_SEPARATOR;
  125. auto packetNo = mPacketNo.get();
  126. auto cmdId = sender.cmdId();
  127. //拼接消息头
  128. stringstream os;
  129. os<<mVersion<<sep<<packetNo<<sep<<mName<<sep<<mHost<<sep<<cmdId<<sep;
  130. //组装消息
  131. sender.write(os);
  132. os.put(0);
  133. os.seekg(0, os.end);
  134. auto len = os.tellg();
  135. os.seekg(0, os.beg);
  136. vector<char> buf(len);
  137. os.read(buf.data(), len);
  138. if (packetId != nullptr)
  139. *packetId = packetNo;
  140. return buf;
  141. }
  142. void FeiqCommu::onTcpClientConnected(int socket)
  143. {
  144. if (mFileServerHandler)
  145. {
  146. //接收请求
  147. unique_ptr<TcpSocket> client(new TcpSocket(socket));
  148. std::array<char,MAX_RCV_SIZE> buf;
  149. int ret = client->recv(buf.data(), MAX_RCV_SIZE);
  150. if (ret <= 0)
  151. return;
  152. //解析请求
  153. vector<char> request(ret);
  154. std::copy(buf.begin(), buf.begin()+ret, request.begin());
  155. Post post;
  156. if (!dumpRaw(request, post))
  157. return;
  158. auto values = splitAllowSeperator(post.extra.begin(), post.extra.end(), HLIST_ENTRY_SEPARATOR);
  159. if (values.size() < 3)
  160. return;
  161. int packetNo = stoi(values[0], 0, 16);
  162. int fileId = stoi(values[1], 0, 16);
  163. int offset = stoi(values[2], 0, 16);
  164. //处理请求
  165. mFileServerHandler(std::move(client), packetNo, fileId, offset);
  166. }
  167. }
  168. bool FeiqCommu::dumpRaw(vector<char>& data, Post& post)
  169. {
  170. auto ptr = data.begin();
  171. auto last = data.end();
  172. //取出协议的前5项
  173. array<string, 5> value;
  174. auto count = 0uL;
  175. while (count < value.size()) {
  176. auto found = std::find(ptr, last, HLIST_ENTRY_SEPARATOR);
  177. if (found == last)
  178. break;
  179. auto size = std::distance(ptr, found);
  180. if (size == 0)
  181. {
  182. value[count]="";
  183. }
  184. else
  185. {
  186. vector<char> buf(size+1);
  187. std::copy(ptr, found, buf.begin());
  188. buf.push_back(0);
  189. std::replace(buf.begin(), buf.end(), HOSTLIST_DUMMY, HLIST_ENTRY_SEPARATOR);
  190. value[count]=toString(buf);//TODO:是否有编码问题?
  191. }
  192. ptr=found+1;
  193. ++count;
  194. }
  195. //协议错误
  196. if (count < value.size())
  197. return false;
  198. //解析
  199. post.from->setVersion(value[0]);
  200. post.packetNo = value[1];
  201. if (!post.from)
  202. post.from=make_shared<Fellow>();
  203. post.from->setPcName(value[2]);
  204. post.from->setHost(value[3]);
  205. post.cmdId = stoull(value[4]);
  206. //取出extra部分
  207. if (ptr != last)
  208. {
  209. auto size = std::distance(ptr, last);
  210. vector<char> buf(size);
  211. std::copy(ptr, last, buf.begin());
  212. post.extra = buf;
  213. }
  214. return true;
  215. }
  216. VersionInfo FeiqCommu::dumpVersionInfo(const string &version)
  217. {
  218. const char sep = '#';
  219. VersionInfo info;
  220. auto tail = version.end();
  221. auto head = version.begin();
  222. //1
  223. auto begin = std::find(head, tail, sep);
  224. if (begin == tail)
  225. return info;
  226. //2=begin
  227. begin = std::find(begin+1, tail, sep);
  228. if (begin == tail)
  229. return info;
  230. ++begin;
  231. //3=end
  232. auto end = std::find(begin, tail, sep);
  233. if (end == tail)
  234. return info;
  235. info.mac = version.substr(distance(head,begin), distance(begin,end));
  236. return info;
  237. }

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

Contributors (1)