From a3eb2ccd0a857f15b52f1fba2df2eb4c69bcf399 Mon Sep 17 00:00:00 2001 From: Fred Nicolson Date: Sat, 10 Dec 2016 12:59:07 +0000 Subject: [PATCH] Added raw send and receive. Fixed TcpSocket close bug. Can now send and receive raw data over TcpSockets for communicating with other protocols, such as HTTP. Fixed TcpSocket::close only calling close() if the connection isn't open (gg wp) --- include/TcpSocket.h | 32 +++++++++++++++++++++++++++- main.cpp | 43 ++++++++++++++++++++----------------- src/TcpSocket.cpp | 52 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/include/TcpSocket.h b/include/TcpSocket.h index 82046eb..e282685 100644 --- a/include/TcpSocket.h +++ b/include/TcpSocket.h @@ -67,15 +67,45 @@ public: return is_connected; } + /*! + * Attempts to send raw data down the socket, without + * any of frnetlib's framing. Useful for communicating through + * different protocols. + * + * @param data The data to send. + * @param size The number of bytes, from data to send. Be careful not to overflow. + * @return The status of the operation. + */ + Status send_raw(const char *data, size_t size); + + + /*! + * Receives raw data from the socket, without any of + * frnetlib's framing. Useful for communicating through + * different protocols. This will attempt to read 'data_size' + * bytes, but might not succeed. It'll return how many bytes were actually + * read in 'received'. + * + * @param data Where to store the received data. + * @param data_size The number of bytes to try and receive. Be sure that it's not larger than data. + * @param received Will be filled with the number of bytes actually received, might be less than you requested. + * @return The status of the operation, if the socket has disconnected etc. + */ + Status receive_raw(void *data, size_t data_size, size_t &received); + private: /*! * Reads size bytes into dest from the socket. + * Unlike receive_raw, this will keep trying + * to receive data until 'size' bytes have been + * read, or the client has disconnected/there was + * an error. * * @param dest Where to read the data into * @param size The number of bytes to read * @return Operation status. */ - Status read_recv(void *dest, size_t size); + Status receive_all(void *dest, size_t size); std::string unprocessed_buffer; std::unique_ptr recv_buffer; diff --git a/main.cpp b/main.cpp index 09fae6c..9481b9e 100644 --- a/main.cpp +++ b/main.cpp @@ -41,13 +41,18 @@ void server() { if(selector.is_ready(**iter)) { - fr::Packet packet; - if((*iter)->receive(packet) == fr::Socket::Success) + std::string message(1024, '\0'); + size_t received; + if((*iter)->receive_raw(&message[0], 1024, received) == fr::Socket::Success) { - std::string message; - packet >> message; - std::cout << (*iter)->get_remote_address() << " sent: " << message << std::endl; - iter++; + + std::cout << (*iter)->get_remote_address() << " sent: " << message.substr(0, received) << std::endl; + + message.clear(); + message = "HTTP/1.1 " + std::to_string(200) + " \r\nConnection: close\r\nContent-type: text/html\r\n\r\n

Hey

\r\n"; + (*iter)->send_raw(&message[0], message.size()); + std::cout << "Sent" << std::endl; + (*iter)->close(); } else { @@ -67,19 +72,19 @@ void server() void client() { - fr::TcpSocket socket; - socket.connect("127.0.0.1", "8081"); - - fr::TcpSocket socket2; - socket2.connect("127.0.0.1", "8081"); - - fr::Packet packet; - packet << "Hello, World! - From socket 1"; - socket.send(packet); - - packet.clear(); - packet << "Hello, world! - From socket 2"; - socket2.send(packet); +// fr::TcpSocket socket; +// socket.connect("127.0.0.1", "8081"); +// +// fr::TcpSocket socket2; +// socket2.connect("127.0.0.1", "8081"); +// +// fr::Packet packet; +// packet << "Hello, World! - From socket 1"; +// socket.send(packet); +// +// packet.clear(); +// packet << "Hello, world! - From socket 2"; +// socket2.send(packet); return; } diff --git a/src/TcpSocket.cpp b/src/TcpSocket.cpp index 2b30127..90b86c2 100644 --- a/src/TcpSocket.cpp +++ b/src/TcpSocket.cpp @@ -23,7 +23,6 @@ namespace fr Socket::Status TcpSocket::send(const Packet &packet) { //Get packet data - size_t sent = 0; std::string data = packet.get_buffer(); //Prepend packet length @@ -32,9 +31,15 @@ namespace fr memcpy(&data[0], &length, sizeof(uint32_t)); //Send it - while(sent < packet.get_buffer().size()) + return send_raw(data.c_str(), data.size()); + } + + Socket::Status TcpSocket::send_raw(const char *data, size_t size) + { + size_t sent = 0; + while(sent < size) { - ssize_t status = ::send(socket_descriptor, data.c_str() + sent, data.size() - sent, 0); + ssize_t status = ::send(socket_descriptor, data + sent, size - sent, 0); if(status > 0) { sent += status; @@ -52,7 +57,6 @@ namespace fr } } } - return Socket::Status::Success; } @@ -62,14 +66,14 @@ namespace fr //Try to read packet length uint32_t packet_length = 0; - status = read_recv(&packet_length, sizeof(packet_length)); + status = receive_all(&packet_length, sizeof(packet_length)); if(status != Socket::Status::Success) return status; packet_length = ntohl(packet_length); //Now we've got the length, read the rest of the data in std::string data(packet_length, 'c'); - status = read_recv(&data[0], packet_length); + status = receive_all(&data[0], packet_length); if(status != Socket::Status::Success) return status; @@ -81,17 +85,32 @@ namespace fr void TcpSocket::close() { - if(!is_connected) + if(is_connected) { ::close(socket_descriptor); is_connected = false; } } - Socket::Status TcpSocket::read_recv(void *dest, size_t size) + Socket::Status TcpSocket::receive_all(void *dest, size_t size) { - //Keep calling recv until there's enough data in the buffer if needed - while(unprocessed_buffer.size() < size) + size_t bytes_read = 0; + while(bytes_read < size) + { + size_t read = 0; + Socket::Status status = receive_raw((uintptr_t*)dest + bytes_read, size, read); + if(status == Socket::Status::Success) + bytes_read += read; + else + return status; + } + return Socket::Status::Success; + } + + Socket::Status TcpSocket::receive_raw(void *data, size_t data_size, size_t &received) + { + received = 0; + if(unprocessed_buffer.size() < data_size) { //Read RECV_CHUNK_SIZE bytes into the recv buffer ssize_t status = ::recv(socket_descriptor, recv_buffer.get(), RECV_CHUNK_SIZE, 0); @@ -99,6 +118,7 @@ namespace fr if(status > 0) { unprocessed_buffer += {recv_buffer.get(), (size_t)status}; + received += status; } else { @@ -112,14 +132,22 @@ namespace fr return Socket::Status::Disconnected; } } + + if(received > data_size) + received = data_size; + } + else + { + received = data_size; } //Copy data to where it needs to go - memcpy(dest, &unprocessed_buffer[0], size); - unprocessed_buffer.erase(0, size); + memcpy(data, &unprocessed_buffer[0], received); + unprocessed_buffer.erase(0, received); return Socket::Status::Success; } + void TcpSocket::set_descriptor(int descriptor) { socket_descriptor = descriptor;