diff --git a/include/NetworkEncoding.h b/include/NetworkEncoding.h index 9184b2a..fd6de19 100644 --- a/include/NetworkEncoding.h +++ b/include/NetworkEncoding.h @@ -47,5 +47,20 @@ inline double ntohd(double val) return val; } +//Windows and UNIX require some different headers. +//We also need some compatibility defines for cross platform support. +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#else +#define closesocket(x) close(x) +#define INVALID_SOCKET 0 +#define SOCKET_ERROR -1 +#include +#include +#endif + #endif //FRNETLIB_NETWORKENCODING_H diff --git a/include/Packet.h b/include/Packet.h index 0c2db58..c875bee 100644 --- a/include/Packet.h +++ b/include/Packet.h @@ -17,7 +17,7 @@ public: * * @return A string containing all of the data added to the packet */ - inline const std::string &construct_packet() + inline const std::string &construct_packet() const { return buffer; } diff --git a/include/Socket.h b/include/Socket.h index 068e1e3..a210380 100644 --- a/include/Socket.h +++ b/include/Socket.h @@ -6,11 +6,63 @@ #define FRNETLIB_SOCKET_H +#include "Packet.h" + class Socket { public: + enum Status + { + Unknown = 0, + Success = 1, + ListenFailed = 2, + BindFailed = 3 + }; -private: + /*! + * Send a packet through the socket + * + * @param packet The packet to send + * @return True on success, false on failure. + */ + virtual bool send(const Packet &packet)=0; + + /*! + * Receive a packet through the socket + * + * @param packet The packet to receive + * @return True on success, false on failure. + */ + virtual bool receive(Packet &packet)=0; + + /*! + * Close the connection. + */ + virtual void close()=0; + + /*! + * Sets the socket's printable remote address + * + * @param addr The string address + */ + inline virtual void set_remote_address(const std::string &addr) + { + remote_address = addr; + } + + /*! + * Gets the socket's printable remote address + * + * @return The string address + */ + inline virtual const std::string &get_remote_address() + { + return remote_address; + } + +protected: + int socket_descriptor; + std::string remote_address; }; diff --git a/include/TcpListener.h b/include/TcpListener.h index 57a617a..f410666 100644 --- a/include/TcpListener.h +++ b/include/TcpListener.h @@ -4,12 +4,43 @@ #ifndef FRNETLIB_TCPLISTENER_H #define FRNETLIB_TCPLISTENER_H +#include +#include +#include "TcpSocket.h" +#include "Socket.h" +#include "NetworkEncoding.h" +namespace fr +{ class TcpListener { +public: + TcpListener(); + /*! + * Listens to the given port for connections + * + * @param port The port to bind to + * @return If the operation was successful + */ + Socket::Status listen(const std::string &port); + + /*! + * Accepts a new connection. + * + * @param client Where to store the connection information + * @return True on success. False on failure. + */ + Socket::Status accept(TcpSocket &client); + +private: + void *get_sin_addr(struct sockaddr *sa); + addrinfo hints; + int socket_descriptor; }; +} + #endif //FRNETLIB_TCPLISTENER_H diff --git a/include/TcpSocket.h b/include/TcpSocket.h index 4af2928..9786966 100644 --- a/include/TcpSocket.h +++ b/include/TcpSocket.h @@ -5,12 +5,47 @@ #ifndef FRNETLIB_TCPSOCKET_H #define FRNETLIB_TCPSOCKET_H +#include "Socket.h" + namespace fr { - class TcpSocket - { - }; +class TcpSocket : public Socket +{ +public: + /*! + * Send a packet through the socket + * + * @param packet The packet to send + * @return True on success, false on failure. + */ + virtual bool send(const Packet &packet); + + /*! + * Receive a packet through the socket + * + * @param packet The packet to receive + * @return True on success, false on failure. + */ + virtual bool receive(Packet &packet); + + /*! + * Close the connection. + */ + virtual void close(); + + /*! + * Sets the socket file descriptor. + * + * @param descriptor The socket descriptor. + */ + void set_descriptor(int descriptor); + +private: + ssize_t read_recv(); + std::string unprocessed_buffer; +}; + } diff --git a/src/TcpListener.cpp b/src/TcpListener.cpp index af3be94..242b71f 100644 --- a/src/TcpListener.cpp +++ b/src/TcpListener.cpp @@ -3,3 +3,98 @@ // #include "TcpListener.h" + +namespace fr +{ + + const int yes = 1; + const int no = 0; + + TcpListener::TcpListener() + { + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; //IPv6 or IPv4. NOTE: Might want to make configurable. + hints.ai_socktype = SOCK_STREAM; //TCP + hints.ai_flags = AI_PASSIVE; //Have the IP filled in for us + } + + Socket::Status TcpListener::listen(const std::string &port) + { + addrinfo *info; + if(int status = getaddrinfo(NULL, port.c_str(), &hints, &info) != 0) + { + return Socket::Status::Unknown; + } + //Try each of the results until we listen successfully + addrinfo *c; + for(c = info; c != nullptr; c = c->ai_next) + { + //Attempt to connect + if((socket_descriptor = socket(c->ai_family, c->ai_socktype, c->ai_protocol)) == INVALID_SOCKET) + { + continue; + } + //Set address re-use option + if(setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(int)) == SOCKET_ERROR) + { + continue; + } + + //Attempt to bind + if(bind(socket_descriptor, c->ai_addr, c->ai_addrlen) == SOCKET_ERROR) + { + closesocket(socket_descriptor); + continue; + } + break; + } + + //Check that we've actually bound + if(c == nullptr) + { + return Socket::Status::BindFailed; + } + + //We're done with this now, cleanup + freeaddrinfo(info); + + //Listen to socket + if(::listen(socket_descriptor, 10) == SOCKET_ERROR) + { + return Socket::ListenFailed; + } + return Socket::Success; + } + + Socket::Status TcpListener::accept(TcpSocket &client) + { + //Prepare to wait for the client + sockaddr_storage client_addr; + int client_descriptor; + char client_printable_addr[INET6_ADDRSTRLEN]; + + //Accept one + socklen_t client_addr_len = sizeof client_addr; + client_descriptor = ::accept(socket_descriptor, (sockaddr*)&client_addr, &client_addr_len); + if(client_descriptor == SOCKET_ERROR) + return Socket::Unknown; + + //Get printable address. If we failed then set it as just 'unknown' + int err = getnameinfo((sockaddr*)&client_addr, client_addr_len, client_printable_addr, sizeof(client_printable_addr), 0,0,NI_NUMERICHOST); + if(err != 0) + strcpy(client_printable_addr, "unknown"); + + //Set client data + client.set_descriptor(client_descriptor); + client.set_remote_address(client_printable_addr); + + return Socket::Success; + } + + void *TcpListener::get_sin_addr(struct sockaddr *sa) + { + if(sa->sa_family == AF_INET) + return &(((sockaddr_in*)sa)->sin_addr); + return &(((sockaddr_in6*)sa)->sin6_addr); + } +} \ No newline at end of file diff --git a/src/TcpSocket.cpp b/src/TcpSocket.cpp index f1ba989..200ee85 100644 --- a/src/TcpSocket.cpp +++ b/src/TcpSocket.cpp @@ -7,4 +7,44 @@ namespace fr { + bool TcpSocket::send(const Packet &packet) + { + size_t send_index = 0; + size_t sent = 0; + + while(sent < packet.construct_packet().size()) + { + ssize_t a = ::send(socket_descriptor, &packet.construct_packet()[send_index], packet.construct_packet().size(), 0); + if(a < 1) + return false; + sent += a; + } + + return true; + } + + bool TcpSocket::receive(Packet &packet) + { + std::string recv_buffer; + + //Read packet length + uint32_t packet_length = 0; + + return false; + } + + void TcpSocket::close() + { + ::close(socket_descriptor); + } + + ssize_t TcpSocket::read_recv() + { + return 0; + } + + void TcpSocket::set_descriptor(int descriptor) + { + socket_descriptor = descriptor; + } } \ No newline at end of file