1 // 2 // protocol.hpp 3 // ~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef PORTHOPPER_PROTOCOL_HPP 12 #define PORTHOPPER_PROTOCOL_HPP 13 14 #include <boost/array.hpp> 15 #include <boost/asio.hpp> 16 #include <cstring> 17 #include <iomanip> 18 #include <string> 19 #include <strstream> 20 21 // This request is sent by the client to the server over a TCP connection. 22 // The client uses it to perform three functions: 23 // - To request that data start being sent to a given port. 24 // - To request that data is no longer sent to a given port. 25 // - To change the target port to another. 26 class control_request 27 { 28 public: 29 // Construct an empty request. Used when receiving. control_request()30 control_request() 31 { 32 } 33 34 // Create a request to start sending data to a given port. start(unsigned short port)35 static const control_request start(unsigned short port) 36 { 37 return control_request(0, port); 38 } 39 40 // Create a request to stop sending data to a given port. stop(unsigned short port)41 static const control_request stop(unsigned short port) 42 { 43 return control_request(port, 0); 44 } 45 46 // Create a request to change the port that data is sent to. change(unsigned short old_port,unsigned short new_port)47 static const control_request change( 48 unsigned short old_port, unsigned short new_port) 49 { 50 return control_request(old_port, new_port); 51 } 52 53 // Get the old port. Returns 0 for start requests. old_port() const54 unsigned short old_port() const 55 { 56 std::istrstream is(data_, encoded_port_size); 57 unsigned short port = 0; 58 is >> std::setw(encoded_port_size) >> std::hex >> port; 59 return port; 60 } 61 62 // Get the new port. Returns 0 for stop requests. new_port() const63 unsigned short new_port() const 64 { 65 std::istrstream is(data_ + encoded_port_size, encoded_port_size); 66 unsigned short port = 0; 67 is >> std::setw(encoded_port_size) >> std::hex >> port; 68 return port; 69 } 70 71 // Obtain buffers for reading from or writing to a socket. to_buffers()72 boost::array<boost::asio::mutable_buffer, 1> to_buffers() 73 { 74 boost::array<boost::asio::mutable_buffer, 1> buffers 75 = { { boost::asio::buffer(data_) } }; 76 return buffers; 77 } 78 79 private: 80 // Construct with specified old and new ports. control_request(unsigned short old_port_number,unsigned short new_port_number)81 control_request(unsigned short old_port_number, 82 unsigned short new_port_number) 83 { 84 std::ostrstream os(data_, control_request_size); 85 os << std::setw(encoded_port_size) << std::hex << old_port_number; 86 os << std::setw(encoded_port_size) << std::hex << new_port_number; 87 } 88 89 // The length in bytes of a control_request and its components. 90 enum 91 { 92 encoded_port_size = 4, // 16-bit port in hex. 93 control_request_size = encoded_port_size * 2 94 }; 95 96 // The encoded request data. 97 char data_[control_request_size]; 98 }; 99 100 // This frame is sent from the server to subscribed clients over UDP. 101 class frame 102 { 103 public: 104 // The maximum allowable length of the payload. 105 enum { payload_size = 32 }; 106 107 // Construct an empty frame. Used when receiving. frame()108 frame() 109 { 110 } 111 112 // Construct a frame with specified frame number and payload. frame(unsigned long frame_number,const std::string & payload_data)113 frame(unsigned long frame_number, const std::string& payload_data) 114 { 115 std::ostrstream os(data_, frame_size); 116 os << std::setw(encoded_number_size) << std::hex << frame_number; 117 os << std::setw(payload_size) 118 << std::setfill(' ') << payload_data.substr(0, payload_size); 119 } 120 121 // Get the frame number. number() const122 unsigned long number() const 123 { 124 std::istrstream is(data_, encoded_number_size); 125 unsigned long frame_number = 0; 126 is >> std::setw(encoded_number_size) >> std::hex >> frame_number; 127 return frame_number; 128 } 129 130 // Get the payload data. payload() const131 const std::string payload() const 132 { 133 return std::string(data_ + encoded_number_size, payload_size); 134 } 135 136 // Obtain buffers for reading from or writing to a socket. to_buffers()137 boost::array<boost::asio::mutable_buffer, 1> to_buffers() 138 { 139 boost::array<boost::asio::mutable_buffer, 1> buffers 140 = { { boost::asio::buffer(data_) } }; 141 return buffers; 142 } 143 144 private: 145 // The length in bytes of a frame and its components. 146 enum 147 { 148 encoded_number_size = 8, // Frame number in hex. 149 frame_size = encoded_number_size + payload_size 150 }; 151 152 // The encoded frame data. 153 char data_[frame_size]; 154 }; 155 156 #endif // PORTHOPPER_PROTOCOL_HPP 157