metacall/mc_socket.cpp

334 lines
6.7 KiB
C++
Raw Normal View History

2011-08-28 15:45:42 +00:00
//
// Copyright (c) 2011 Alex Yatskov
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#include "metacall.hpp"
#include "mc_socket.hpp"
namespace metacall {
2011-09-05 03:05:33 +00:00
#ifdef _WIN32
2011-08-28 15:45:42 +00:00
2011-09-04 18:14:55 +00:00
//
// Winsock
//
2011-09-04 18:49:21 +00:00
#pragma comment(lib, "ws2_32.lib")
2011-09-05 03:26:55 +00:00
static struct Winsock {
2011-09-05 03:05:33 +00:00
Winsock() {
2011-09-04 18:49:21 +00:00
WSAData data;
WSAStartup(MAKEWORD(2, 2), &data);
2011-09-04 18:14:55 +00:00
}
2011-09-05 03:05:33 +00:00
~Winsock() {
2011-09-04 18:14:55 +00:00
WSACleanup();
}
2012-02-19 17:55:16 +00:00
} s_winsock;
2011-09-04 18:14:55 +00:00
#endif
2011-08-28 15:45:42 +00:00
2011-09-05 03:05:33 +00:00
//
// Constants
//
enum {
SOCKET_INVALID = -1
};
2011-08-28 15:45:42 +00:00
//
// Socket
//
Socket::Socket() :
2012-02-19 15:08:38 +00:00
m_socket(SOCKET_INVALID)
2011-08-28 15:45:42 +00:00
{
}
Socket::~Socket() {
close();
}
bool Socket::open() {
close();
2012-02-19 15:08:38 +00:00
m_socket = socket(AF_INET, SOCK_STREAM, 0);
2011-08-28 15:45:42 +00:00
return opened();
}
void Socket::close() {
if (!opened()) {
return;
}
#ifdef _WIN32
2012-02-19 15:08:38 +00:00
closesocket(m_socket);
2011-08-28 15:45:42 +00:00
#else
2012-02-19 15:08:38 +00:00
::close(m_socket);
2011-08-28 15:45:42 +00:00
#endif
2012-02-19 15:08:38 +00:00
m_socket = SOCKET_INVALID;
2011-08-28 15:45:42 +00:00
}
int Socket::release() {
2012-02-19 15:08:38 +00:00
const int temp = m_socket;
m_socket = SOCKET_INVALID;
2011-08-28 15:45:42 +00:00
return temp;
}
void Socket::set(int socket) {
close();
2012-02-19 15:08:38 +00:00
m_socket = socket;
2011-08-28 15:45:42 +00:00
}
void Socket::setNagle(bool enable) {
ASSERT(opened());
const int value = enable ? 0 : 1;
const char* const valuePtr = reinterpret_cast<const char*>(&value);
setsockopt(
2012-02-19 15:08:38 +00:00
m_socket,
2011-08-28 15:45:42 +00:00
IPPROTO_TCP,
TCP_NODELAY,
valuePtr,
sizeof(value)
);
}
void Socket::setBlocking(bool enable) {
ASSERT(opened());
#ifdef _WIN32
unsigned long nonBlock = enable ? 0 : 1;
2012-02-19 15:08:38 +00:00
ioctlsocket(m_socket, FIONBIO, &nonBlock);
2011-08-28 15:45:42 +00:00
#else
2012-02-19 15:08:38 +00:00
const int flagsOld = fcntl(m_socket, F_GETFL);
2011-08-28 15:45:42 +00:00
const int flagsNew = enable ? flagsOld & ~O_NONBLOCK : flagsOld | O_NONBLOCK;
2012-02-19 15:08:38 +00:00
fcntl(m_socket, F_SETFL, flagsNew);
2011-08-28 15:45:42 +00:00
#endif
}
bool Socket::connect(const char name[], int port) {
ASSERT(opened());
long address = 0;
if (!resolve(name, &address)) {
return false;
}
2012-02-19 15:02:35 +00:00
sockaddr_in host;
2011-08-28 15:45:42 +00:00
host.sin_port = htons(static_cast<unsigned short>(port));
host.sin_family = AF_INET;
host.sin_addr.s_addr = address;
const sockaddr* const hostPtr = reinterpret_cast<const sockaddr*>(&host);
2012-02-19 15:08:38 +00:00
if (::connect(m_socket, hostPtr, sizeof(host)) == SOCKET_INVALID) {
2011-08-28 15:45:42 +00:00
return false;
}
return true;
}
bool Socket::bind(int port) {
ASSERT(opened());
2012-02-19 15:02:35 +00:00
sockaddr_in host;
2011-08-28 15:45:42 +00:00
host.sin_family = AF_INET;
host.sin_addr.s_addr = INADDR_ANY;
host.sin_port = htons(static_cast<unsigned short>(port));
const sockaddr* const hostPtr = reinterpret_cast<const sockaddr*>(&host);
2012-02-19 15:08:38 +00:00
if (::bind(m_socket, hostPtr, sizeof(host)) == SOCKET_INVALID) {
2011-08-28 15:45:42 +00:00
return false;
}
return true;
}
bool Socket::listen(int backlog) {
ASSERT(opened());
2012-02-19 15:08:38 +00:00
return ::listen(m_socket, backlog) != SOCKET_INVALID;
2011-08-28 15:45:42 +00:00
}
bool Socket::accept(Socket* socket) {
ASSERT(opened());
socket->close();
2012-02-19 15:08:38 +00:00
socket->m_socket = ::accept(m_socket, 0, 0);
2011-08-28 15:45:42 +00:00
2012-02-19 15:08:38 +00:00
return socket->m_socket != SOCKET_INVALID;
2011-08-28 15:45:42 +00:00
}
int Socket::receive(void* buffer, int size) {
ASSERT(opened());
if (size <= 0) {
return 0;
}
return recv(
2012-02-19 15:08:38 +00:00
m_socket,
2011-08-28 15:45:42 +00:00
static_cast<char *>(buffer),
size,
0
);
}
int Socket::peek(void* buffer, int size) const {
ASSERT(opened());
if (size <= 0) {
return 0;
}
return recv(
2012-02-19 15:08:38 +00:00
m_socket,
2011-08-28 15:45:42 +00:00
static_cast<char *>(buffer),
size,
MSG_PEEK
);
}
int Socket::send(const void* buffer, int size) {
ASSERT(opened());
if (size <= 0) {
return 0;
}
return ::send(
2012-02-19 15:08:38 +00:00
m_socket,
2011-08-28 15:45:42 +00:00
static_cast<const char*>(buffer),
size,
0
);
}
bool Socket::wait(unsigned mask, int seconds) const {
ASSERT(opened());
fd_set readSet;
fd_set writeSet;
fd_set exceptSet;
fd_set* readSetUse = NULL;
fd_set* writeSetUse = NULL;
fd_set* exceptSetUse = NULL;
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable:4127)
#endif
if (mask & MASK_READ) {
2011-08-28 15:45:42 +00:00
readSetUse = &readSet;
FD_ZERO(readSetUse);
2012-02-19 15:08:38 +00:00
FD_SET(m_socket, readSetUse);
2011-08-28 15:45:42 +00:00
}
if (mask & MASK_WRITE) {
2011-08-28 15:45:42 +00:00
writeSetUse = &writeSet;
FD_ZERO(writeSetUse);
2012-02-19 15:08:38 +00:00
FD_SET(m_socket, writeSetUse);
2011-08-28 15:45:42 +00:00
}
if (mask & MASK_EXCEPT) {
2011-08-28 15:45:42 +00:00
exceptSetUse = &exceptSet;
FD_ZERO(exceptSetUse);
2012-02-19 15:08:38 +00:00
FD_SET(m_socket, exceptSetUse);
2011-08-28 15:45:42 +00:00
}
#ifdef _WIN32
#pragma warning(pop)
#endif
2012-02-19 15:02:35 +00:00
timeval timeoutVal;
2011-08-28 15:45:42 +00:00
timeoutVal.tv_sec = seconds;
2012-02-19 15:02:35 +00:00
timeoutVal.tv_usec = 0;
2011-08-28 15:45:42 +00:00
timeval* const timeoutPtr = seconds < 0 ? NULL : &timeoutVal;
const int result = select(
2012-02-19 15:08:38 +00:00
m_socket + 1,
2011-08-28 15:45:42 +00:00
readSetUse,
writeSetUse,
exceptSetUse,
timeoutPtr
);
return result > 0;
}
bool Socket::opened() const {
2012-02-19 15:08:38 +00:00
return m_socket != SOCKET_INVALID;
2011-08-28 15:45:42 +00:00
}
bool Socket::connected() const {
if (!opened()) {
return false;
}
if (!wait(MASK_READ, 0)) {
return true;
}
byte buffer = 0;
if (peek(&buffer, sizeof(buffer)) == 0) {
return false;
}
return true;
}
const char* Socket::hostname() const {
if (!opened()) {
return NULL;
}
2012-02-19 15:02:35 +00:00
sockaddr_in host;
2011-08-28 15:45:42 +00:00
socklen_t hostSize = sizeof(host);
sockaddr* const hostPtr = reinterpret_cast<sockaddr*>(&host);
2012-02-19 15:08:38 +00:00
if (getsockname(m_socket, hostPtr, &hostSize) == SOCKET_INVALID) {
2011-08-28 15:45:42 +00:00
return NULL;
}
return inet_ntoa(host.sin_addr);
}
bool Socket::resolve(const char name[], long* address) {
if ((*address = inet_addr(name)) != INADDR_NONE) {
return true;
}
hostent* const host = gethostbyname(name);
if (host == NULL) {
return false;
}
*address = *reinterpret_cast<long*>(host->h_addr_list[0]);
return true;
}
}