Work on TcpListener and TcpSocket.

This commit is contained in:
Fred Nicolson 2016-12-08 23:01:01 +00:00
parent 4f9502df23
commit 533412d3d0
7 changed files with 273 additions and 5 deletions

View File

@ -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 <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#else
#define closesocket(x) close(x)
#define INVALID_SOCKET 0
#define SOCKET_ERROR -1
#include <netdb.h>
#include <unistd.h>
#endif
#endif //FRNETLIB_NETWORKENCODING_H

View File

@ -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;
}

View File

@ -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;
};

View File

@ -4,12 +4,43 @@
#ifndef FRNETLIB_TCPLISTENER_H
#define FRNETLIB_TCPLISTENER_H
#include <string>
#include <netdb.h>
#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

View File

@ -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;
};
}

View File

@ -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);
}
}

View File

@ -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;
}
}