2015-10-28 03:21:28 +08:00
|
|
|
#pragma once
|
|
|
|
|
2017-10-09 16:53:03 +08:00
|
|
|
#include <experimental/optional>
|
2017-10-17 20:05:08 +08:00
|
|
|
#include <functional>
|
2016-08-02 05:14:09 +08:00
|
|
|
#include <iostream>
|
|
|
|
|
2018-01-15 21:03:07 +08:00
|
|
|
#include "io/network/endpoint.hpp"
|
2017-10-09 16:53:03 +08:00
|
|
|
|
2017-03-06 20:37:51 +08:00
|
|
|
namespace io::network {
|
2015-10-28 03:21:28 +08:00
|
|
|
|
2017-03-06 20:37:51 +08:00
|
|
|
/**
|
|
|
|
* This class creates a network socket.
|
2018-01-15 21:03:07 +08:00
|
|
|
* It is used to connect/bind/listen on a Endpoint (address + port).
|
2017-03-06 20:37:51 +08:00
|
|
|
* It has wrappers for setting network socket flags and wrappers for
|
|
|
|
* reading/writing data from/to the socket.
|
|
|
|
*/
|
2017-02-18 18:54:37 +08:00
|
|
|
class Socket {
|
|
|
|
public:
|
2017-10-09 16:53:03 +08:00
|
|
|
Socket() = default;
|
|
|
|
Socket(const Socket &) = delete;
|
|
|
|
Socket &operator=(const Socket &) = delete;
|
|
|
|
Socket(Socket &&);
|
|
|
|
Socket &operator=(Socket &&);
|
2017-03-06 20:37:51 +08:00
|
|
|
~Socket();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes the socket if it is open.
|
|
|
|
*/
|
|
|
|
void Close();
|
|
|
|
|
2018-01-25 00:42:48 +08:00
|
|
|
/**
|
|
|
|
* Shutdown the socket if it is open.
|
|
|
|
*/
|
|
|
|
void Shutdown();
|
|
|
|
|
2017-03-06 20:37:51 +08:00
|
|
|
/**
|
|
|
|
* Checks whether the socket is open.
|
|
|
|
*
|
|
|
|
* @return socket open status:
|
|
|
|
* true if the socket is open
|
|
|
|
* false if the socket is closed
|
|
|
|
*/
|
2017-10-17 20:05:08 +08:00
|
|
|
bool IsOpen() const;
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Connects the socket to the specified endpoint.
|
|
|
|
*
|
2018-01-15 21:03:07 +08:00
|
|
|
* @param endpoint Endpoint to which to connect to
|
2017-03-06 20:37:51 +08:00
|
|
|
*
|
|
|
|
* @return connection success status:
|
|
|
|
* true if the connect succeeded
|
|
|
|
* false if the connect failed
|
|
|
|
*/
|
2018-01-15 21:03:07 +08:00
|
|
|
bool Connect(const Endpoint &endpoint);
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Binds the socket to the specified endpoint.
|
|
|
|
*
|
2018-01-15 21:03:07 +08:00
|
|
|
* @param endpoint Endpoint to which to bind to
|
2017-03-06 20:37:51 +08:00
|
|
|
*
|
|
|
|
* @return bind success status:
|
|
|
|
* true if the bind succeeded
|
|
|
|
* false if the bind failed
|
|
|
|
*/
|
2018-01-15 21:03:07 +08:00
|
|
|
bool Bind(const Endpoint &endpoint);
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Start listening on the bound socket.
|
|
|
|
*
|
2017-09-12 21:25:43 +08:00
|
|
|
* @param backlog maximum number of pending connections in the connection
|
|
|
|
* queue
|
2017-03-06 20:37:51 +08:00
|
|
|
*
|
|
|
|
* @return listen success status:
|
|
|
|
* true if the listen succeeded
|
|
|
|
* false if the listen failed
|
|
|
|
*/
|
|
|
|
bool Listen(int backlog);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Accepts a new connection.
|
|
|
|
* This function accepts a new connection on a listening socket.
|
|
|
|
*
|
2017-10-09 16:53:03 +08:00
|
|
|
* @return socket if accepted, nullopt otherwise.
|
2017-03-06 20:37:51 +08:00
|
|
|
*/
|
2017-10-09 16:53:03 +08:00
|
|
|
std::experimental::optional<Socket> Accept();
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the socket to non-blocking.
|
|
|
|
*/
|
2017-12-12 19:25:15 +08:00
|
|
|
void SetNonBlocking();
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Enables TCP keep-alive on the socket.
|
|
|
|
*/
|
2017-12-12 19:25:15 +08:00
|
|
|
void SetKeepAlive();
|
2017-03-06 20:37:51 +08:00
|
|
|
|
2017-05-10 20:52:51 +08:00
|
|
|
/**
|
|
|
|
* Enables TCP no_delay on the socket.
|
|
|
|
* When enabled, the socket doesn't wait for an ACK of every data packet
|
|
|
|
* before sending the next packet.
|
|
|
|
*/
|
2017-12-12 19:25:15 +08:00
|
|
|
void SetNoDelay();
|
2017-05-10 20:52:51 +08:00
|
|
|
|
2017-04-07 23:09:49 +08:00
|
|
|
/**
|
|
|
|
* Sets the socket timeout.
|
|
|
|
*
|
|
|
|
* @param sec timeout seconds value
|
|
|
|
* @param usec timeout microseconds value
|
|
|
|
*/
|
2017-12-12 19:25:15 +08:00
|
|
|
void SetTimeout(long sec, long usec);
|
2017-04-07 23:09:49 +08:00
|
|
|
|
2018-01-24 19:16:14 +08:00
|
|
|
/**
|
|
|
|
* Checks if there are any errors on a socket. Returns 0 if there are none.
|
|
|
|
*/
|
|
|
|
int ErrorStatus() const;
|
|
|
|
|
2017-03-06 20:37:51 +08:00
|
|
|
/**
|
2017-10-09 16:53:03 +08:00
|
|
|
* Returns the socket file descriptor.
|
2017-03-06 20:37:51 +08:00
|
|
|
*/
|
2017-10-09 16:53:03 +08:00
|
|
|
int fd() const { return socket_; }
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the currently active endpoint of the socket.
|
|
|
|
*/
|
2018-01-15 21:03:07 +08:00
|
|
|
const Endpoint &endpoint() const { return endpoint_; }
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Write data to the socket.
|
|
|
|
* Theese functions guarantee that all data will be written.
|
|
|
|
*
|
2017-10-17 20:05:08 +08:00
|
|
|
* @param data uint8_t* to data that should be written
|
2017-03-06 20:37:51 +08:00
|
|
|
* @param len length of char* or uint8_t* data
|
2018-01-25 20:08:59 +08:00
|
|
|
* @param have_more set to true if you plan to send more data to allow the
|
|
|
|
* kernel to buffer the data instead of immediately sending it out
|
2017-10-17 20:05:08 +08:00
|
|
|
* @param keep_retrying while function executes to true socket will retry to
|
|
|
|
* write data if nonterminal error occurred on socket (EAGAIN, EWOULDBLOCK,
|
|
|
|
* EINTR)... useful if socket is in nonblocking mode or timeout is set on a
|
|
|
|
* socket. By default Write doesn't retry if any error occurrs.
|
|
|
|
*
|
|
|
|
* TODO: Logic for retrying can be in derived class or in a wrapper of this
|
|
|
|
* class, unfortunately from current return value we don't know what error
|
|
|
|
* occured nor how much data was written.
|
2017-03-06 20:37:51 +08:00
|
|
|
*
|
|
|
|
* @return write success status:
|
|
|
|
* true if write succeeded
|
|
|
|
* false if write failed
|
|
|
|
*/
|
2018-01-25 20:08:59 +08:00
|
|
|
bool Write(const uint8_t *data, size_t len, bool have_more = false,
|
2017-10-17 20:05:08 +08:00
|
|
|
const std::function<bool()> &keep_retrying = [] { return false; });
|
2018-01-25 20:08:59 +08:00
|
|
|
bool Write(const std::string &s, bool have_more = false,
|
2017-10-25 20:47:46 +08:00
|
|
|
const std::function<bool()> &keep_retrying = [] { return false; });
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Read data from the socket.
|
|
|
|
* This function is a direct wrapper for the read function.
|
|
|
|
*
|
|
|
|
* @param buffer pointer to the read buffer
|
|
|
|
* @param len length of the read buffer
|
|
|
|
*
|
|
|
|
* @return read success status:
|
|
|
|
* > 0 if data was read, means number of read bytes
|
|
|
|
* == 0 if the client closed the connection
|
|
|
|
* < 0 if an error has occurred
|
|
|
|
*/
|
2017-10-17 20:05:08 +08:00
|
|
|
// TODO: Return type should be something like StatusOr<int> which would return
|
|
|
|
// number of read bytes if read succeeded and error code and error message
|
|
|
|
// otherwise (deduced from errno). We can implement that type easily on top of
|
|
|
|
// std::variant once c++17 becomes available in memgraph.
|
2017-09-12 21:25:43 +08:00
|
|
|
int Read(void *buffer, size_t len);
|
2017-03-06 20:37:51 +08:00
|
|
|
|
|
|
|
private:
|
2018-01-24 19:16:14 +08:00
|
|
|
Socket(int fd, const Endpoint &endpoint) : socket_(fd), endpoint_(endpoint) {}
|
2017-03-06 20:37:51 +08:00
|
|
|
|
2017-10-09 16:53:03 +08:00
|
|
|
int socket_ = -1;
|
2018-01-15 21:03:07 +08:00
|
|
|
Endpoint endpoint_;
|
2015-10-28 03:21:28 +08:00
|
|
|
};
|
2018-01-15 21:03:07 +08:00
|
|
|
} // namespace io::network
|