1 /* Copyright (C) 2016 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef NETFILETRANSFER_H 19 #define NETFILETRANSFER_H 20 21 #include <map> 22 23 class CNetMessage; 24 class CNetClientSession; 25 class CNetServerSession; 26 class INetSession; 27 28 // Assume this is sufficiently less than MTU that packets won't get 29 // fragmented or dropped. 30 static const size_t DEFAULT_FILE_TRANSFER_PACKET_SIZE = 1024; 31 32 // To improve performance without flooding ENet's internal buffers, 33 // maintain a small number of in-flight packets. 34 // Pick numbers so that with e.g. 200ms round-trip latency 35 // we can hopefully get windowSize*packetSize*1000/200 = 160KB/s bandwidth 36 static const size_t DEFAULT_FILE_TRANSFER_WINDOW_SIZE = 32; 37 38 // Some arbitrary limit to make it slightly harder to use up all of someone's RAM 39 static const size_t MAX_FILE_TRANSFER_SIZE = 8*MiB; 40 41 /** 42 * Asynchronous file-receiving task. 43 * Other code should subclass this, implement OnComplete(), 44 * then pass it to CNetFileTransferer::StartTask. 45 */ 46 class CNetFileReceiveTask 47 { 48 public: CNetFileReceiveTask()49 CNetFileReceiveTask() : m_RequestID(0), m_Length(0) { } ~CNetFileReceiveTask()50 virtual ~CNetFileReceiveTask() {} 51 52 /** 53 * Called when m_Buffer contains the full received data. 54 */ 55 virtual void OnComplete() = 0; 56 57 // TODO: Ought to have an OnFailure, e.g. when the session drops or there's another error 58 59 /** 60 * Uniquely identifies the request within the scope of its CNetFileTransferer. 61 * Set automatically by StartTask. 62 */ 63 u32 m_RequestID; 64 65 size_t m_Length; 66 67 std::string m_Buffer; 68 }; 69 70 /** 71 * Handles transferring files between clients and servers. 72 */ 73 class CNetFileTransferer 74 { 75 public: CNetFileTransferer(INetSession * session)76 CNetFileTransferer(INetSession* session) 77 : m_Session(session), m_NextRequestID(1), m_LastProgressReportTime(0) 78 { 79 } 80 81 /** 82 * Should be called when a message is received from the network. 83 * Returns INFO::SKIPPED if the message is not one that this class handles. 84 * Returns INFO::OK if the message is handled successfully, 85 * or ERR::FAIL if handled unsuccessfully. 86 */ 87 Status HandleMessageReceive(const CNetMessage* message); 88 89 /** 90 * Registers a file-receiving task. 91 */ 92 void StartTask(const shared_ptr<CNetFileReceiveTask>& task); 93 94 /** 95 * Registers data to be sent in response to a request. 96 * (Callers are expected to have their own mechanism for receiving 97 * requests and deciding what to respond with.) 98 */ 99 void StartResponse(u32 requestID, const std::string& data); 100 101 /** 102 * Call frequently (e.g. once per frame) to trigger any necessary 103 * packet processing. 104 */ 105 void Poll(); 106 107 private: 108 /** 109 * Asynchronous file-sending task. 110 */ 111 struct CNetFileSendTask 112 { 113 u32 requestID; 114 std::string buffer; 115 size_t offset; 116 size_t maxWindowSize; 117 size_t packetsInFlight; 118 }; 119 120 INetSession* m_Session; 121 122 u32 m_NextRequestID; 123 124 typedef std::map<u32, shared_ptr<CNetFileReceiveTask>> FileReceiveTasksMap; 125 FileReceiveTasksMap m_FileReceiveTasks; 126 127 typedef std::map<u32, CNetFileSendTask> FileSendTasksMap; 128 FileSendTasksMap m_FileSendTasks; 129 130 double m_LastProgressReportTime; 131 }; 132 133 #endif // NETFILETRANSFER_H 134