view C++/modules/Socket/Socket.cpp @ 390:d7e6d7d1e102

Socket: get rid of SocketState
author David Demelier <markand@malikania.fr>
date Fri, 03 Jul 2015 13:02:05 +0200
parents 743b3a1c71c8
children d5ec1174b707
line wrap: on
line source

/*
 * Socket.cpp -- portable C++ socket wrappers
 *
 * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <cstring>

#include "Socket.h"
#include "SocketAddress.h"

/* --------------------------------------------------------
 * System dependent code
 * -------------------------------------------------------- */

#if defined(_WIN32)
const SocketAbstract::Handle SocketAbstract::Invalid{INVALID_SOCKET};
const int SocketAbstract::Error{SOCKET_ERROR};
#else
const int SocketAbstract::Invalid{-1};
const int SocketAbstract::Error{-1};
#endif

#if defined(_WIN32)

std::string SocketAbstract::syserror(int errn)
{
	LPSTR str = nullptr;
	std::string errmsg = "Unknown error";

	FormatMessageA(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		errn,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPSTR)&str, 0, NULL);


	if (str) {
		errmsg = std::string(str);
		LocalFree(str);
	}

	return errmsg;
}

#else

std::string SocketAbstract::syserror(int errn)
{
	return strerror(errn);
}

#endif

std::string SocketAbstract::syserror()
{
#if defined(_WIN32)
	return syserror(WSAGetLastError());
#else
	return syserror(errno);
#endif
}

/* --------------------------------------------------------
 * SocketError class
 * -------------------------------------------------------- */

SocketError::SocketError(Code code, std::string function)
	: m_code{code}
	, m_function{std::move(function)}
	, m_error{SocketAbstract::syserror()}
{
}

SocketError::SocketError(Code code, std::string function, int error)
	: m_code{code}
	, m_function{std::move(function)}
	, m_error{SocketAbstract::syserror(error)}
{
}

SocketError::SocketError(Code code, std::string function, std::string error)
	: m_code{code}
	, m_function{std::move(function)}
	, m_error{std::move(error)}
{
}

/* --------------------------------------------------------
 * SocketAbstract class
 * -------------------------------------------------------- */

#if defined(_WIN32)
std::mutex SocketAbstract::s_mutex;
std::atomic<bool> SocketAbstract::s_initialized{false};
#endif

SocketAbstract::SocketAbstract(int domain, int type, int protocol)
{
#if defined(_WIN32) && !defined(SOCKET_NO_WSA_INIT)
	if (!s_initialized) {
		initialize();
	}
#endif

	m_handle = ::socket(domain, type, protocol);

	if (m_handle == Invalid) {
		throw SocketError{SocketError::System, "socket"};
	}
}

SocketAbstract::SocketAbstract(SocketAbstract &&other) noexcept
{
	m_handle = other.m_handle;

	// Invalidate other
	other.m_handle = -1;
}

SocketAbstract::~SocketAbstract()
{
	close();
}

void SocketAbstract::close()
{
	if (m_handle != Invalid) {
#if defined(_WIN32)
		::closesocket(m_handle);
#else
		::close(m_handle);
#endif
		m_handle = Invalid;
	}
}

void SocketAbstract::setBlockMode(bool block)
{
#if defined(O_NONBLOCK) && !defined(_WIN32)
	int flags;

	if ((flags = fcntl(m_handle, F_GETFL, 0)) == -1) {
		flags = 0;
	}

	if (block) {
		flags &= ~(O_NONBLOCK);
	} else {
		flags |= O_NONBLOCK;
	}

	if (fcntl(m_handle, F_SETFL, flags) == Error) {
		throw SocketError{SocketError::System, "setBlockMode"};
	}
#else
	unsigned long flags = (block) ? 0 : 1;

	if (ioctlsocket(m_handle, FIONBIO, &flags) == Error) {
		throw SocketError{SocketError::System, "setBlockMode"};
	}
#endif
}

SocketAbstract &SocketAbstract::operator=(SocketAbstract &&other) noexcept
{
	m_handle = other.m_handle;

	// Invalidate other
	other.m_handle = Invalid;

	return *this;
}

bool operator==(const SocketAbstract &s1, const SocketAbstract &s2)
{
	return s1.handle() == s2.handle();
}

bool operator!=(const SocketAbstract &s1, const SocketAbstract &s2)
{
	return s1.handle() != s2.handle();
}

bool operator<(const SocketAbstract &s1, const SocketAbstract &s2)
{
	return s1.handle() < s2.handle();
}

bool operator>(const SocketAbstract &s1, const SocketAbstract &s2)
{
	return s1.handle() > s2.handle();
}

bool operator<=(const SocketAbstract &s1, const SocketAbstract &s2)
{
	return s1.handle() <= s2.handle();
}

bool operator>=(const SocketAbstract &s1, const SocketAbstract &s2)
{
	return s1.handle() >= s2.handle();
}