1/* 2 * Copyright (c) 2011-2015, Intel Corporation 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation and/or 13 * other materials provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors 16 * may be used to endorse or promote products derived from this software without 17 * specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30#include "Message.h" 31#include "Socket.h" 32#include "Iterator.hpp" 33#include <asio.hpp> 34#include <vector> 35#include <numeric> 36#include <cassert> 37 38using std::string; 39 40CMessage::CMessage(MsgType ucMsgId) : _ucMsgId(ucMsgId), _uiIndex(0) 41{ 42} 43 44CMessage::CMessage() : _ucMsgId(MsgType::EInvalid), _uiIndex(0) 45{ 46} 47 48// Msg Id 49CMessage::MsgType CMessage::getMsgId() const 50{ 51 return _ucMsgId; 52} 53 54bool CMessage::isValidAccess(size_t offset, size_t size) const 55{ 56 return offset + size <= getMessageDataSize(); 57} 58 59// Data 60void CMessage::writeData(const void *pvData, size_t size) 61{ 62 assert(isValidAccess(_uiIndex, size)); 63 64 auto first = MAKE_ARRAY_ITERATOR(static_cast<const uint8_t *>(pvData), size); 65 auto last = first + size; 66 auto destFirst = begin(mData) + _uiIndex; 67 68 std::copy(first, last, destFirst); 69 70 _uiIndex += size; 71} 72 73void CMessage::readData(void *pvData, size_t size) 74{ 75 assert(isValidAccess(_uiIndex, size)); 76 77 auto first = begin(mData) + _uiIndex; 78 auto last = first + size; 79 auto destFirst = MAKE_ARRAY_ITERATOR(static_cast<uint8_t *>(pvData), size); 80 81 std::copy(first, last, destFirst); 82 83 _uiIndex += size; 84} 85 86void CMessage::writeString(const string &strData) 87{ 88 // Size 89 uint32_t size = static_cast<uint32_t>(strData.length()); 90 91 writeData(&size, sizeof(size)); 92 93 // Content 94 writeData(strData.c_str(), size); 95} 96 97void CMessage::readString(string &strData) 98{ 99 // Size 100 uint32_t uiSize; 101 102 readData(&uiSize, sizeof(uiSize)); 103 104 // Data 105 std::vector<char> string(uiSize + 1); 106 107 // Content 108 readData(string.data(), uiSize); 109 110 // NULL-terminate string 111 string.back() = '\0'; 112 113 // Output 114 strData = string.data(); 115} 116 117size_t CMessage::getStringSize(const string &strData) const 118{ 119 // Return string length plus room to store its length 120 return strData.length() + sizeof(uint32_t); 121} 122 123// Remaining data size 124size_t CMessage::getRemainingDataSize() const 125{ 126 return getMessageDataSize() - _uiIndex; 127} 128 129// Send/Receive 130CMessage::Result CMessage::serialize(Socket &&socket, bool bOut, string &strError) 131{ 132 asio::ip::tcp::socket &asioSocket = socket.get(); 133 134 if (bOut) { 135 asio::error_code ec; 136 137 // Make room for data to send 138 allocateData(getDataSize()); 139 140 // Get data from derived 141 fillDataToSend(); 142 143 // Finished providing data? 144 assert(_uiIndex == getMessageDataSize()); 145 146 // First send sync word 147 uint16_t uiSyncWord = SYNC_WORD; 148 149 if (!asio::write(asioSocket, asio::buffer(&uiSyncWord, sizeof(uiSyncWord)), ec)) { 150 151 if (ec == asio::error::eof) { 152 return peerDisconnected; 153 } 154 return error; 155 } 156 157 // Size 158 uint32_t uiSize = (uint32_t)(sizeof(_ucMsgId) + getMessageDataSize()); 159 160 if (!asio::write(asioSocket, asio::buffer(&uiSize, sizeof(uiSize)), ec)) { 161 162 strError += string("Size write failed: ") + ec.message(); 163 return error; 164 } 165 166 // Msg Id 167 if (!asio::write(asioSocket, asio::buffer(&_ucMsgId, sizeof(_ucMsgId)), ec)) { 168 169 strError += string("Msg write failed: ") + ec.message(); 170 return error; 171 } 172 173 // Data 174 if (!asio::write(asioSocket, asio::buffer(mData), ec)) { 175 176 strError = string("Data write failed: ") + ec.message(); 177 return error; 178 } 179 180 // Checksum 181 uint8_t ucChecksum = computeChecksum(); 182 183 if (!asio::write(asioSocket, asio::buffer(&ucChecksum, sizeof(ucChecksum)), ec)) { 184 185 strError = string("Checksum write failed: ") + ec.message(); 186 return error; 187 } 188 189 } else { 190 // First read sync word 191 uint16_t uiSyncWord = 0; 192 asio::error_code ec; 193 194 if (!asio::read(asioSocket, asio::buffer(&uiSyncWord, sizeof(uiSyncWord)), ec)) { 195 strError = string("Sync read failed: ") + ec.message(); 196 if (ec == asio::error::eof) { 197 return peerDisconnected; 198 } 199 return error; 200 } 201 202 // Check Sync word 203 if (uiSyncWord != SYNC_WORD) { 204 205 strError = "Sync word incorrect"; 206 return error; 207 } 208 209 // Size 210 uint32_t uiSize = 0; 211 212 if (!asio::read(asioSocket, asio::buffer(&uiSize, sizeof(uiSize)), ec)) { 213 strError = string("Size read failed: ") + ec.message(); 214 return error; 215 } 216 217 // Msg Id 218 if (!asio::read(asioSocket, asio::buffer(&_ucMsgId, sizeof(_ucMsgId)), ec)) { 219 strError = string("Msg id read failed: ") + ec.message(); 220 return error; 221 } 222 223 // Data 224 225 // Allocate 226 allocateData(uiSize - sizeof(_ucMsgId)); 227 228 // Data receive 229 if (!asio::read(asioSocket, asio::buffer(mData), ec)) { 230 strError = string("Data read failed: ") + ec.message(); 231 return error; 232 } 233 234 // Checksum 235 uint8_t ucChecksum = 0; 236 237 if (!asio::read(asioSocket, asio::buffer(&ucChecksum, sizeof(ucChecksum)), ec)) { 238 strError = string("Checksum read failed: ") + ec.message(); 239 return error; 240 } 241 // Compare 242 if (ucChecksum != computeChecksum()) { 243 244 strError = "Received checksum != computed checksum"; 245 return error; 246 } 247 248 // Collect data in derived 249 collectReceivedData(); 250 } 251 252 return success; 253} 254 255// Checksum 256uint8_t CMessage::computeChecksum() const 257{ 258 return accumulate(begin(mData), end(mData), static_cast<uint8_t>(_ucMsgId)); 259} 260 261// Allocation of room to store the message 262void CMessage::allocateData(size_t size) 263{ 264 // Remove previous one 265 mData.clear(); 266 267 // Do allocate 268 mData.resize(size); 269 270 // Reset Index 271 _uiIndex = 0; 272} 273