AutopatcherServerTest.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. // Common includes
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include "Kbhit.h"
  14. #include "GetTime.h"
  15. #include "RakPeerInterface.h"
  16. #include "MessageIdentifiers.h"
  17. #include "BitStream.h"
  18. #include "StringCompressor.h"
  19. #include "FileListTransfer.h"
  20. #include "FileList.h" // FLP_Printf
  21. #include "PacketizedTCP.h"
  22. #include "Gets.h"
  23. // Server only includes
  24. #include "AutopatcherServer.h"
  25. // Replace this repository with your own implementation if you don't want to use PostgreSQL
  26. #include "AutopatcherPostgreRepository.h"
  27. #ifdef _WIN32
  28. #include "WindowsIncludes.h" // Sleep
  29. #else
  30. #include <unistd.h> // usleep
  31. #endif
  32. #define USE_TCP
  33. #define LISTEN_PORT 60000
  34. #define MAX_INCOMING_CONNECTIONS 128
  35. char WORKING_DIRECTORY[MAX_PATH];
  36. char PATH_TO_XDELTA_EXE[MAX_PATH];
  37. // The default AutopatcherPostgreRepository2 uses bsdiff which takes too much memory for large files.
  38. // I override MakePatch to use XDelta in this case
  39. class AutopatcherPostgreRepository2_WithXDelta : public RakNet::AutopatcherPostgreRepository2
  40. {
  41. bool MakePatch(const char *oldFile, const char *newFile, char **patch, unsigned int *patchLength, int *patchAlgorithm)
  42. {
  43. FILE *fpOld = fopen(oldFile, "rb");
  44. fseek(fpOld, 0, SEEK_END);
  45. int contentLengthOld = ftell(fpOld);
  46. FILE *fpNew = fopen(newFile, "rb");
  47. fseek(fpNew, 0, SEEK_END);
  48. int contentLengthNew = ftell(fpNew);
  49. if ((contentLengthOld < 33554432 && contentLengthNew < 33554432) || PATH_TO_XDELTA_EXE[0]==0)
  50. {
  51. // Use bsdiff, which does a good job but takes a lot of memory based on the size of the file
  52. *patchAlgorithm=0;
  53. bool b = MakePatchBSDiff(fpOld, contentLengthOld, fpNew, contentLengthNew, patch, patchLength);
  54. fclose(fpOld);
  55. fclose(fpNew);
  56. return b;
  57. }
  58. else
  59. {
  60. *patchAlgorithm=1;
  61. fclose(fpOld);
  62. fclose(fpNew);
  63. char buff[128];
  64. RakNet::TimeUS time = RakNet::GetTimeUS();
  65. #if defined(_WIN32)
  66. sprintf(buff, "%I64u", time);
  67. #else
  68. sprintf(buff, "%llu", (long long unsigned int) time);
  69. #endif
  70. // Invoke xdelta
  71. // See https://code.google.com/p/xdelta/wiki/CommandLineSyntax
  72. char commandLine[512];
  73. _snprintf(commandLine, sizeof(commandLine)-1, "-f -s %s %s patchServer_%s.tmp", oldFile, newFile, buff);
  74. commandLine[511]=0;
  75. SHELLEXECUTEINFO shellExecuteInfo;
  76. shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO);
  77. shellExecuteInfo.fMask = SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE;
  78. shellExecuteInfo.hwnd = NULL;
  79. shellExecuteInfo.lpVerb = "open";
  80. shellExecuteInfo.lpFile = PATH_TO_XDELTA_EXE;
  81. shellExecuteInfo.lpParameters = commandLine;
  82. shellExecuteInfo.lpDirectory = WORKING_DIRECTORY;
  83. shellExecuteInfo.nShow = SW_SHOWNORMAL;
  84. shellExecuteInfo.hInstApp = NULL;
  85. ShellExecuteEx(&shellExecuteInfo);
  86. //ShellExecute(NULL, "open", PATH_TO_XDELTA_EXE, commandLine, WORKING_DIRECTORY, SW_SHOWNORMAL);
  87. char pathToPatch[MAX_PATH];
  88. sprintf(pathToPatch, "%s/patchServer_%s.tmp", WORKING_DIRECTORY, buff);
  89. // r+ instead of r, because I want exclusive access in case xdelta is still working
  90. FILE *fpPatch = fopen(pathToPatch, "r+b");
  91. RakNet::TimeUS stopWaiting = time + 60000000 * 5;
  92. while (fpPatch==0 && RakNet::GetTimeUS() < stopWaiting)
  93. {
  94. RakSleep(1000);
  95. fpPatch = fopen(pathToPatch, "r+b");
  96. }
  97. if (fpPatch==0)
  98. return false;
  99. fseek(fpPatch, 0, SEEK_END);
  100. *patchLength = ftell(fpPatch);
  101. fseek(fpPatch, 0, SEEK_SET);
  102. *patch = (char*) rakMalloc_Ex(*patchLength, _FILE_AND_LINE_);
  103. fread(*patch, 1, *patchLength, fpPatch);
  104. fclose(fpPatch);
  105. int unlinkRes = _unlink(pathToPatch);
  106. while (unlinkRes!=0 && RakNet::GetTimeUS() < stopWaiting)
  107. {
  108. RakSleep(1000);
  109. unlinkRes = _unlink(pathToPatch);
  110. }
  111. if (unlinkRes!=0)
  112. printf("\nWARNING: unlink %s failed.\nerr=%i (%s)\n", pathToPatch, errno, strerror(errno));
  113. return true;
  114. }
  115. }
  116. };
  117. int main(int argc, char **argv)
  118. {
  119. printf("Server starting... ");
  120. RakNet::AutopatcherServer autopatcherServer;
  121. // RakNet::FLP_Printf progressIndicator;
  122. RakNet::FileListTransfer fileListTransfer;
  123. static const int workerThreadCount=4; // Used for checking patches only
  124. static const int sqlConnectionObjectCount=32; // Used for both checking patches and downloading
  125. AutopatcherPostgreRepository2_WithXDelta connectionObject[sqlConnectionObjectCount];
  126. RakNet::AutopatcherRepositoryInterface *connectionObjectAddresses[sqlConnectionObjectCount];
  127. for (int i=0; i < sqlConnectionObjectCount; i++)
  128. connectionObjectAddresses[i]=&connectionObject[i];
  129. // fileListTransfer.AddCallback(&progressIndicator);
  130. autopatcherServer.SetFileListTransferPlugin(&fileListTransfer);
  131. // PostgreSQL is fast, so this may not be necessary, or could use fewer threads
  132. // This is used to read increments of large files concurrently, thereby serving users downloads as other users read from the DB
  133. fileListTransfer.StartIncrementalReadThreads(sqlConnectionObjectCount);
  134. autopatcherServer.SetMaxConurrentUsers(MAX_INCOMING_CONNECTIONS); // More users than this get queued up
  135. RakNet::AutopatcherServerLoadNotifier_Printf loadNotifier;
  136. autopatcherServer.SetLoadManagementCallback(&loadNotifier);
  137. #ifdef USE_TCP
  138. RakNet::PacketizedTCP packetizedTCP;
  139. if (packetizedTCP.Start(LISTEN_PORT,MAX_INCOMING_CONNECTIONS)==false)
  140. {
  141. printf("Failed to start TCP. Is the port already in use?");
  142. return 1;
  143. }
  144. packetizedTCP.AttachPlugin(&autopatcherServer);
  145. packetizedTCP.AttachPlugin(&fileListTransfer);
  146. #else
  147. RakNet::RakPeerInterface *rakPeer;
  148. rakPeer = RakNet::RakPeerInterface::GetInstance();
  149. RakNet::SocketDescriptor socketDescriptor(LISTEN_PORT,0);
  150. rakPeer->Startup(MAX_INCOMING_CONNECTIONS,&socketDescriptor, 1);
  151. rakPeer->SetMaximumIncomingConnections(MAX_INCOMING_CONNECTIONS);
  152. rakPeer->AttachPlugin(&autopatcherServer);
  153. rakPeer->AttachPlugin(&fileListTransfer);
  154. #endif
  155. printf("started.\n");
  156. printf("Enter database password:\n");
  157. char connectionString[256],password[128];
  158. char username[256];
  159. strcpy(username, "postgres");
  160. gets(password);
  161. if (password[0]==0) strcpy(password, "aaaa");
  162. strcpy(connectionString, "user=");
  163. strcat(connectionString, username);
  164. strcat(connectionString, " password=");
  165. strcat(connectionString, password);
  166. for (int conIdx=0; conIdx < sqlConnectionObjectCount; conIdx++)
  167. {
  168. if (connectionObject[conIdx].Connect(connectionString)==false)
  169. {
  170. printf("Database connection failed.\n");
  171. return 1;
  172. }
  173. }
  174. printf("Database connection suceeded.\n");
  175. printf("Starting threads\n");
  176. // 4 Worker threads, which is CPU intensive
  177. // A greater number of SQL connections, which read files incrementally for large downloads
  178. autopatcherServer.StartThreads(workerThreadCount,sqlConnectionObjectCount, connectionObjectAddresses);
  179. autopatcherServer.CacheMostRecentPatch(0);
  180. // autopatcherServer.SetAllowDownloadOfOriginalUnmodifiedFiles(false);
  181. printf("System ready for connections\n");
  182. // https://code.google.com/p/xdelta/downloads/list
  183. printf("Optional: Enter path to xdelta.exe: ");
  184. Gets(PATH_TO_XDELTA_EXE, sizeof(PATH_TO_XDELTA_EXE));
  185. if (PATH_TO_XDELTA_EXE[0]==0)
  186. strcpy(PATH_TO_XDELTA_EXE, "c:/xdelta3-3.0.6-win32.exe");
  187. if (PATH_TO_XDELTA_EXE[0])
  188. {
  189. printf("Enter working directory to store temporary files: ");
  190. Gets(WORKING_DIRECTORY, sizeof(WORKING_DIRECTORY));
  191. if (WORKING_DIRECTORY[0]==0)
  192. GetTempPath(MAX_PATH, WORKING_DIRECTORY);
  193. if (WORKING_DIRECTORY[strlen(WORKING_DIRECTORY)-1]=='\\' || WORKING_DIRECTORY[strlen(WORKING_DIRECTORY)-1]=='/')
  194. WORKING_DIRECTORY[strlen(WORKING_DIRECTORY)-1]=0;
  195. }
  196. printf("(D)rop database\n(C)reate database.\n(A)dd application\n(U)pdate revision.\n(R)emove application\n(Q)uit\n");
  197. char ch;
  198. RakNet::Packet *p;
  199. while (1)
  200. {
  201. #ifdef USE_TCP
  202. RakNet::SystemAddress notificationAddress;
  203. notificationAddress=packetizedTCP.HasCompletedConnectionAttempt();
  204. if (notificationAddress!=RakNet::UNASSIGNED_SYSTEM_ADDRESS)
  205. printf("ID_CONNECTION_REQUEST_ACCEPTED\n");
  206. notificationAddress=packetizedTCP.HasNewIncomingConnection();
  207. if (notificationAddress!=RakNet::UNASSIGNED_SYSTEM_ADDRESS)
  208. printf("ID_NEW_INCOMING_CONNECTION\n");
  209. notificationAddress=packetizedTCP.HasLostConnection();
  210. if (notificationAddress!=RakNet::UNASSIGNED_SYSTEM_ADDRESS)
  211. printf("ID_CONNECTION_LOST\n");
  212. p=packetizedTCP.Receive();
  213. while (p)
  214. {
  215. packetizedTCP.DeallocatePacket(p);
  216. p=packetizedTCP.Receive();
  217. }
  218. #else
  219. p=rakPeer->Receive();
  220. while (p)
  221. {
  222. if (p->data[0]==ID_NEW_INCOMING_CONNECTION)
  223. printf("ID_NEW_INCOMING_CONNECTION\n");
  224. else if (p->data[0]==ID_DISCONNECTION_NOTIFICATION)
  225. printf("ID_DISCONNECTION_NOTIFICATION\n");
  226. else if (p->data[0]==ID_CONNECTION_LOST)
  227. printf("ID_CONNECTION_LOST\n");
  228. rakPeer->DeallocatePacket(p);
  229. p=rakPeer->Receive();
  230. }
  231. #endif
  232. if (kbhit())
  233. {
  234. ch=getch();
  235. if (ch=='q')
  236. break;
  237. else if (ch=='c')
  238. {
  239. if (connectionObject[0].CreateAutopatcherTables()==false)
  240. printf("%s", connectionObject[0].GetLastError());
  241. }
  242. else if (ch=='d')
  243. {
  244. if (connectionObject[0].DestroyAutopatcherTables()==false)
  245. printf("%s", connectionObject[0].GetLastError());
  246. }
  247. else if (ch=='a')
  248. {
  249. printf("Enter application name to add: ");
  250. char appName[512];
  251. Gets(appName,sizeof(appName));
  252. if (appName[0]==0)
  253. strcpy(appName, "TestApp");
  254. if (connectionObject[0].AddApplication(appName, username)==false)
  255. printf("%s", connectionObject[0].GetLastError());
  256. else
  257. printf("Done\n");
  258. }
  259. else if (ch=='r')
  260. {
  261. printf("Enter application name to remove: ");
  262. char appName[512];
  263. Gets(appName,sizeof(appName));
  264. if (appName[0]==0)
  265. strcpy(appName, "TestApp");
  266. if (connectionObject[0].RemoveApplication(appName)==false)
  267. printf("%s", connectionObject[0].GetLastError());
  268. else
  269. printf("Done\n");
  270. }
  271. else if (ch=='u')
  272. {
  273. printf("Enter application name: ");
  274. char appName[512];
  275. Gets(appName,sizeof(appName));
  276. if (appName[0]==0)
  277. strcpy(appName, "TestApp");
  278. printf("Enter application directory: ");
  279. char appDir[512];
  280. Gets(appDir,sizeof(appDir));
  281. if (appDir[0]==0)
  282. strcpy(appDir, "D:\\temp");
  283. if (connectionObject[0].UpdateApplicationFiles(appName, appDir, username, 0)==false)
  284. {
  285. printf("%s", connectionObject[0].GetLastError());
  286. }
  287. else
  288. {
  289. printf("Update success.\n");
  290. autopatcherServer.CacheMostRecentPatch(appName);
  291. }
  292. }
  293. }
  294. RakSleep(30);
  295. }
  296. #ifdef USE_TCP
  297. packetizedTCP.Stop();
  298. #else
  299. RakNet::RakPeerInterface::DestroyInstance(rakPeer);
  300. #endif
  301. return 0;
  302. }
粤ICP备19079148号