Mercurial > code
diff C++/modules/Socket/SocketTcp.cpp @ 334:0b576ee64d45
* Create brand new hierarchy
* Rename DynLib to Dynlib
* Remove some warnings
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 08 Mar 2015 14:26:33 +0100 |
parents | C++/SocketTcp.cpp@cba77da58496 |
children | 486767e1d165 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketTcp.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,227 @@ +/* + * SocketTcp.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 "SocketAddress.h" +#include "SocketListener.h" +#include "SocketTcp.h" + +/* -------------------------------------------------------- + * SocketAbstractTcp + * -------------------------------------------------------- */ + +void SocketAbstractTcp::listen(int max) +{ + if (::listen(m_handle, max) == Error) + throw SocketError(SocketError::System, "listen"); +} + +Socket SocketAbstractTcp::standardAccept(SocketAddress &info) +{ + Socket::Handle handle; + + // Store the information + sockaddr_storage address; + socklen_t addrlen; + + addrlen = sizeof (sockaddr_storage); + handle = ::accept(m_handle, reinterpret_cast<sockaddr *>(&address), &addrlen); + + if (handle == Invalid) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "accept", error); + + throw SocketError(SocketError::System, "accept", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "accept"); + + throw SocketError(SocketError::System, "accept"); +#endif + } + + info = SocketAddress(address, addrlen); + + return Socket(handle); +} + +void SocketAbstractTcp::standardConnect(const SocketAddress &address) +{ + if (m_state == SocketState::Connected) + return; + + auto &sa = address.address(); + auto addrlen = address.length(); + + if (::connect(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) { + /* + * Determine if the error comes from a non-blocking connect that cannot be + * accomplished yet. + */ +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "connect", error); + + throw SocketError(SocketError::System, "connect", error); +#else + if (errno == EINPROGRESS) + throw SocketError(SocketError::WouldBlockWrite, "connect"); + + throw SocketError(SocketError::System, "connect"); +#endif + } + + m_state = SocketState::Connected; +} + +/* -------------------------------------------------------- + * SocketTcp + * -------------------------------------------------------- */ + +SocketTcp SocketTcp::accept() +{ + SocketAddress dummy; + + return accept(dummy); +} + +SocketTcp SocketTcp::accept(SocketAddress &info) +{ + return standardAccept(info); +} + +void SocketTcp::connect(const SocketAddress &address) +{ + return standardConnect(address); +} + +void SocketTcp::waitConnect(const SocketAddress &address, int timeout) +{ + if (m_state == SocketState::Connected) + return; + + // Initial try + try { + connect(address); + } catch (const SocketError &ex) { + if (ex.code() == SocketError::WouldBlockWrite) { + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + // Socket is writable? Check if there is an error + + int error = get<int>(SOL_SOCKET, SO_ERROR); + + if (error) { + throw SocketError(SocketError::System, "connect", error); + } + } else { + throw; + } + } + + m_state = SocketState::Connected; +} + +SocketTcp SocketTcp::waitAccept(int timeout) +{ + SocketAddress dummy; + + return waitAccept(dummy, timeout); +} + +SocketTcp SocketTcp::waitAccept(SocketAddress &info, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return accept(info); +} + +unsigned SocketTcp::recv(void *data, unsigned dataLen) +{ + int nbread; + + nbread = ::recv(m_handle, (Socket::Arg)data, dataLen, 0); + if (nbread == Error) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "recv", error) + + throw SocketError(SocketError::System, "recv", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "recv"); + + throw SocketError(SocketError::System, "recv"); +#endif + } else if (nbread == 0) + m_state = SocketState::Closed; + + return (unsigned)nbread; +} + +unsigned SocketTcp::waitRecv(void *data, unsigned length, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return recv(data, length); +} + +unsigned SocketTcp::send(const void *data, unsigned length) +{ + int nbsent; + + nbsent = ::send(m_handle, (Socket::ConstArg)data, length, 0); + if (nbsent == Error) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "send", error); + + throw SocketError(SocketError::System, "send", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "send"); + + throw SocketError(SocketError::System, "send"); +#endif + } + + return (unsigned)nbsent; +} + +unsigned SocketTcp::waitSend(const void *data, unsigned length, int timeout) +{ + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + return send(data, length); +}