NetworkChannel.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /////////////////////////////////////////////////////////////////////////////
  2. // NetworkChannel.cpp
  3. //
  4. // Copyright (c) Electronic Arts Inc. All rights reserved.
  5. /////////////////////////////////////////////////////////////////////////////
  6. #include <EABase/eabase.h>
  7. #include <EAAssert/eaassert.h>
  8. #include <EAMain/internal/EAMainChannels.h>
  9. #if defined(EA_PLATFORM_IPHONE)
  10. #define EAMAIN_HAS_NETWORK_CHANNEL 1
  11. #define EAMAIN_FREOPEN_SUPPORTED 1
  12. #endif
  13. #if defined(EA_PLATFORM_WINRT)
  14. #define EAMAIN_HAS_NETWORK_CHANNEL 1
  15. #define EAMAIN_FREOPEN_SUPPORTED 0
  16. #endif
  17. #if defined(EA_PLATFORM_WINDOWS_PHONE) && !defined(EAMAIN_HAS_NETWORK_CHANNEL)
  18. #define EAMAIN_HAS_NETWORK_CHANNEL 1
  19. #define EAMAIN_FREOPEN_SUPPORTED 0
  20. #endif
  21. // winrt-arm configurations do not have winsock, so for these configurations
  22. // we disable the use of the network channel.
  23. #if defined(_MSC_VER) && !defined(EA_PLATFORM_WINDOWS_PHONE) && defined(EA_PROCESSOR_ARM) && defined(EAMAIN_HAS_NETWORK_CHANNEL)
  24. #undef EAMAIN_HAS_NETWORK_CHANNEL
  25. #endif
  26. #if defined(EA_PLATFORM_CAPILANO) && !defined(EAMAIN_HAS_NETWORK_CHANNEL)
  27. #define EAMAIN_HAS_NETWORK_CHANNEL 1
  28. #define EAMAIN_FREOPEN_SUPPORTED 1
  29. #endif
  30. #if !defined(EAMAIN_HAS_NETWORK_CHANNEL)
  31. #define EAMAIN_HAS_NETWORK_CHANNEL 0
  32. #endif
  33. #if !defined(EAMAIN_FREOPEN_SUPPORTED)
  34. #define EAMAIN_FREOPEN_SUPPORTED 0
  35. #endif
  36. #if EAMAIN_HAS_NETWORK_CHANNEL
  37. #if defined(_MSC_VER)
  38. EA_DISABLE_ALL_VC_WARNINGS()
  39. #if defined(WINAPI_FAMILY)
  40. #undef WINAPI_FAMILY
  41. #endif
  42. #define WINAPI_FAMILY WINAPI_FAMILY_DESKTOP_APP
  43. #include <WinSock2.h>
  44. #include <ws2tcpip.h>
  45. #pragma comment(lib, "Ws2_32.lib")
  46. #if defined(EA_PLATFORM_WINDOWS_PHONE)
  47. #pragma warning(disable:4265)
  48. #include <thread>
  49. #else
  50. // Restoring VC warnings on Windows Phone causes some warnings to pop
  51. // up down below, emanating from std::thread. In order to have some
  52. // coverage of this code, warnings are re-enabled on other platforms.
  53. EA_RESTORE_ALL_VC_WARNINGS()
  54. #endif
  55. #define SocketGetLastError() WSAGetLastError()
  56. #define snprintf _snprintf
  57. #else
  58. #include <sys/types.h>
  59. #include <sys/socket.h>
  60. #include <errno.h>
  61. #if !defined(EA_PLATFORM_SONY)
  62. #include <netdb.h>
  63. #else
  64. #include <net.h>
  65. #endif
  66. #include <string.h>
  67. #include <unistd.h>
  68. typedef int SOCKET;
  69. const int INVALID_SOCKET = -1;
  70. const int SOCKET_ERROR = -1;
  71. #define SD_SEND SHUT_WR
  72. #define closesocket close
  73. #define WSAECONNREFUSED ECONNREFUSED
  74. #define WSAENETUNREACH ENETUNREACH
  75. #define SocketGetLastError() errno
  76. #endif
  77. #include <stdio.h>
  78. #include "eathread/eathread_thread.h"
  79. #include "EAStdC/EAMemory.h"
  80. #include "EAStdC/EAString.h"
  81. #include "EAStdC/EASprintf.h"
  82. namespace EA
  83. {
  84. namespace EAMain
  85. {
  86. namespace Internal
  87. {
  88. class NetworkChannel : public IChannel
  89. {
  90. SOCKET m_socket;
  91. char m_serverAddressStorage[128];
  92. char m_port[6];
  93. const char *m_serverAddress;
  94. static void SleepThread(int milliseconds);
  95. public:
  96. NetworkChannel();
  97. virtual ~NetworkChannel();
  98. virtual void Init();
  99. virtual void Send(const char8_t *data);
  100. virtual void Shutdown();
  101. void SetServerPort(const char *server, const char *port);
  102. bool Connect();
  103. };
  104. static NetworkChannel g_NetworkChannelInstance;
  105. void NetworkChannel::SleepThread(int milliseconds)
  106. {
  107. #if defined(_MSC_VER)
  108. #if defined(EA_PLATFORM_WINDOWS_PHONE)
  109. std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
  110. #else
  111. Sleep(milliseconds);
  112. #endif
  113. #else
  114. usleep(milliseconds * 1000);
  115. #endif
  116. }
  117. NetworkChannel::NetworkChannel()
  118. : m_socket(INVALID_SOCKET)
  119. , m_serverAddress(&m_serverAddressStorage[0])
  120. {
  121. EA::StdC::Memset8(m_serverAddressStorage, 0, sizeof m_serverAddressStorage);
  122. EA::StdC::Memset8(m_port, 0, sizeof m_port);
  123. #if defined(_MSC_VER)
  124. WSAData wsaData = {};
  125. WSAStartup(MAKEWORD(2, 2), &wsaData);
  126. #endif
  127. }
  128. static void LogNetworkError(const char *format, ...)
  129. {
  130. (void)format;
  131. }
  132. NetworkChannel::~NetworkChannel()
  133. {
  134. #if defined(_MSC_VER)
  135. WSACleanup();
  136. #endif
  137. }
  138. void NetworkChannel::Init()
  139. {
  140. }
  141. void NetworkChannel::Send(const char8_t *data)
  142. {
  143. char *buffer = const_cast<char *>(data);
  144. ssize_t bufferLength = static_cast<ssize_t>(strlen(buffer));
  145. ssize_t bytesSent = 0;
  146. while (bytesSent < bufferLength)
  147. {
  148. ssize_t result = send(m_socket, buffer + bytesSent, static_cast<int>(bufferLength - bytesSent), 0);
  149. if (result == SOCKET_ERROR)
  150. {
  151. bool reconnected = false;
  152. LogNetworkError("[NetworkChannel::Send] Reconnecting...");
  153. for (int i = 0; i < 20; ++i)
  154. {
  155. Shutdown();
  156. if (!Connect())
  157. {
  158. LogNetworkError("[NetworkChannel::Send] Send failed: %d", SocketGetLastError());
  159. }
  160. else
  161. {
  162. reconnected = true;
  163. break;
  164. }
  165. SleepThread(500);
  166. }
  167. if (!reconnected)
  168. {
  169. LogNetworkError("[NetworkChannel::Send] Unable to connect, aborting.");
  170. return;
  171. }
  172. }
  173. else
  174. {
  175. bytesSent += result;
  176. }
  177. }
  178. }
  179. void NetworkChannel::Shutdown()
  180. {
  181. // Closing the socket does not wait for pending data to be sent.
  182. // To ensure that all data has been sent, the write end of the
  183. // socket must first be shut down. This sends a FIN packet to
  184. // the receiver after all data has been sent and acknowledged
  185. // by the receiver. This must also be paired with a call to
  186. // recv to ensure that all pending readable data has been
  187. // read.
  188. shutdown(m_socket, SD_SEND);
  189. char buffer[128];
  190. for (;;)
  191. {
  192. ssize_t rv = recv(m_socket, buffer, (int)sizeof buffer, 0);
  193. if (rv <= 0)
  194. {
  195. // We can assume that a graceful shutdown has occurred
  196. // when rv == 0. If rv < 0 an error has occurred, but
  197. // we are not at a point where we can easily recover, so
  198. // this code will just shutdown the socket and exit the
  199. // program if an error happens here.
  200. break;
  201. }
  202. }
  203. closesocket(m_socket);
  204. m_socket = INVALID_SOCKET;
  205. }
  206. void NetworkChannel::SetServerPort(const char *server, const char *port)
  207. {
  208. using namespace EA::StdC;
  209. // If, somehow, the server name string is longer than the storge
  210. // allocated, allocate some space for it on the C-heap. Not ideal,
  211. // may actually fail some tests, but the alternative would be
  212. // no output from EAMain at all.
  213. if (Strlcpy(m_serverAddressStorage, server, sizeof m_serverAddressStorage) > (sizeof m_serverAddressStorage))
  214. {
  215. size_t serverNameLength = Strlen(server);
  216. char *ptr = static_cast<char *>(calloc(serverNameLength + 1, 1));
  217. Strlcpy(ptr, server, serverNameLength + 1);
  218. m_serverAddress = ptr;
  219. }
  220. // Valid port numbers are in the range [1, 65535] so will have
  221. // always 5 digits max.
  222. EA_ASSERT(sizeof m_port == 6);
  223. Strlcpy(m_port, port, sizeof m_port);
  224. }
  225. bool NetworkChannel::Connect()
  226. {
  227. EA_ASSERT(m_serverAddress != NULL);
  228. EA_ASSERT(m_port != NULL);
  229. bool result = false;
  230. SOCKET remoteSocket = INVALID_SOCKET;
  231. const int MAX_RETRIES = 250;
  232. #if !defined(EA_PLATFORM_SONY)
  233. struct addrinfo hints = {};
  234. hints.ai_socktype = SOCK_STREAM;
  235. addrinfo *p = NULL;
  236. if (getaddrinfo(m_serverAddress, m_port, &hints, &p) != 0)
  237. {
  238. LogNetworkError("[NetworkChannel::Connect] Cannot connect to %s:%s", m_serverAddress, m_port);
  239. goto ErrorReturn;
  240. }
  241. for (struct addrinfo *endpoint = p; endpoint != NULL; endpoint = endpoint->ai_next)
  242. #endif
  243. {
  244. #if !defined(EA_PLATFORM_SONY)
  245. remoteSocket = socket(endpoint->ai_family, endpoint->ai_socktype, endpoint->ai_protocol);
  246. #else
  247. remoteSocket = sceNetSocket("EAMain Socket", SCE_NET_AF_INET, SCE_NET_SOCK_STREAM, 0);
  248. #endif
  249. if (remoteSocket == INVALID_SOCKET)
  250. {
  251. LogNetworkError("[NetworkChannel::Connect] Cannot create socket");
  252. goto ErrorReturn;
  253. }
  254. #if defined(_MSC_VER)
  255. if (endpoint->ai_family == AF_INET6)
  256. {
  257. DWORD v6only = 0;
  258. setsockopt(remoteSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof v6only);
  259. }
  260. #endif
  261. for (int i = 0; i < MAX_RETRIES; ++i)
  262. {
  263. #if !defined(EA_PLATFORM_SONY)
  264. int connectResult = connect(remoteSocket, endpoint->ai_addr, (int)endpoint->ai_addrlen);
  265. #else
  266. char *p;
  267. SceNetInPort_t port = strtol(m_port, &p, 10);
  268. SceNetSockaddrIn sin;
  269. EA::StdC::Memset8(&sin, 0, sizeof sin);
  270. sin.sin_len = sizeof(sin);
  271. sin.sin_family = SCE_NET_AF_INET;
  272. if (sceNetInetPton(SCE_NET_AF_INET, m_serverAddress, &sin.sin_addr) <= 0)
  273. {
  274. LogNetworkError("[NetworkChannel::Connect] Cannot connect to %s:%s", m_serverAddress, m_port);
  275. goto ErrorReturn;
  276. }
  277. sin.sin_port = sceNetHtons(port);
  278. sin.sin_vport = sceNetHtons(SCE_NET_ADHOC_PORT);
  279. int connectResult = sceNetConnect(remoteSocket, (SceNetSockaddr *)&sin, sizeof(sin));
  280. #endif
  281. if (connectResult == 0)
  282. {
  283. result = true;
  284. m_socket = remoteSocket;
  285. goto SuccessReturn;
  286. }
  287. switch (SocketGetLastError())
  288. {
  289. case WSAENETUNREACH:
  290. SleepThread(20);
  291. continue;
  292. default:
  293. LogNetworkError("[NetworkChannel::Connect] Cannot connect to socket");
  294. goto ErrorReturn;
  295. }
  296. }
  297. }
  298. ErrorReturn:
  299. if (remoteSocket != INVALID_SOCKET)
  300. {
  301. LogNetworkError("[NetworkChannel::Connect] FAILED");
  302. closesocket(remoteSocket);
  303. }
  304. SuccessReturn:
  305. #if !defined(EA_PLATFORM_SONY)
  306. if (p)
  307. {
  308. freeaddrinfo(p);
  309. }
  310. #endif
  311. return result;
  312. }
  313. static IChannel *CreateNetworkChannelImpl(const char *server, int port)
  314. {
  315. char portString[6];
  316. if (port > 65536 || port < 0)
  317. {
  318. return NULL;
  319. }
  320. snprintf(portString, sizeof portString, "%d", port);
  321. portString[5] = 0;
  322. g_NetworkChannelInstance.SetServerPort(server, portString);
  323. if (g_NetworkChannelInstance.Connect())
  324. {
  325. return &g_NetworkChannelInstance;
  326. }
  327. return NULL;
  328. }
  329. #if !EAMAIN_FREOPEN_SUPPORTED
  330. IChannel *CreateNetworkChannel(const char *server, int port)
  331. {
  332. // On platforms where we do not support freopen on standard
  333. // IO streams, we create a raw network channel.
  334. return CreateNetworkChannelImpl(server, port);
  335. }
  336. #else
  337. static EA::Thread::Thread g_PrintThread;
  338. static volatile bool g_PrintThreadDone;
  339. static FILE *g_RedirectedStdoutHandle;
  340. static FILE *g_RedirectedStderrHandle;
  341. class StdoutWrapperChannel : public IChannel
  342. {
  343. NetworkChannel &m_channel;
  344. bool m_shutdown;
  345. StdoutWrapperChannel& operator=(const StdoutWrapperChannel&);
  346. StdoutWrapperChannel(const StdoutWrapperChannel&);
  347. public:
  348. StdoutWrapperChannel(NetworkChannel &channel)
  349. : m_channel(channel)
  350. , m_shutdown(false)
  351. {
  352. }
  353. virtual ~StdoutWrapperChannel();
  354. virtual void Init()
  355. {
  356. }
  357. virtual void Send(const char8_t *data)
  358. {
  359. fputs(data, stdout);
  360. fflush(stdout);
  361. }
  362. virtual void Shutdown()
  363. {
  364. if (g_RedirectedStdoutHandle)
  365. {
  366. fclose(g_RedirectedStdoutHandle);
  367. }
  368. if (g_RedirectedStderrHandle)
  369. {
  370. fclose(g_RedirectedStderrHandle);
  371. }
  372. g_PrintThreadDone = true;
  373. if (g_PrintThread.GetStatus() == EA::Thread::Thread::kStatusRunning)
  374. {
  375. g_PrintThread.WaitForEnd();
  376. }
  377. m_channel.Shutdown();
  378. m_shutdown = true;
  379. }
  380. };
  381. StdoutWrapperChannel::~StdoutWrapperChannel()
  382. {
  383. if (!m_shutdown)
  384. {
  385. Shutdown();
  386. }
  387. }
  388. static bool ReadFromFile(FILE *file)
  389. {
  390. static const int BUFFER_SIZE = 1024;
  391. static char buffer[BUFFER_SIZE + 1];
  392. bool haveRead = false;
  393. // This is a tortuous way of checking to see if there is data
  394. // available but the goal here is to prevent this thread from
  395. // blocking if nothing is ready. Blocking could be desirable
  396. // if we were only reading from one stream but because this
  397. // code attempts to read from both stdout and stderr we do not
  398. // want it to block on reading one while the other is receiving
  399. // important information.
  400. // Another possibility would be to use non-blocking IO but this
  401. // would require different implementations for different
  402. // platforms.
  403. long currentPosition = ftell(file);
  404. fseek(file, 0, SEEK_END);
  405. long endPosition = ftell(file);
  406. if (endPosition > currentPosition)
  407. {
  408. size_t bytesAvailable = static_cast<size_t>(endPosition - currentPosition);
  409. fseek(file, currentPosition, SEEK_SET);
  410. while (bytesAvailable > 0)
  411. {
  412. size_t bytesToRead = (bytesAvailable > BUFFER_SIZE) ? BUFFER_SIZE : bytesAvailable;
  413. size_t bytesRead = fread(buffer, 1, bytesToRead, file);
  414. buffer[bytesRead] = 0;
  415. g_NetworkChannelInstance.Send(buffer);
  416. bytesAvailable -= bytesRead;
  417. }
  418. haveRead = true;
  419. }
  420. return haveRead;
  421. }
  422. static char g_StdoutLogPath[256];
  423. static char g_StderrLogPath[256];
  424. static intptr_t PrintFunction(void *)
  425. {
  426. FILE *stdoutLog = fopen(g_StdoutLogPath, "rb");
  427. FILE *stderrLog = fopen(g_StderrLogPath, "rb");
  428. while (!g_PrintThreadDone)
  429. {
  430. // It might look neater to combine these file reads into
  431. // the if statement but this was not done because the
  432. // shortcircuting of the left hand condition prevented
  433. // the right hand condition from being evaluated, but
  434. // every iteration should read from both sources.
  435. bool haveReadStdout = ReadFromFile(stdoutLog);
  436. bool haveReadStderr = ReadFromFile(stderrLog);
  437. if (!haveReadStdout && !haveReadStderr)
  438. {
  439. EA::Thread::ThreadSleep(50);
  440. }
  441. }
  442. fflush(stdout); ReadFromFile(stdoutLog); fclose(stdoutLog);
  443. fflush(stderr); ReadFromFile(stderrLog); fclose(stderrLog);
  444. return 0;
  445. }
  446. IChannel *CreateNetworkChannel(const char *server, int port)
  447. {
  448. #if defined(EA_PLATFORM_CAPILANO)
  449. if (IsDebuggerPresent())
  450. {
  451. return NULL;
  452. }
  453. #endif
  454. EA::Thread::ThreadParameters threadParameters;
  455. char *STDOUT_LOG_NAME = "stdout.log";
  456. const char *STDERR_LOG_NAME = "stderr.log";
  457. threadParameters.mnPriority = EA::Thread::kThreadPriorityMax;
  458. #if defined EA_PLATFORM_IPHONE
  459. char temporaryDirectory[256];
  460. confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof temporaryDirectory);
  461. EA::StdC::Snprintf(g_StdoutLogPath, sizeof g_StdoutLogPath, "%s/%s", temporaryDirectory, STDOUT_LOG_NAME);
  462. EA::StdC::Snprintf(g_StderrLogPath, sizeof g_StderrLogPath, "%s/%s", temporaryDirectory, STDERR_LOG_NAME);
  463. #elif defined EA_PLATFORM_CAPILANO
  464. EA::StdC::Snprintf(g_StdoutLogPath, sizeof g_StdoutLogPath, "T:\\%s", STDOUT_LOG_NAME);
  465. EA::StdC::Snprintf(g_StderrLogPath, sizeof g_StderrLogPath, "T:\\%s", STDERR_LOG_NAME);
  466. #else
  467. EA::StdC::Strlcpy(g_StdoutLogPath, STDOUT_LOG_NAME, sizeof g_StdoutLogPath);
  468. EA::StdC::Strlcpy(g_StderrLogPath, STDERR_LOG_NAME, sizeof g_StderrLogPath);
  469. #endif
  470. fprintf(stdout, ""); fflush(stdout);
  471. fprintf(stderr, ""); fflush(stderr);
  472. g_RedirectedStdoutHandle = freopen(g_StdoutLogPath, "wb", stdout);
  473. setvbuf(stdout, NULL, _IONBF, 0);
  474. g_RedirectedStderrHandle = freopen(g_StderrLogPath, "wb", stderr);
  475. setvbuf(stderr, NULL, _IONBF, 0);
  476. if (CreateNetworkChannelImpl(server, port) != NULL)
  477. {
  478. g_PrintThread.Begin(PrintFunction, NULL, &threadParameters);
  479. }
  480. return new StdoutWrapperChannel(g_NetworkChannelInstance);
  481. }
  482. #endif
  483. }
  484. }
  485. }
  486. #else
  487. namespace EA
  488. {
  489. namespace EAMain
  490. {
  491. namespace Internal
  492. {
  493. IChannel *CreateNetworkChannel(const char *server, int port)
  494. {
  495. return NULL;
  496. }
  497. }
  498. }
  499. }
  500. #endif