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;
}
/*!
* 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<char[]> recv_buffer;

View File

@ -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<h1>Hey</h1>\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;
}

View File

@ -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)
{
ssize_t status = ::send(socket_descriptor, data.c_str() + sent, data.size() - sent, 0);
size_t sent = 0;
while(sent < size)
{
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;