+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008 Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef WIN32
+#include <sys/socket.h>
+#endif
+#include <errno.h>
+#include <msp/io/poll.h>
+#include <msp/strings/formatter.h>
+#include "streamsocket.h"
+
+namespace Msp {
+namespace Net {
+
+/**
+Constructs a new StreamSocket.
+*/
+StreamSocket::StreamSocket(Family af, int proto):
+ Socket(af, SOCK_STREAM, proto),
+ connecting(false)
+{ }
+
+/**
+Checks the status of an ongoing connection attempt. If the connection fails
+with an error, an exception is thrown.
+
+@return 0 if the connection finished, 1 if not
+*/
+int StreamSocket::poll_connect(const Time::TimeDelta &timeout)
+{
+ check_state(false);
+ if(!connecting)
+ throw InvalidState("No connection attempt going on");
+
+ int res=poll(*this, IO::P_OUTPUT, timeout);
+ if(res==-1)
+#ifdef WIN32
+ throw Exception(format("Connection polling failed: %d", WSAGetLastError()));
+#else
+ throw Exception(format("Connection polling failed: %s", strerror(errno)));
+#endif
+ else if(res>0)
+ {
+ connecting=false;
+
+ int err;
+ socklen_t len=sizeof(int);
+ get_option(SOL_SOCKET, SO_ERROR, &err, &len);
+
+ if(err!=0)
+ {
+ set_events(IO::P_NONE);
+#ifdef WIN32
+ throw Exception(format("Connection failed: %d", err));
+#else
+ throw Exception(format("Connection failed: %s", strerror(err)));
+#endif
+ }
+
+#ifdef WIN32
+ WSAEventSelect(handle, event, FD_READ|FD_CLOSE);
+#endif
+ set_events(IO::P_INPUT);
+
+ connected=true;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+Connects the socket to a remote address. In non-blocking mode, this function
+may return before the connection is finished. The caller must then use either
+the poll_connect function or an EventDispatcher to determine when the
+connection is finished.
+
+@return 0 if the connection finished, 1 if it is in progress
+*/
+int StreamSocket::connect(const SockAddr &addr)
+{
+ check_state(false);
+
+ if(connected)
+ throw InvalidState("Socket is already connected");
+
+ sockaddr sa;
+ socklen_t size=addr.fill_sockaddr(sa);
+
+#ifdef WIN32
+ int err=WSAConnect(handle, &sa, size, 0, 0, 0, 0);
+ if(err=SOCKET_ERROR)
+ {
+ int err_code=WSAGetLastError();
+ if(err_code==WSAEWOULDBLOCK)
+ {
+ connecting=true;
+ WSAEventSelect(handle, event, FD_CONNECT);
+ set_events(IO::P_OUTPUT);
+ }
+ else
+ throw Exception(format("Unable to connect: %d", err_code));
+ }
+#else
+ int err=::connect(handle, &sa, size);
+ if(err==-1)
+ {
+ if(errno==EINPROGRESS)
+ {
+ connecting=true;
+ set_events(IO::P_OUTPUT);
+ }
+ else
+ throw Exception(format("Unable to connect: %s", strerror(errno)));
+ }
+#endif
+
+ delete peer_addr;
+ peer_addr=addr.copy();
+
+ delete local_addr;
+ size=sizeof(sockaddr);
+ getsockname(handle, &sa, &size);
+ local_addr=SockAddr::create(sa);
+
+ if(err==0)
+ {
+ connected=true;
+ signal_connect_finished.emit(0);
+ }
+
+ return (err==0)?0:1;
+}
+
+/**
+Used by StreamListenSocket to construct a new socket from accept.
+*/
+StreamSocket::StreamSocket(SocketHandle h, const SockAddr &paddr):
+ Socket(h, paddr),
+ connecting(false)
+{
+#ifdef WIN32
+ WSAEventSelect(handle, event, FD_READ|FD_CLOSE);
+#endif
+ set_events(IO::P_INPUT);
+}
+
+void StreamSocket::on_event(IO::PollEvent ev)
+{
+ //cout<<"StreamSocket::on_event "<<ev<<'\n';
+ if((ev&(IO::P_OUTPUT|IO::P_ERROR)) && connecting)
+ {
+ int err;
+ socklen_t len=sizeof(err);
+ get_option(SOL_SOCKET, SO_ERROR, &err, &len);
+
+ connecting=false;
+ connected=(err==0);
+ signal_connect_finished.emit(err);
+
+ if(err!=0)
+ {
+ delete peer_addr;
+ peer_addr=0;
+ }
+
+#ifdef WIN32
+ WSAEventSelect(handle, event, FD_READ|FD_CLOSE);
+#endif
+ set_events((err==0) ? IO::P_INPUT : IO::P_NONE);
+ }
+}
+
+} // namespace Net
+} // namespace Msp