]> git.tdb.fi Git - libs/net.git/commitdiff
Initial revision
authorMikko Rasa <tdb@tdb.fi>
Mon, 28 Apr 2008 14:23:18 +0000 (14:23 +0000)
committerMikko Rasa <tdb@tdb.fi>
Mon, 28 Apr 2008 14:23:18 +0000 (14:23 +0000)
17 files changed:
Build [new file with mode: 0644]
source/constants.h [new file with mode: 0644]
source/datagramsocket.cpp [new file with mode: 0644]
source/datagramsocket.h [new file with mode: 0644]
source/inet.cpp [new file with mode: 0644]
source/inet.h [new file with mode: 0644]
source/resolve.cpp [new file with mode: 0644]
source/resolve.h [new file with mode: 0644]
source/sockaddr.cpp [new file with mode: 0644]
source/sockaddr.h [new file with mode: 0644]
source/socket.cpp [new file with mode: 0644]
source/socket.h [new file with mode: 0644]
source/streamlistensocket.cpp [new file with mode: 0644]
source/streamlistensocket.h [new file with mode: 0644]
source/streamsocket.cpp [new file with mode: 0644]
source/streamsocket.h [new file with mode: 0644]
source/types.h [new file with mode: 0644]

diff --git a/Build b/Build
new file mode 100644 (file)
index 0000000..e69e1b8
--- /dev/null
+++ b/Build
@@ -0,0 +1,18 @@
+package "mspnet"
+{
+       require "mspio";
+       if "arch=win32"
+       {
+               build_info
+               {
+                       library "ws2_32";
+               };
+       };
+
+       library "mspnet"
+       {
+               source "source";
+               install true;
+               install_headers "msp/net";
+       };
+};
diff --git a/source/constants.h b/source/constants.h
new file mode 100644 (file)
index 0000000..3a883fb
--- /dev/null
@@ -0,0 +1,31 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_CONSTANTS_H_
+#define MSP_NET_CONSTANTS_H_
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#endif
+
+namespace Msp {
+namespace Net {
+
+enum Family
+{
+       UNSPEC=AF_UNSPEC,
+       INET=AF_INET,
+       INET6=AF_INET6,
+       UNIF=AF_UNIX
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/datagramsocket.cpp b/source/datagramsocket.cpp
new file mode 100644 (file)
index 0000000..1394ffa
--- /dev/null
@@ -0,0 +1,95 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#include <errno.h>
+#include <msp/strings/formatter.h>
+#include "datagramsocket.h"
+
+namespace Msp {
+namespace Net {
+
+DatagramSocket::DatagramSocket(Family f, int p):
+       Socket(f, SOCK_DGRAM, p)
+{ }
+
+int DatagramSocket::connect(const SockAddr &addr)
+{
+       check_state(false);
+
+       sockaddr sa;
+       socklen_t size=addr.fill_sockaddr(sa);
+
+       int err=::connect(handle, &sa, size);
+       if(err==-1)
+               throw Exception(format("Unable to connect: %s", strerror(errno)));
+
+       delete peer_addr;
+       peer_addr=addr.copy();
+
+       delete local_addr;
+       size=sizeof(sockaddr);
+       getsockname(handle, &sa, &size);
+       local_addr=SockAddr::create(sa);
+
+       connected=true;
+
+       return (err==0)?0:1;
+}
+
+unsigned DatagramSocket::sendto(const char *buf, unsigned size, const SockAddr &addr_)
+{
+       check_state(false);
+
+       if(size==0)
+               return 0;
+
+       sockaddr addr;
+       socklen_t addr_len=addr_.fill_sockaddr(addr);
+
+       int ret=::sendto(handle, buf, size, 0, &addr, addr_len);
+       if(ret<0)
+       {
+               if(errno==EAGAIN)
+                       return 0;
+               else
+                       throw Exception(format("Sendto failed: %s", strerror(errno)));
+       }
+
+       return ret;
+}
+
+unsigned DatagramSocket::recvfrom(char *buf, unsigned size, SockAddr *&addr_)
+{
+       check_state(false);
+
+       if(size==0)
+               return 0;
+
+       sockaddr addr;
+       socklen_t addr_len=sizeof(sockaddr);
+
+       int ret=::recvfrom(handle, buf, size, 0, &addr, &addr_len);
+       if(ret<0)
+       {
+               if(errno==EAGAIN)
+                       return 0;
+               else
+                       throw Exception(format("Recvfrom failed: %s", strerror(errno)));
+       }
+
+       addr_=SockAddr::create(addr);
+
+       return ret;
+}
+
+IO::PollEvent DatagramSocket::get_initial_events() const
+{
+       return IO::P_INPUT;
+}
+
+} // namespace Net
+} // namespace Msp
diff --git a/source/datagramsocket.h b/source/datagramsocket.h
new file mode 100644 (file)
index 0000000..b97f0a6
--- /dev/null
@@ -0,0 +1,30 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_DATAGRAMSOCKET_H_
+#define MSP_NET_DATAGRAMSOCKET_H_
+
+#include "socket.h"
+
+namespace Msp {
+namespace Net {
+
+class DatagramSocket: public Socket
+{
+public:
+       DatagramSocket(Family, int =0);
+       int connect(const SockAddr &);
+       unsigned sendto(const char *, unsigned, const SockAddr &);
+       unsigned recvfrom(char *, unsigned, SockAddr *&);
+private:
+       IO::PollEvent get_initial_events() const;
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/inet.cpp b/source/inet.cpp
new file mode 100644 (file)
index 0000000..181c734
--- /dev/null
@@ -0,0 +1,48 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#include <msp/strings/formatter.h>
+#include "inet.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Net {
+
+InetAddr::InetAddr():
+       addr(0),
+       port(0)
+{ }
+
+InetAddr::InetAddr(sockaddr_in &sa):
+       addr(sa.sin_addr.s_addr),
+       port(sa.sin_port)
+{ }
+
+InetAddr::InetAddr(in_addr_t a, in_port_t p):
+       addr(htonl(a)),
+       port(htons(p))
+{ }
+
+string InetAddr::str() const
+{
+       const unsigned char *ptr=reinterpret_cast<const unsigned char *>(&addr);
+       return format("%d.%d.%d.%d:%d", static_cast<int>(ptr[0]), static_cast<int>(ptr[1]), static_cast<int>(ptr[2]), static_cast<int>(ptr[3]), ntohs(port));
+}
+
+unsigned InetAddr::fill_sockaddr(sockaddr &sa) const
+{
+       sockaddr_in &sai=reinterpret_cast<sockaddr_in &>(sa);
+       sai.sin_family=AF_INET;
+       sai.sin_addr.s_addr=addr;
+       sai.sin_port=port;
+
+       return sizeof(sockaddr_in);
+}
+
+} // namespace Net
+} // namespace Msp
diff --git a/source/inet.h b/source/inet.h
new file mode 100644 (file)
index 0000000..1092bc1
--- /dev/null
@@ -0,0 +1,50 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_INET_H_
+#define MSP_NET_INET_H_
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+#include <string>
+#include "sockaddr.h"
+
+namespace Msp {
+namespace Net {
+
+#ifdef WIN32
+typedef u_long in_addr_t;
+typedef u_short in_port_t;
+#endif
+
+/**
+Address class for IPv4 sockets.
+*/
+class InetAddr: public SockAddr
+{
+public:
+       InetAddr();
+       InetAddr(sockaddr_in &);
+       InetAddr(in_addr_t, in_port_t);
+       Family      get_family() const { return INET; }
+       in_addr_t   get_addr() const { return ntohl(addr); }
+       in_port_t   get_port() const { return ntohs(port); }
+       std::string str() const;
+       unsigned    fill_sockaddr(sockaddr &) const;
+       InetAddr    *copy() const { return new InetAddr(*this); }
+private:
+       in_addr_t   addr;
+       in_port_t   port;
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/resolve.cpp b/source/resolve.cpp
new file mode 100644 (file)
index 0000000..121b152
--- /dev/null
@@ -0,0 +1,66 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifdef WIN32
+#define _WIN32_WINNT 0x0501
+#include <ws2tcpip.h>
+#else
+#include <netdb.h>
+#endif
+#include <msp/strings/formatter.h>
+#include "socket.h"
+#include "resolve.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Net {
+
+SockAddr *resolve(const string &s, Family family)
+{
+       string host, serv;
+       unsigned colon=s.find(':');
+       if(colon!=string::npos)
+       {
+               host=s.substr(0, colon);
+               serv=s.substr(colon+1);
+       }
+       else
+               host=s;
+
+       addrinfo hints={0, family, 0, 0, 0, 0, 0, 0};
+       addrinfo *res;
+       const char *chost=(host.empty() ? 0 : host.c_str());
+       const char *cserv=(serv.empty() ? 0 : serv.c_str());
+       int err=getaddrinfo(chost, cserv, &hints, &res);
+       if(err==0)
+       {
+               SockAddr *addr=SockAddr::create(*res->ai_addr);
+               freeaddrinfo(res);
+               return addr;
+       }
+       else
+#ifdef WIN32
+               throw Exception(format("Can't resolve '%s': %d", host, err));
+#else
+               throw Exception(format("Can't resolve '%s': %s", host, gai_strerror(err)));
+#endif
+}
+
+               /*sockaddr sa;
+               unsigned size=fill_sockaddr(sa);
+               char hst[128];
+               char srv[128];
+               int err=getnameinfo(&sa, size, hst, 128, srv, 128, 0);
+               if(err==0)
+               {
+                       host=hst;
+                       serv=srv;
+               }*/
+
+} // namespace Net
+} // namespace Msp
diff --git a/source/resolve.h b/source/resolve.h
new file mode 100644 (file)
index 0000000..cc957ac
--- /dev/null
@@ -0,0 +1,24 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_RESOLVE_H_
+#define MSP_NET_RESOLVE_H_
+
+#include <string>
+#include "constants.h"
+
+namespace Msp {
+namespace Net {
+
+class SockAddr;
+
+SockAddr *resolve(const std::string &, Family =UNSPEC);
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/sockaddr.cpp b/source/sockaddr.cpp
new file mode 100644 (file)
index 0000000..c3ae2db
--- /dev/null
@@ -0,0 +1,26 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#include <msp/core/except.h>
+#include "inet.h"
+
+namespace Msp {
+namespace Net {
+
+SockAddr *SockAddr::create(sockaddr &sa)
+{
+       switch(sa.sa_family)
+       {
+       case AF_INET:
+               return new InetAddr(reinterpret_cast<sockaddr_in &>(sa));
+       default:
+               throw InvalidParameterValue("Unknown address family");
+       }
+}
+
+} // namespace Net
+} // namespace Msp
diff --git a/source/sockaddr.h b/source/sockaddr.h
new file mode 100644 (file)
index 0000000..63f2ab4
--- /dev/null
@@ -0,0 +1,44 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_SOCKADDR_H_
+#define MSP_NET_SOCKADDR_H_
+
+#ifndef WIN32
+#include <sys/socket.h>
+#endif
+#include "constants.h"
+
+namespace Msp {
+namespace Net {
+
+class SockAddr
+{
+public:
+       virtual Family get_family() const =0;
+       virtual std::string str() const =0;
+
+       /**
+       Fills the given struct sockaddr with information from this SockAddr.
+
+       @return  Number of bytes used
+       */
+       virtual unsigned fill_sockaddr(sockaddr &) const =0;
+
+       virtual SockAddr *copy() const =0;
+
+       virtual ~SockAddr() { }
+
+       static SockAddr *create(sockaddr &);
+protected:
+       SockAddr() { }
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/socket.cpp b/source/socket.cpp
new file mode 100644 (file)
index 0000000..decdbeb
--- /dev/null
@@ -0,0 +1,222 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef WIN32
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#endif
+#include <iostream>
+#include <msp/strings/formatter.h>
+#include <msp/time/units.h>
+#include "socket.h"
+
+namespace {
+
+#ifdef WIN32
+class WinSockHelper
+{
+public:
+       WinSockHelper()
+       {
+               WSADATA wsa_data;
+               int err=WSAStartup(0x0002, &wsa_data);
+               if(err)
+                       std::cerr<<"Failed to initialize WinSock: "<<err<<'\n';
+       }
+
+       ~WinSockHelper()
+       {
+               WSACleanup();
+       }
+};
+
+WinSockHelper wsh;
+#endif
+
+}
+
+namespace Msp {
+namespace Net {
+
+void Socket::set_block(bool b)
+{
+       mode=(mode&~IO::M_NONBLOCK);
+       if(b)
+               mode=(mode|IO::M_NONBLOCK);
+
+#ifdef WIN32
+       u_long flag=!b;
+       ioctlsocket(handle, FIONBIO, &flag);
+#else
+       int flags=fcntl(handle, F_GETFL);
+       fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
+#endif
+}
+
+IO::Handle Socket::get_event_handle()
+{
+#ifdef WIN32
+       return event;
+#else
+       return handle;
+#endif
+}
+
+
+void Socket::bind(const SockAddr &addr)
+{
+       check_state(false);
+
+       sockaddr sa;
+       unsigned size=addr.fill_sockaddr(sa);
+
+       int err=::bind(handle, &sa, size);
+       if(err==-1)
+               throw Exception(format("Unable to bind: %s", strerror(errno)));
+
+       delete local_addr;
+       local_addr=addr.copy();
+}
+
+/**
+Closes the socket.  Most operations on the socket will return an error 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();
+
+       delete local_addr;
+       local_addr=0;
+       delete peer_addr;
+       peer_addr=0;
+}
+
+const SockAddr &Socket::get_local_address() const
+{
+       if(local_addr==0)
+               throw InvalidState("Local address not set");
+       return *local_addr;
+}
+
+const SockAddr &Socket::get_peer_address() const
+{
+       if(peer_addr==0)
+               throw InvalidState("Peer address not set");
+       return *peer_addr;
+}
+
+Socket::~Socket()
+{
+       close();
+}
+
+Socket::Socket(SocketHandle h, const SockAddr &paddr):
+       handle(h),
+       connected(true),
+       local_addr(0),
+       peer_addr(paddr.copy())
+{
+       sockaddr sa;
+       socklen_t size=sizeof(sockaddr);
+       getsockname(handle, &sa, &size);
+       local_addr=SockAddr::create(sa);
+
+#ifdef WIN32
+       event=CreateEvent(0, false, false, 0);
+#endif
+}
+
+Socket::Socket(Family af, int type, int proto):
+       connected(false),
+       local_addr(0),
+       peer_addr(0)
+{
+       handle=socket(af, type, proto);
+
+#ifdef WIN32
+       event=CreateEvent(0, false, false, 0);
+#endif
+}
+
+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");
+}
+
+int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen)
+{
+#ifdef WIN32
+       return getsockopt(handle, level, optname, reinterpret_cast<char *>(optval), optlen);
+#else
+       return getsockopt(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 Exception(format("Writing to socket failed: %s", strerror(errno)));
+       }
+
+       return ret;
+}
+
+unsigned Socket::do_read(char *buf, unsigned size)
+{
+       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 Exception(format("Reading from socket failed: %s", strerror(errno)));
+       }
+       else if(ret==0 && !eof_flag)
+       {
+               eof_flag=true;
+               signal_end_of_file.emit();
+               set_events(IO::P_NONE);
+       }
+
+       return ret;
+}
+
+} // namespace Net
+} // namespace Msp
diff --git a/source/socket.h b/source/socket.h
new file mode 100644 (file)
index 0000000..f275a7e
--- /dev/null
@@ -0,0 +1,52 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_SOCKET_H_
+#define MSP_NET_SOCKET_H_
+
+#include <msp/io/base.h>
+#include "constants.h"
+#include "sockaddr.h"
+#include "types.h"
+
+namespace Msp {
+namespace Net {
+
+class Socket: public IO::Base
+{
+public:
+       void set_block(bool);
+       IO::Handle get_event_handle();
+
+       bool get_connected() const { return connected; }
+       void bind(const SockAddr &);
+       virtual int connect(const SockAddr &) =0;
+       void close();
+       const SockAddr &get_local_address() const;
+       const SockAddr &get_peer_address() const;
+       ~Socket();
+protected:
+       SocketHandle handle;
+#ifdef WIN32
+       IO::Handle event;
+#endif
+       bool       connected;
+       SockAddr   *local_addr;
+       SockAddr   *peer_addr;
+
+       Socket(SocketHandle, const SockAddr &);
+       Socket(Family, int, int);
+       void check_state(bool) const;
+       int get_option(int, int, void *, socklen_t *);
+       unsigned do_write(const char *, unsigned);
+       unsigned do_read(char *, unsigned);
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/streamlistensocket.cpp b/source/streamlistensocket.cpp
new file mode 100644 (file)
index 0000000..df80b00
--- /dev/null
@@ -0,0 +1,57 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#include <errno.h>
+#include <msp/core/refptr.h>
+#include <msp/strings/formatter.h>
+#include "streamlistensocket.h"
+#include "streamsocket.h"
+
+namespace Msp {
+namespace Net {
+
+StreamListenSocket::StreamListenSocket(Family af, int proto):
+       Socket(af, SOCK_STREAM, proto),
+       listening(false)
+{ }
+
+int StreamListenSocket::connect(const SockAddr &)
+{
+       throw Exception("Can't connect a listen socket");
+}
+
+void StreamListenSocket::listen(const SockAddr &addr, unsigned backlog)
+{
+       bind(addr);
+
+       int err=::listen(handle, backlog);
+       if(err==-1)
+               throw Exception(format("Unable to listen: %s", strerror(errno)));
+
+#ifdef WIN32
+       WSAEventSelect(handle, event, FD_ACCEPT);
+#endif
+       set_events(IO::P_INPUT);
+
+       listening=true;
+}
+
+StreamSocket *StreamListenSocket::accept()
+{
+       if(!listening)
+               throw InvalidState("Socket is not listening");
+
+       sockaddr sa;
+       socklen_t size=sizeof(sockaddr);
+       SocketHandle new_h=::accept(handle, &sa, &size);
+
+       RefPtr<SockAddr> paddr=SockAddr::create(sa);
+       return new StreamSocket(new_h, *paddr);
+}
+
+} // namespace Net
+} // namespace Msp
diff --git a/source/streamlistensocket.h b/source/streamlistensocket.h
new file mode 100644 (file)
index 0000000..054f2fb
--- /dev/null
@@ -0,0 +1,32 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_STREAMLISTENSOCKET_H_
+#define MSP_NET_STREAMLISTENSOCKET_H_
+
+#include "socket.h"
+
+namespace Msp {
+namespace Net {
+
+class StreamSocket;
+
+class StreamListenSocket: public Socket
+{
+public:
+       StreamListenSocket(Family, int =0);
+       int connect(const SockAddr &);
+       void listen(const SockAddr &, unsigned =4);
+       StreamSocket *accept();
+private:
+       bool listening;
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/streamsocket.cpp b/source/streamsocket.cpp
new file mode 100644 (file)
index 0000000..cf1a7b5
--- /dev/null
@@ -0,0 +1,180 @@
+/* $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
diff --git a/source/streamsocket.h b/source/streamsocket.h
new file mode 100644 (file)
index 0000000..0bba5c3
--- /dev/null
@@ -0,0 +1,40 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_STREAMSOCKET_H_
+#define MSP_NET_STREAMSOCKET_H_
+
+#include "socket.h"
+
+namespace Msp {
+namespace Net {
+
+class StreamSocket: public Socket
+{
+       friend class StreamListenSocket;
+public:
+       /**
+       Emitted when the socket finishes connecting.  The argument is a standard
+       error code, 0 indicating success.
+       */
+       sigc::signal<void, int> signal_connect_finished;
+
+       StreamSocket(Family, int =0);
+       bool get_connecting() const { return connecting; }
+       int poll_connect(const Time::TimeDelta &);
+       int connect(const SockAddr &);
+private:
+       bool connecting;
+
+       StreamSocket(SocketHandle, const SockAddr &);
+       void on_event(IO::PollEvent);
+};
+
+} // namespace Net
+} // namespace Msp
+
+#endif
diff --git a/source/types.h b/source/types.h
new file mode 100644 (file)
index 0000000..f3ca3ab
--- /dev/null
@@ -0,0 +1,28 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2008  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_TYPES_H_
+#define MSP_NET_TYPES_H_
+
+namespace Msp {
+namespace Net {
+
+#ifdef WIN32
+typedef SOCKET SocketHandle;
+typedef int socklen_t;
+
+#define MSP_NET_INVALID_SOCKET_HANDLE static_cast<SOCKET>(-1)
+#else
+typedef IO::Handle SocketHandle;
+
+#define MSP_NET_INVALID_SOCKET_HANDLE MSP_IO_INVALID_HANDLE
+#endif
+
+} // namespace Net
+} // namespace Msp
+
+#endif