Implement base Socket class

Fix derp in IPAddress::resolve
This commit is contained in:
Phillip Stephens 2015-10-27 08:47:07 -07:00
parent 9e7733eb02
commit 7c4d0bb6fd
6 changed files with 269 additions and 23 deletions

View File

@ -154,10 +154,12 @@ add_library(AthenaZelda EXCLUDE_FROM_ALL
)
add_library(AthenaNet
src/sockwrap.c
src/Athena/Socket.cpp
src/Athena/OAuth.cpp
src/Athena/IPAddress.cpp
include/sockwrap.h
include/Athena/Socket.hpp
include/Athena/OAuth.hpp
include/Athena/IPAddress.hpp)

View File

@ -1,15 +1,43 @@
#ifndef SOCKET_HPP
#define SOCKET_HPP
#include "Athena/Global.hpp"
#include "sockwrap.h"
namespace Athena
{
namespace net
{
class Socket
{
// Disable copying
public:
enum Type
{
TCP,
UDP
};
explicit Socket(Type type = TCP);
virtual ~Socket() { close() ; }
void setBlocking(bool blocking);
bool isBlocking() const { return m_isBlocking; }
protected:
sockhandle_t handle() { return m_handle; }
void create();
void close();
private:
// Disable copying
Socket(const Socket&)=delete;
Socket& operator=(const Socket&)=delete;
Type m_type;
sockhandle_t m_handle;
bool m_isBlocking;
};
}
}

61
include/sockwrap.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef SOCKWRAP_H
#define SOCKWRAP_H
#include <stdint.h>
#include <stdbool.h>
#ifdef _WIN32
#ifdef _WIN32_WINDOWS
#undef _WIN32_WINDOWS
#endif
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINDOWS 0x0501
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <basetsd>
typedef UINT_PTR sockhandle_t;
typedef int addrlen_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
typedef int32_t sockhandle_t;
typedef socklen_t addrlen_t;
#endif
#define ANY_PORT 0
#ifdef __cplusplus
extern "C" {
#endif
// TODO: More granular errors
typedef enum
{
SS_Done,
SS_NotReady,
SS_Partial,
SS_Disconnected,
SS_Error
} sockstatus_t;
struct sockaddr_in sock_create_address(uint32_t address, uint16_t port);
void sock_close_socket(sockhandle_t sock);
void sock_set_blocking(sockhandle_t sock, bool block);
sockstatus_t sock_error_status();
sockhandle_t sock_invalid_socket();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,25 +1,5 @@
#include "Athena/IPAddress.hpp"
#ifdef __unix__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#elif defined(_WIN32)
#ifdef _WIN32_WINDOWS
#undef _WIN32_WINDOWS
#endif
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINDOWS 0x0501
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include "sockwrap.h" // To resolve local IP
namespace Athena
{
@ -55,6 +35,35 @@ const std::string IPAddress::toString() const
return inet_ntoa(address);
}
const atUint32 IPAddress::toInt() const
{
return ntohl(m_address);
}
IPAddress IPAddress::localAddress()
{
sockhandle_t sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == sock_invalid_socket())
return IPAddress();
struct sockaddr_in address = sock_create_address(ntohl(INADDR_LOOPBACK), 9);
if (connect(sock, reinterpret_cast<sockaddr*>(&address), sizeof(struct sockaddr_in)) == -1)
{
sock_close_socket(sock);
return IPAddress();
}
addrlen_t size = sizeof(address);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&address), &size) == -1)
{
sock_close_socket(sock);
return IPAddress();
}
sock_close_socket(sock);
return IPAddress(ntohl(address.sin_addr.s_addr));
}
void IPAddress::resolve(const std::string& address)
{
if (address == "0.0.0.0")
@ -72,10 +81,11 @@ void IPAddress::resolve(const std::string& address)
atUint32 ip = inet_addr(address.c_str());
if (ip == INADDR_NONE)
{
addrinfo hints = {0};
addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_INET;
addrinfo* result = nullptr;
if (getaddrinfo(address.c_str(), NULL, &hints, &result))
if (getaddrinfo(address.c_str(), nullptr, &hints, &result) == 0)
{
if (result)
{

View File

@ -1 +1,53 @@
#include "Athena/Socket.hpp"
namespace Athena
{
namespace net
{
Socket::Socket(Socket::Type type)
: m_type(type),
m_handle(sock_invalid_socket()),
m_isBlocking(true)
{}
void Socket::create()
{
if (m_handle == sock_invalid_socket())
{
m_handle = socket(PF_INET, m_type == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
setBlocking(m_isBlocking);
int yes = 1;
if (m_type == TCP)
{
// Disable Nagle algorithm
if (setsockopt(m_handle, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int)) == -1)
atWarning("Failed to set socket option \"TCP_NODELAY\", TCP packets will be buffered");
#ifdef __APPLE__
if (setsockopt(m_handle, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(int)) == -1)
atWarning("Failed to set socket option \"SO_NOSIGPIPE\"");
#endif
}
else
{
if (setsockopt(m_handle, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(int)) == -1)
atWarning("Failed to enable broadcast on UDP socket");
}
}
}
void Socket::close()
{
sock_close_socket(m_handle);
m_handle = sock_invalid_socket();
}
void Socket::setBlocking(bool blocking)
{
sock_set_blocking(m_handle, blocking);
}
}
}

93
src/sockwrap.c Normal file
View File

@ -0,0 +1,93 @@
#include "sockwrap.h"
#ifndef _WIN32
#include <errno.h>
#include <fcntl.h>
#endif
struct sockaddr_in sock_create_address(uint32_t address, uint16_t port)
{
struct sockaddr_in addr = {0};
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
#ifdef __APPLE__
addr.sin_len = sizeof(addr);
#endif
return addr;
}
void sock_close_socket(sockhandle_t sock)
{
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
void sock_set_blocking(sockhandle_t sock, bool block)
{
#ifdef _WIN32
u_long blocking = block ? 0 : 1;
ioctlsocket(sock, FIONBIO, &blocking);
#else
int status = fcntl(sock, F_GETFL);
if (block)
status &= ~O_NONBLOCK;
else
status |= O_NONBLOCK;
fcntl(sock, F_SETFL, status);
#endif
}
sockstatus_t sock_error_status()
{
#ifdef _WIN32
switch(WSAGetLastError())
{
case WSAEWOULDBLOCK:
case WSAEALREADY:
return SS_NotReady;
case WSAECONNABORTED:
case WSAECONNRESET:
case WSATIMEDOUT:
case WSAENETRESET:
case WSAENOTCONN:
return SS_Disconnected;
case WSAEISCONN:
return SS_Done;
default:
return SS_Error;
}
#else
if (errno == EAGAIN || errno == EINPROGRESS)
return SS_NotReady;
switch(errno)
{
case EWOULDBLOCK: return SS_NotReady;
case ECONNABORTED:
case ECONNRESET:
case ETIMEDOUT:
case ENETRESET:
case ENOTCONN:
case EPIPE:
return SS_Disconnected;
default:
return SS_Error;
}
#endif
}
sockhandle_t sock_invalid_socket()
{
#if _WIN32
return INVALID_SOCKET;
#else
return -1;
#endif
}