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:
parent
a9493fca2a
commit
a3eb2ccd0a
@ -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;
|
||||||
|
|||||||
43
main.cpp
43
main.cpp
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
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)
|
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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user