-/* $Id$
-
-This file is part of libmspnet
-Copyright © 2008 Mikkosoft Productions, Mikko Rasa
-Distributed under the LGPL
-*/
-
#ifndef WIN32
-#include <errno.h>
+#include <cerrno>
#include <fcntl.h>
#include <sys/socket.h>
#endif
#include <iostream>
-#include <msp/strings/formatter.h>
+#include <msp/core/systemerror.h>
+#include <msp/io/handle_private.h>
+#include <msp/strings/format.h>
+#include <msp/time/rawtime_private.h>
#include <msp/time/units.h>
+#include "sockaddr_private.h"
#include "socket.h"
+#include "socket_private.h"
namespace {
WinSockHelper()
{
WSADATA wsa_data;
- int err=WSAStartup(0x0002, &wsa_data);
+ int err = WSAStartup(0x0002, &wsa_data);
if(err)
std::cerr<<"Failed to initialize WinSock: "<<err<<'\n';
}
namespace Msp {
namespace Net {
-Socket::Socket(SocketHandle h, const SockAddr &paddr):
- handle(h),
- connected(true),
- local_addr(0),
- peer_addr(paddr.copy())
+Socket::Socket(const Private &p):
+ priv(new Private),
+ local_addr(0)
{
- sockaddr sa;
- socklen_t size=sizeof(sockaddr);
- getsockname(handle, &sa, &size);
- local_addr=SockAddr::create(sa);
+ priv->handle = p.handle;
+
+ SockAddr::SysAddr sa;
+ getsockname(priv->handle, reinterpret_cast<sockaddr *>(&sa.addr), &sa.size);
+ local_addr = SockAddr::new_from_sys(sa);
#ifdef WIN32
- event=CreateEvent(0, false, false, 0);
+ *priv->event = CreateEvent(0, false, false, 0);
+#else
+ *priv->event = priv->handle;
#endif
}
Socket::Socket(Family af, int type, int proto):
- connected(false),
- local_addr(0),
- peer_addr(0)
+ priv(new Private),
+ local_addr(0)
{
- handle=socket(af, type, proto);
+ priv->handle = socket(family_to_sys(af), type, proto);
#ifdef WIN32
- event=CreateEvent(0, false, false, 0);
+ *priv->event = CreateEvent(0, false, false, 0);
+#else
+ *priv->event = priv->handle;
#endif
}
Socket::~Socket()
{
- close();
+#ifdef WIN32
+ closesocket(priv->handle);
+ CloseHandle(*priv->event);
+#else
+ ::close(priv->handle);
+#endif
+
+ delete local_addr;
+ delete priv;
}
void Socket::set_block(bool b)
{
- mode=(mode&~IO::M_NONBLOCK);
+ mode = (mode&~IO::M_NONBLOCK);
if(b)
- mode=(mode|IO::M_NONBLOCK);
+ mode = (mode|IO::M_NONBLOCK);
#ifdef WIN32
- u_long flag=!b;
- ioctlsocket(handle, FIONBIO, &flag);
+ u_long flag = !b;
+ ioctlsocket(priv->handle, FIONBIO, &flag);
#else
- int flags=fcntl(handle, F_GETFL);
- fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
+ int flags = fcntl(priv->handle, F_GETFL);
+ fcntl(priv->handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
#endif
}
-IO::Handle Socket::get_event_handle()
+const IO::Handle &Socket::get_event_handle()
{
-#ifdef WIN32
- return event;
-#else
- return handle;
-#endif
+ return priv->event;
}
void Socket::bind(const SockAddr &addr)
{
- check_state(false);
-
- sockaddr sa;
- unsigned size=addr.fill_sockaddr(sa);
+ SockAddr::SysAddr sa = addr.to_sys();
- int err=::bind(handle, &sa, size);
+ int err = ::bind(priv->handle, reinterpret_cast<sockaddr *>(&sa.addr), sa.size);
if(err==-1)
- throw SystemError("Unable to bind", errno);
-
- delete local_addr;
- local_addr=addr.copy();
-}
-
-/**
-Closes the socket. Most operations on the socket will throw an exception after
-this.
-*/
-void Socket::close()
-{
- if(handle==MSP_NET_INVALID_SOCKET_HANDLE)
- return;
-
- set_events(IO::P_NONE);
-
- signal_flush_required.emit();
-#ifdef WIN32
- closesocket(handle);
- CloseHandle(event);
-#else
- ::close(handle);
-#endif
- handle=MSP_NET_INVALID_SOCKET_HANDLE;
- signal_closed.emit();
+ throw system_error("bind");
delete local_addr;
- local_addr=0;
- delete peer_addr;
- peer_addr=0;
+ local_addr = addr.copy();
}
const SockAddr &Socket::get_local_address() const
{
if(local_addr==0)
- throw InvalidState("Local address not set");
+ throw bad_socket_state("not bound");
return *local_addr;
}
-const SockAddr &Socket::get_peer_address() const
+void Socket::set_timeout(const Time::TimeDelta &timeout)
{
- if(peer_addr==0)
- throw InvalidState("Peer address not set");
- return *peer_addr;
-}
-
-void Socket::check_state(bool conn) const
-{
- if(handle==MSP_NET_INVALID_SOCKET_HANDLE)
- throw Exception("Socket is closed");
- if(conn && !connected)
- throw Exception("Socket is not connected");
+#ifndef WIN32
+ timeval tv = Time::rawtime_to_timeval(timeout.raw());
+ set_option(SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));
+ set_option(SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval));
+#else
+ DWORD msecs = static_cast<DWORD>(timeout/Time::msec);
+ set_option(SOL_SOCKET, SO_RCVTIMEO, &msecs, sizeof(DWORD));
+ set_option(SOL_SOCKET, SO_SNDTIMEO, &msecs, sizeof(DWORD));
+#endif
}
-int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen)
+int Socket::set_option(int level, int optname, const void *optval, socklen_t optlen)
{
#ifdef WIN32
- return getsockopt(handle, level, optname, reinterpret_cast<char *>(optval), optlen);
+ return setsockopt(priv->handle, level, optname, reinterpret_cast<const char *>(optval), optlen);
#else
- return getsockopt(handle, level, optname, optval, optlen);
+ return setsockopt(priv->handle, level, optname, optval, optlen);
#endif
}
-unsigned Socket::do_write(const char *buf, unsigned size)
-{
- check_state(true);
-
- if(size==0)
- return 0;
-
- int ret=::send(handle, buf, size, 0);
- if(ret<0)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Writing to socket failed", errno);
- }
-
- return ret;
-}
-
-unsigned Socket::do_read(char *buf, unsigned size)
+int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen) const
{
- check_state(true);
-
- if(size==0)
- return 0;
-
- int ret=::recv(handle, buf, size, 0);
- if(ret<0)
- {
- if(errno==EAGAIN)
- return 0;
- else
- throw SystemError("Reading from socket failed", errno);
- }
- else if(ret==0 && !eof_flag)
- {
- eof_flag=true;
- signal_end_of_file.emit();
- set_events(IO::P_NONE);
- }
-
- return ret;
+#ifdef WIN32
+ return getsockopt(priv->handle, level, optname, reinterpret_cast<char *>(optval), optlen);
+#else
+ return getsockopt(priv->handle, level, optname, optval, optlen);
+#endif
}
} // namespace Net