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)
This commit is contained in:
Fred Nicolson 2016-12-10 12:59:07 +00:00
parent a9493fca2a
commit a3eb2ccd0a
3 changed files with 95 additions and 32 deletions

View File

@ -67,15 +67,45 @@ public:
return is_connected; 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: private:
/*! /*!
* Reads size bytes into dest from the socket. * 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 dest Where to read the data into
* @param size The number of bytes to read * @param size The number of bytes to read
* @return Operation status. * @return Operation status.
*/ */
Status read_recv(void *dest, size_t size); Status receive_all(void *dest, size_t size);
std::string unprocessed_buffer; std::string unprocessed_buffer;
std::unique_ptr<char[]> recv_buffer; std::unique_ptr<char[]> recv_buffer;

View File

@ -41,13 +41,18 @@ void server()
{ {
if(selector.is_ready(**iter)) if(selector.is_ready(**iter))
{ {
fr::Packet packet; std::string message(1024, '\0');
if((*iter)->receive(packet) == fr::Socket::Success) 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.substr(0, received) << std::endl;
std::cout << (*iter)->get_remote_address() << " sent: " << message << std::endl;
iter++; message.clear();
message = "HTTP/1.1 " + std::to_string(200) + " \r\nConnection: close\r\nContent-type: text/html\r\n\r\n<h1>Hey</h1>\r\n";
(*iter)->send_raw(&message[0], message.size());
std::cout << "Sent" << std::endl;
(*iter)->close();
} }
else else
{ {
@ -67,19 +72,19 @@ void server()
void client() void client()
{ {
fr::TcpSocket socket; // fr::TcpSocket socket;
socket.connect("127.0.0.1", "8081"); // socket.connect("127.0.0.1", "8081");
//
fr::TcpSocket socket2; // fr::TcpSocket socket2;
socket2.connect("127.0.0.1", "8081"); // socket2.connect("127.0.0.1", "8081");
//
fr::Packet packet; // fr::Packet packet;
packet << "Hello, World! - From socket 1"; // packet << "Hello, World! - From socket 1";
socket.send(packet); // socket.send(packet);
//
packet.clear(); // packet.clear();
packet << "Hello, world! - From socket 2"; // packet << "Hello, world! - From socket 2";
socket2.send(packet); // socket2.send(packet);
return; return;
} }

View File

@ -23,7 +23,6 @@ namespace fr
Socket::Status TcpSocket::send(const Packet &packet) Socket::Status TcpSocket::send(const Packet &packet)
{ {
//Get packet data //Get packet data
size_t sent = 0;
std::string data = packet.get_buffer(); std::string data = packet.get_buffer();
//Prepend packet length //Prepend packet length
@ -32,9 +31,15 @@ namespace fr
memcpy(&data[0], &length, sizeof(uint32_t)); memcpy(&data[0], &length, sizeof(uint32_t));
//Send it //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) if(status > 0)
{ {
sent += status; sent += status;
@ -52,7 +57,6 @@ namespace fr
} }
} }
} }
return Socket::Status::Success; return Socket::Status::Success;
} }
@ -62,14 +66,14 @@ namespace fr
//Try to read packet length //Try to read packet length
uint32_t packet_length = 0; 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) if(status != Socket::Status::Success)
return status; return status;
packet_length = ntohl(packet_length); packet_length = ntohl(packet_length);
//Now we've got the length, read the rest of the data in //Now we've got the length, read the rest of the data in
std::string data(packet_length, 'c'); 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) if(status != Socket::Status::Success)
return status; return status;
@ -81,17 +85,32 @@ namespace fr
void TcpSocket::close() void TcpSocket::close()
{ {
if(!is_connected) if(is_connected)
{ {
::close(socket_descriptor); ::close(socket_descriptor);
is_connected = false; 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 size_t bytes_read = 0;
while(unprocessed_buffer.size() < size) 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 //Read RECV_CHUNK_SIZE bytes into the recv buffer
ssize_t status = ::recv(socket_descriptor, recv_buffer.get(), RECV_CHUNK_SIZE, 0); ssize_t status = ::recv(socket_descriptor, recv_buffer.get(), RECV_CHUNK_SIZE, 0);
@ -99,6 +118,7 @@ namespace fr
if(status > 0) if(status > 0)
{ {
unprocessed_buffer += {recv_buffer.get(), (size_t)status}; unprocessed_buffer += {recv_buffer.get(), (size_t)status};
received += status;
} }
else else
{ {
@ -112,14 +132,22 @@ namespace fr
return Socket::Status::Disconnected; return Socket::Status::Disconnected;
} }
} }
if(received > data_size)
received = data_size;
}
else
{
received = data_size;
} }
//Copy data to where it needs to go //Copy data to where it needs to go
memcpy(dest, &unprocessed_buffer[0], size); memcpy(data, &unprocessed_buffer[0], received);
unprocessed_buffer.erase(0, size); unprocessed_buffer.erase(0, received);
return Socket::Status::Success; return Socket::Status::Success;
} }
void TcpSocket::set_descriptor(int descriptor) void TcpSocket::set_descriptor(int descriptor)
{ {
socket_descriptor = descriptor; socket_descriptor = descriptor;