From f59eded7c3e162bbdfc6db424c9badc730017698 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 28 Apr 2008 14:23:18 +0000 Subject: [PATCH] Initial revision --- Build | 18 +++ source/constants.h | 31 +++++ source/datagramsocket.cpp | 95 +++++++++++++++ source/datagramsocket.h | 30 +++++ source/inet.cpp | 48 ++++++++ source/inet.h | 50 ++++++++ source/resolve.cpp | 66 ++++++++++ source/resolve.h | 24 ++++ source/sockaddr.cpp | 26 ++++ source/sockaddr.h | 44 +++++++ source/socket.cpp | 222 ++++++++++++++++++++++++++++++++++ source/socket.h | 52 ++++++++ source/streamlistensocket.cpp | 57 +++++++++ source/streamlistensocket.h | 32 +++++ source/streamsocket.cpp | 180 +++++++++++++++++++++++++++ source/streamsocket.h | 40 ++++++ source/types.h | 28 +++++ 17 files changed, 1043 insertions(+) create mode 100644 Build create mode 100644 source/constants.h create mode 100644 source/datagramsocket.cpp create mode 100644 source/datagramsocket.h create mode 100644 source/inet.cpp create mode 100644 source/inet.h create mode 100644 source/resolve.cpp create mode 100644 source/resolve.h create mode 100644 source/sockaddr.cpp create mode 100644 source/sockaddr.h create mode 100644 source/socket.cpp create mode 100644 source/socket.h create mode 100644 source/streamlistensocket.cpp create mode 100644 source/streamlistensocket.h create mode 100644 source/streamsocket.cpp create mode 100644 source/streamsocket.h create mode 100644 source/types.h diff --git a/Build b/Build new file mode 100644 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 index 0000000..3a883fb --- /dev/null +++ b/source/constants.h @@ -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 +#else +#include +#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 index 0000000..1394ffa --- /dev/null +++ b/source/datagramsocket.cpp @@ -0,0 +1,95 @@ +/* $Id$ + +This file is part of libmspnet +Copyright © 2008 Mikkosoft Productions, Mikko Rasa +Distributed under the LGPL +*/ + +#include +#include +#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 index 0000000..b97f0a6 --- /dev/null +++ b/source/datagramsocket.h @@ -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 index 0000000..181c734 --- /dev/null +++ b/source/inet.cpp @@ -0,0 +1,48 @@ +/* $Id$ + +This file is part of libmspnet +Copyright © 2008 Mikkosoft Productions, Mikko Rasa +Distributed under the LGPL +*/ + +#include +#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(&addr); + return format("%d.%d.%d.%d:%d", static_cast(ptr[0]), static_cast(ptr[1]), static_cast(ptr[2]), static_cast(ptr[3]), ntohs(port)); +} + +unsigned InetAddr::fill_sockaddr(sockaddr &sa) const +{ + sockaddr_in &sai=reinterpret_cast(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 index 0000000..1092bc1 --- /dev/null +++ b/source/inet.h @@ -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 +#else +#include +#endif +#include +#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 index 0000000..121b152 --- /dev/null +++ b/source/resolve.cpp @@ -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 +#else +#include +#endif +#include +#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 index 0000000..cc957ac --- /dev/null +++ b/source/resolve.h @@ -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 +#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 index 0000000..c3ae2db --- /dev/null +++ b/source/sockaddr.cpp @@ -0,0 +1,26 @@ +/* $Id$ + +This file is part of libmspnet +Copyright © 2008 Mikkosoft Productions, Mikko Rasa +Distributed under the LGPL +*/ + +#include +#include "inet.h" + +namespace Msp { +namespace Net { + +SockAddr *SockAddr::create(sockaddr &sa) +{ + switch(sa.sa_family) + { + case AF_INET: + return new InetAddr(reinterpret_cast(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 index 0000000..63f2ab4 --- /dev/null +++ b/source/sockaddr.h @@ -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 +#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 index 0000000..decdbeb --- /dev/null +++ b/source/socket.cpp @@ -0,0 +1,222 @@ +/* $Id$ + +This file is part of libmspnet +Copyright © 2008 Mikkosoft Productions, Mikko Rasa +Distributed under the LGPL +*/ + +#ifndef WIN32 +#include +#include +#include +#endif +#include +#include +#include +#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: "<(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 index 0000000..f275a7e --- /dev/null +++ b/source/socket.h @@ -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 +#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 index 0000000..df80b00 --- /dev/null +++ b/source/streamlistensocket.cpp @@ -0,0 +1,57 @@ +/* $Id$ + +This file is part of libmspnet +Copyright © 2008 Mikkosoft Productions, Mikko Rasa +Distributed under the LGPL +*/ + +#include +#include +#include +#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 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 index 0000000..054f2fb --- /dev/null +++ b/source/streamlistensocket.h @@ -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 index 0000000..cf1a7b5 --- /dev/null +++ b/source/streamsocket.cpp @@ -0,0 +1,180 @@ +/* $Id$ + +This file is part of libmspnet +Copyright © 2008 Mikkosoft Productions, Mikko Rasa +Distributed under the LGPL +*/ + +#ifndef WIN32 +#include +#endif +#include +#include +#include +#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 "< 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 index 0000000..f3ca3ab --- /dev/null +++ b/source/types.h @@ -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(-1) +#else +typedef IO::Handle SocketHandle; + +#define MSP_NET_INVALID_SOCKET_HANDLE MSP_IO_INVALID_HANDLE +#endif + +} // namespace Net +} // namespace Msp + +#endif -- 2.43.0