Connection.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /*
  2. * Connection.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "Connection.h"
  12. #include "../registerTypes/RegisterTypes.h"
  13. #include "../mapping/CMap.h"
  14. #include "../CGameState.h"
  15. #include <boost/asio.hpp>
  16. VCMI_LIB_NAMESPACE_BEGIN
  17. using namespace boost;
  18. using namespace boost::asio::ip;
  19. #if defined(__hppa__) || \
  20. defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
  21. (defined(__MIPS__) && defined(__MISPEB__)) || \
  22. defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
  23. defined(__sparc__)
  24. #define BIG_ENDIAN
  25. #else
  26. #define LIL_ENDIAN
  27. #endif
  28. void CConnection::init()
  29. {
  30. enableBufferedWrite = false;
  31. socket->set_option(boost::asio::ip::tcp::no_delay(true));
  32. try
  33. {
  34. socket->set_option(boost::asio::socket_base::send_buffer_size(4194304));
  35. socket->set_option(boost::asio::socket_base::receive_buffer_size(4194304));
  36. }
  37. catch (const boost::system::system_error & e)
  38. {
  39. logNetwork->error("error setting socket option: %s", e.what());
  40. }
  41. enableSmartPointerSerialization();
  42. disableStackSendingByID();
  43. registerTypes(iser);
  44. registerTypes(oser);
  45. #ifdef LIL_ENDIAN
  46. myEndianess = true;
  47. #else
  48. myEndianess = false;
  49. #endif
  50. connected = true;
  51. std::string pom;
  52. //we got connection
  53. oser & std::string("Aiya!\n") & name & uuid & myEndianess; //identify ourselves
  54. iser & pom & pom & contactUuid & contactEndianess;
  55. logNetwork->info("Established connection with %s. UUID: %s", pom, contactUuid);
  56. mutexRead = std::make_shared<boost::mutex>();
  57. mutexWrite = std::make_shared<boost::mutex>();
  58. iser.fileVersion = SERIALIZATION_VERSION;
  59. }
  60. CConnection::CConnection(std::string host, ui16 port, std::string Name, std::string UUID)
  61. : io_service(std::make_shared<asio::io_service>()), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0)
  62. {
  63. int i;
  64. boost::system::error_code error = asio::error::host_not_found;
  65. socket = std::make_shared<tcp::socket>(*io_service);
  66. tcp::resolver resolver(*io_service);
  67. tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)),error);
  68. if(error)
  69. {
  70. logNetwork->error("Problem with resolving: \n%s", error.message());
  71. goto connerror1;
  72. }
  73. pom = endpoint_iterator;
  74. if(pom != end)
  75. logNetwork->info("Found endpoints:");
  76. else
  77. {
  78. logNetwork->error("Critical problem: No endpoints found!");
  79. goto connerror1;
  80. }
  81. i=0;
  82. while(pom != end)
  83. {
  84. logNetwork->info("\t%d:%s", i, (boost::asio::ip::tcp::endpoint&)*pom);
  85. pom++;
  86. }
  87. i=0;
  88. while(endpoint_iterator != end)
  89. {
  90. logNetwork->info("Trying connection to %s(%d)", (boost::asio::ip::tcp::endpoint&)*endpoint_iterator, i++);
  91. socket->connect(*endpoint_iterator, error);
  92. if(!error)
  93. {
  94. init();
  95. return;
  96. }
  97. else
  98. {
  99. logNetwork->error("Problem with connecting: %s", error.message());
  100. }
  101. endpoint_iterator++;
  102. }
  103. //we shouldn't be here - error handling
  104. connerror1:
  105. logNetwork->error("Something went wrong... checking for error info");
  106. if(error)
  107. logNetwork->error(error.message());
  108. else
  109. logNetwork->error("No error info. ");
  110. throw std::runtime_error("Can't establish connection :(");
  111. }
  112. CConnection::CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID)
  113. : iser(this), oser(this), socket(Socket), name(Name), uuid(UUID), connectionID(0)
  114. {
  115. init();
  116. }
  117. CConnection::CConnection(std::shared_ptr<TAcceptor> acceptor, std::shared_ptr<boost::asio::io_service> io_service, std::string Name, std::string UUID)
  118. : io_service(io_service), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0)
  119. {
  120. boost::system::error_code error = asio::error::host_not_found;
  121. socket = std::make_shared<tcp::socket>(*io_service);
  122. acceptor->accept(*socket,error);
  123. if (error)
  124. {
  125. logNetwork->error("Error on accepting: %s", error.message());
  126. socket.reset();
  127. throw std::runtime_error("Can't establish connection :(");
  128. }
  129. init();
  130. }
  131. void CConnection::flushBuffers()
  132. {
  133. if(!enableBufferedWrite)
  134. return;
  135. try
  136. {
  137. asio::write(*socket, writeBuffer);
  138. }
  139. catch(...)
  140. {
  141. //connection has been lost
  142. connected = false;
  143. throw;
  144. }
  145. enableBufferedWrite = false;
  146. }
  147. int CConnection::write(const void * data, unsigned size)
  148. {
  149. try
  150. {
  151. if(enableBufferedWrite)
  152. {
  153. std::ostream ostream(&writeBuffer);
  154. ostream.write(static_cast<const char *>(data), size);
  155. return size;
  156. }
  157. int ret;
  158. ret = static_cast<int>(asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size))));
  159. return ret;
  160. }
  161. catch(...)
  162. {
  163. //connection has been lost
  164. connected = false;
  165. throw;
  166. }
  167. }
  168. int CConnection::read(void * data, unsigned size)
  169. {
  170. try
  171. {
  172. if(enableBufferedRead)
  173. {
  174. auto available = readBuffer.size();
  175. while(available < size)
  176. {
  177. auto bytesRead = socket->read_some(readBuffer.prepare(1024));
  178. readBuffer.commit(bytesRead);
  179. available = readBuffer.size();
  180. }
  181. std::istream istream(&readBuffer);
  182. istream.read(static_cast<char *>(data), size);
  183. return size;
  184. }
  185. int ret = static_cast<int>(asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size))));
  186. return ret;
  187. }
  188. catch(...)
  189. {
  190. //connection has been lost
  191. connected = false;
  192. throw;
  193. }
  194. }
  195. CConnection::~CConnection()
  196. {
  197. if(handler)
  198. handler->join();
  199. close();
  200. }
  201. template<class T>
  202. CConnection & CConnection::operator&(const T &t) {
  203. // throw std::exception();
  204. //XXX this is temporaly ? solution to fix gcc (4.3.3, other?) compilation
  205. // problem for more details contact [email protected] or [email protected]
  206. // do not remove this exception it shoudnt be called
  207. return *this;
  208. }
  209. void CConnection::close()
  210. {
  211. if(socket)
  212. {
  213. socket->close();
  214. socket.reset();
  215. }
  216. }
  217. bool CConnection::isOpen() const
  218. {
  219. return socket && connected;
  220. }
  221. void CConnection::reportState(vstd::CLoggerBase * out)
  222. {
  223. out->debug("CConnection");
  224. if(socket && socket->is_open())
  225. {
  226. out->debug("\tWe have an open and valid socket");
  227. out->debug("\t %d bytes awaiting", socket->available());
  228. }
  229. }
  230. CPack * CConnection::retrievePack()
  231. {
  232. enableBufferedRead = true;
  233. CPack * pack = nullptr;
  234. boost::unique_lock<boost::mutex> lock(*mutexRead);
  235. iser & pack;
  236. logNetwork->trace("Received CPack of type %s", (pack ? typeid(*pack).name() : "nullptr"));
  237. if(pack == nullptr)
  238. {
  239. logNetwork->error("Received a nullptr CPack! You should check whether client and server ABI matches.");
  240. }
  241. else
  242. {
  243. pack->c = this->shared_from_this();
  244. }
  245. enableBufferedRead = false;
  246. return pack;
  247. }
  248. void CConnection::sendPack(const CPack * pack)
  249. {
  250. boost::unique_lock<boost::mutex> lock(*mutexWrite);
  251. logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
  252. enableBufferedWrite = true;
  253. oser & pack;
  254. flushBuffers();
  255. }
  256. void CConnection::disableStackSendingByID()
  257. {
  258. CSerializer::sendStackInstanceByIds = false;
  259. }
  260. void CConnection::enableStackSendingByID()
  261. {
  262. CSerializer::sendStackInstanceByIds = true;
  263. }
  264. void CConnection::disableSmartPointerSerialization()
  265. {
  266. iser.smartPointerSerialization = oser.smartPointerSerialization = false;
  267. }
  268. void CConnection::enableSmartPointerSerialization()
  269. {
  270. iser.smartPointerSerialization = oser.smartPointerSerialization = true;
  271. }
  272. void CConnection::enterLobbyConnectionMode()
  273. {
  274. iser.loadedPointers.clear();
  275. oser.savedPointers.clear();
  276. disableSmartVectorMemberSerialization();
  277. disableSmartPointerSerialization();
  278. }
  279. void CConnection::enterGameplayConnectionMode(CGameState * gs)
  280. {
  281. enableStackSendingByID();
  282. disableSmartPointerSerialization();
  283. addStdVecItems(gs);
  284. }
  285. void CConnection::disableSmartVectorMemberSerialization()
  286. {
  287. CSerializer::smartVectorMembersSerialization = false;
  288. }
  289. void CConnection::enableSmartVectorMemberSerializatoin()
  290. {
  291. CSerializer::smartVectorMembersSerialization = true;
  292. }
  293. std::string CConnection::toString() const
  294. {
  295. boost::format fmt("Connection with %s (ID: %d UUID: %s)");
  296. fmt % name % connectionID % uuid;
  297. return fmt.str();
  298. }
  299. VCMI_LIB_NAMESPACE_END