diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/MsgPack.cpp b/MsgPack.cpp index 5cc3810..3a2fa51 100755 --- a/MsgPack.cpp +++ b/MsgPack.cpp @@ -15,6 +15,12 @@ #include "include/MsgPack.h" +#ifdef WIN32 +#define __builtin_bswap16 _byteswap_ushort +#define __builtin_bswap32 _byteswap_ulong +#define __builtin_bswap64 _byteswap_uint64 +#endif + void storeUint8(uint8_t* target, uint8_t source) { *reinterpret_cast(target) = source; } @@ -195,8 +201,8 @@ namespace MsgPack { case Type::NIL: stream << "null"; return; - case Type::ERROR: - stream << "error"; + case Type::UNDEFINED: + stream << "undefined"; break; case Type::BOOL_FALSE: stream << "false"; @@ -957,7 +963,7 @@ namespace MsgPack { else switch(nextByte) { case Type::NIL: - case Type::ERROR: + case Type::UNDEFINED: case Type::BOOL_FALSE: case Type::BOOL_TRUE: element = new Primitive(); diff --git a/README.md b/README.md index cdcfec4..ace37ef 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Features: * Enable/Disable blocking mode * Join/Leave UDP-Multicast groups * UDP-IPv4-Broadcast -* Operating Systems: Mac OS, Linux +* Operating Systems: Mac OS, Linux, Windows * Socket can be used as std::streambuf * SocketManager calls events for connecting, disconnecting, receiving data and write buffer overflow * MsgPack support: http://msgpack.org diff --git a/Socket.cpp b/Socket.cpp index bb14ba5..e8835c9 100755 --- a/Socket.cpp +++ b/Socket.cpp @@ -1,517 +1,556 @@ -/* - netLink: c++ 11 networking library - Copyright 2013 Alexander Meißner (lichtso@gamefortec.net) - - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - - -#include "include/Socket.h" - -namespace netLink { - -static void readSockaddr(const struct sockaddr* addr, std::string& host, unsigned int& port) { - char buffer[INET6_ADDRSTRLEN]; - if(addr->sa_family == AF_INET) { - auto sin = reinterpret_cast(addr); - port = ntohs(sin->sin_port); - inet_ntop(addr->sa_family, &(sin->sin_addr), buffer, sizeof(buffer)); - }else{ - auto sin = reinterpret_cast(addr); - port = ntohs(sin->sin6_port); - inet_ntop(addr->sa_family, &(sin->sin6_addr), buffer, sizeof(buffer)); - } - host = buffer; -} - -Socket::AddrinfoContainer Socket::getSocketInfoFor(const char* host, unsigned int port, bool wildcardAddress) { - struct addrinfo conf, *res; - memset(&conf, 0, sizeof(conf)); - conf.ai_flags = AI_V4MAPPED; - conf.ai_family = AF_UNSPEC; - - if(wildcardAddress) - conf.ai_flags |= AI_PASSIVE; - - switch(type) { - case TCP_CLIENT: - case TCP_SERVER: - conf.ai_socktype = SOCK_STREAM; - break; - case UDP_PEER: - conf.ai_socktype = SOCK_DGRAM; - break; - default: - throw Exception(Exception::BAD_PROTOCOL); - } - - char portStr[10]; - snprintf(portStr, 10, "%u", port); - - int result = getaddrinfo(host, portStr, &conf, &res); - if(result != 0) - throw Exception(Exception::ERROR_RESOLVING_ADDRESS); - - return AddrinfoContainer(res); -} - -Socket::pos_type Socket::seekoff(Socket::off_type off, std::ios_base::seekdir way, std::ios_base::openmode which) { - switch(way) { - case std::ios_base::beg: - if(off < 0) return EOF; - - if(which & std::ios_base::in) { - if(eback()+off >= egptr()) return EOF; - setg(eback(), eback()+off, egptr()); - } - - if(which & std::ios_base::out) { - if(pbase()+off >= epptr()) return EOF; - setp(pbase(), epptr()); - pbump(off); - } - return off; - case std::ios_base::cur: - if(which == std::ios_base::in) { - if(eback()+off >= egptr() || egptr()+off < eback()) return EOF; - setg(eback(), gptr()+off, egptr()); - return gptr()-eback(); - }else if(which == std::ios_base::out) { - if(pbase()+off >= epptr() || epptr()+off < pbase()) return EOF; - pbump(off); - return pptr()-pbase(); - }else - return EOF; - case std::ios_base::end: - if(off > 0) return EOF; - - if(which == std::ios_base::in) { - if(egptr()+off < eback()) return EOF; - setg(eback(), egptr()+off, egptr()); - return gptr()-eback(); - }else if(which == std::ios_base::out) { - if(epptr()+off < pbase()) return EOF; - setp(pbase(), epptr()); - pbump(off); - return pptr()-pbase(); - }else - return EOF; - } -} - -Socket::pos_type Socket::seekpos(Socket::pos_type sp, std::ios_base::openmode which) { - return Socket::seekoff(sp, std::ios_base::beg, which); -} - -Socket::int_type Socket::sync() { - if(getOutputBufferSize() <= 0) //No output buffer - return EOF; - - if(pptr() == pbase()) //Allready in sync - return 0; - - try { - send(pbase(), pptr()-pbase()); - setp(pbase(), epptr()); - return 0; - } catch(Exception err) { - return EOF; - } -} - -std::streamsize Socket::xsgetn(char_type* buffer, std::streamsize size) { - if(inputIntermediateSize > 0) //Read from input buffer - return super::xsgetn(buffer, size); - - try { - return receive(buffer, size); - } catch(Exception err) { - return 0; - } -} - -Socket::int_type Socket::underflow() { - if(type == UDP_PEER || advanceInputBuffer() <= 0) - return EOF; - return *eback(); -} - -std::streamsize Socket::xsputn(const char_type* buffer, std::streamsize size) { - if(getOutputBufferSize()) //Write into buffer - return super::xsputn(buffer, size); - - try { - return send(buffer, size); - } catch(Exception err) { - return 0; - } -} - -Socket::int_type Socket::overflow(int_type c) { - if(sync() == EOF) - return EOF; - *pbase() = c; - pbump(1); - return c; -} - - - -void Socket::initSocket(bool blockingConnect) { - AddrinfoContainer info; - - if(type == TCP_CLIENT) - info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); - else{ - const char* host; - if(!hostLocal.compare("") || !hostLocal.compare("*")) - host = NULL; - else - host = hostLocal.c_str(); - info = getSocketInfoFor(host, portLocal, true); - } - - struct addrinfo* nextAddr = info.get(); - while(nextAddr) { - handle = socket(nextAddr->ai_family, nextAddr->ai_socktype, nextAddr->ai_protocol); - if(handle == -1) { - nextAddr = nextAddr->ai_next; - continue; - } - - setBlockingMode(blockingConnect); - int flag = 1; - if(setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) - throw Exception(Exception::ERROR_SET_SOCK_OPT); - - switch(nextAddr->ai_family) { - case AF_INET: - ipVersion = IPv4; - break; - case AF_INET6: - ipVersion = IPv6; - break; - } - - switch(type) { - case NONE: - case TCP_SERVERS_CLIENT: - throw Exception(Exception::BAD_TYPE); - case TCP_CLIENT: - if(connect(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1 && blockingConnect) { - close(handle); - handle = -1; - }else if(blockingConnect) - status = READY; - else - status = NOT_CONNECTED; - break; - case TCP_SERVER: { - if(bind(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1) { - close(handle); - handle = -1; - } - - if(listen(handle, status) == -1) - throw Exception(Exception::ERROR_INIT); - } break; - case UDP_PEER: { - if(bind(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1) { - close(handle); - handle = -1; - } - - status = READY; - } break; - } - if(handle == -1) { - nextAddr = nextAddr->ai_next; - continue; - } - - if(blockingConnect) - setBlockingMode(false); - break; - } - - if(handle == -1) - throw Exception(Exception::ERROR_INIT); - - unsigned int size = sizeof(nextAddr->ai_addr); - if(getsockname(handle, nextAddr->ai_addr, &size) != 0) - throw Exception(Exception::ERROR_GET_SOCK_NAME); - - readSockaddr(nextAddr->ai_addr, hostLocal, portLocal); -} - -void Socket::setMulticastGroup(const struct sockaddr* addr, bool join) { - if(ipVersion == IPv4) { - auto sin = reinterpret_cast(addr)->sin_addr; - if((ntohl(sin.s_addr) & 0xF0000000) == 0xE0000000) { - struct ip_mreq mreq; - mreq.imr_multiaddr = sin; - mreq.imr_interface.s_addr = 0; - - if(setsockopt(handle, IPPROTO_IP, (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) - throw Exception(Exception::ERROR_SET_SOCK_OPT); - } - }else{ - auto sin = reinterpret_cast(addr)->sin6_addr; - if(sin.s6_addr[0] == 0xFF) { - struct ipv6_mreq mreq; - mreq.ipv6mr_multiaddr = sin; - mreq.ipv6mr_interface = 0; - - if(setsockopt(handle, IPPROTO_IPV6, (join) ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, &mreq, sizeof(ipv6_mreq)) == -1) - throw Exception(Exception::ERROR_SET_SOCK_OPT); - } - } -} - -Socket::Socket(int _handle, const std::string& _hostLocal, unsigned _portLocal, - struct sockaddr* remoteAddr, IPVersion _ipVersion) - :ipVersion(_ipVersion), type(TCP_SERVERS_CLIENT), status(READY), - handle(_handle), hostLocal(_hostLocal), portLocal(_portLocal) { - readSockaddr(remoteAddr, hostRemote, portRemote); - setBlockingMode(false); -} - -void Socket::initAsTcpClient(const std::string& _hostRemote, unsigned _portRemote, bool waitUntilConnected) { - type = TCP_CLIENT; - hostRemote = _hostRemote; - portRemote = _portRemote; - initSocket(waitUntilConnected); -} - -void Socket::initAsTcpServer(const std::string& _hostLocal, unsigned _portLocal, unsigned _listenQueue) { - type = TCP_SERVER; - hostLocal = _hostLocal; - portLocal = _portLocal; - status = _listenQueue; - initSocket(false); -} - -void Socket::initAsUdpPeer(const std::string& _hostLocal, unsigned _portLocal) { - type = UDP_PEER; - hostLocal = _hostLocal; - portLocal = _portLocal; - initSocket(false); -} - -Socket::~Socket() { - disconnect(); -} - -SocketType Socket::getType() const { - return type; -} - -IPVersion Socket::getIPVersion() const { - return ipVersion; -} - -SocketStatus Socket::getStatus() const { - if(type == TCP_SERVER) - return (status == NOT_INITIALIZED) ? NOT_INITIALIZED : LISTENING; - else - return (SocketStatus)status; -} - -std::streamsize Socket::showmanyc() { - int result = -1; - if(ioctl(handle, FIONREAD, &result)) - throw Exception(Exception::ERROR_IOCTL); - else - return result; -} - -std::streamsize Socket::advanceInputBuffer() { - if(inputIntermediateSize == 0) //No input buffer - return 0; - - std::streamsize inAvail; - if(type == UDP_PEER) - inAvail = 0; - else{ - inAvail = egptr()-gptr(); - memmove(eback(), gptr(), inAvail); - } - - try { - inAvail += receive(eback()+inAvail, inputIntermediateSize-inAvail); - } catch(Exception err) { - - } - - setg(eback(), eback(), eback()+inAvail); - return inAvail; -} - -std::streamsize Socket::receive(char_type* buffer, std::streamsize size) { - size = std::min(size, showmanyc()); - if(size == 0) return 0; - - switch(type) { - case UDP_PEER: { - struct sockaddr remoteAddr; - unsigned int addrSize = sizeof(remoteAddr); - int result = recvfrom(handle, buffer, size, 0, &remoteAddr, &addrSize); - - if(result == -1) { - portRemote = 0; - hostRemote = ""; - throw Exception(Exception::ERROR_READ); - }else - readSockaddr(&remoteAddr, hostRemote, portRemote); - - return result; - } - case TCP_CLIENT: - case TCP_SERVERS_CLIENT: { - int result = recv(handle, buffer, size, 0); - if(result == -1) - throw Exception(Exception::ERROR_READ); - - return result; - } - case NONE: - case TCP_SERVER: - throw Exception(Exception::BAD_TYPE); - } -} - -std::streamsize Socket::send(const char_type* buffer, std::streamsize size) { - if(size == 0) return 0; - - switch(type) { - case UDP_PEER: { - AddrinfoContainer info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); - - size_t sentBytes = 0; - while(sentBytes < size) { - int result = ::sendto(handle, (const char*)buffer + sentBytes, size - sentBytes, 0, info->ai_addr, info->ai_addrlen); - - if(result == -1) - throw Exception(Exception::ERROR_SEND); - - sentBytes += result; - } - - return sentBytes; - } - case TCP_CLIENT: - case TCP_SERVERS_CLIENT: { - size_t sentBytes = 0; - while(sentBytes < size) { - int result = ::send(handle, (const char*)buffer + sentBytes, size - sentBytes, 0); - - if(result == -1) - throw Exception(Exception::ERROR_SEND); - - sentBytes += result; - } - return sentBytes; - } - case NONE: - case TCP_SERVER: - throw Exception(Exception::BAD_TYPE); - } -} - -std::streamsize Socket::getInputBufferSize() { - return inputIntermediateSize; -} - -std::streamsize Socket::getOutputBufferSize() { - return epptr()-pbase(); -} - -void Socket::setInputBufferSize(std::streamsize n) { - if(eback()) delete [] eback(); - if(n == 0) return; - if(type == TCP_SERVER) - throw Exception(Exception::BAD_TYPE); - - char_type* readBuffer = new char_type[n]; - setg(readBuffer, readBuffer, readBuffer); - inputIntermediateSize = n; -} - -void Socket::setOutputBufferSize(std::streamsize n) { - if(pbase()) delete [] pbase(); - if(n == 0) return; - if(type == TCP_SERVER) - throw Exception(Exception::BAD_TYPE); - - char_type* writeBuffer = new char_type[n]; - setp(writeBuffer, writeBuffer+n); -} - -void Socket::setBlockingMode(bool blocking) { - int flags = fcntl(handle, F_GETFL); - if(blocking) - flags &= ~O_NONBLOCK; - else - flags |= O_NONBLOCK; - if(fcntl(handle, F_SETFL, flags) == -1) - throw Exception(Exception::ERROR_IOCTL); -} - -void Socket::setBroadcast(bool active) { - if(type != UDP_PEER || ipVersion != IPv4) - throw Exception(Exception::BAD_PROTOCOL); - - int flag = 1; - if(setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) == -1) - throw Exception(Exception::ERROR_SET_SOCK_OPT); -} - -void Socket::setMulticastGroup(const std::string& address, bool join) { - if(type != UDP_PEER) - throw Exception(Exception::BAD_PROTOCOL); - - struct sockaddr_storage addr; - if(ipVersion == IPv4) { - auto sin = reinterpret_cast(&addr); - if(inet_pton(AF_INET, address.c_str(), &sin->sin_addr) == -1) - throw Exception(Exception::ERROR_RESOLVING_ADDRESS); - }else{ - auto sin = reinterpret_cast(&addr); - if(inet_pton(AF_INET6, address.c_str(), &sin->sin6_addr) == -1) - throw Exception(Exception::ERROR_RESOLVING_ADDRESS); - } - - setMulticastGroup(reinterpret_cast(&addr), join); -} - -std::unique_ptr Socket::accept() { - if(type != TCP_SERVER) - throw Exception(Exception::BAD_TYPE); - - struct sockaddr remoteAddr; - unsigned int addrSize = sizeof(remoteAddr); - - int newHandler = ::accept(handle, &remoteAddr, &addrSize); - if(newHandler == -1) return nullptr; - - return std::unique_ptr(new Socket(newHandler, hostLocal, portLocal, &remoteAddr, ipVersion)); -} - -void Socket::disconnect() { - setInputBufferSize(0); - setOutputBufferSize(0); - if(handle == -1) return; - close(handle); - handle = -1; - status = NOT_CONNECTED; -} - +/* + netLink: c++ 11 networking library + Copyright 2013 Alexander Meißner (lichtso@gamefortec.net) + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "include/Socket.h" + +#ifdef WIN32 +#define snprintf _snprintf_s +#else +#define closesocket close +#endif + +namespace netLink { + +static void readSockaddr(const struct sockaddr* addr, std::string& host, unsigned int& port) { + char buffer[INET6_ADDRSTRLEN]; + if(addr->sa_family == AF_INET) { + auto sin = reinterpret_cast(addr); + port = ntohs(sin->sin_port); + inet_ntop(addr->sa_family, (void*)&(sin->sin_addr), buffer, sizeof(buffer)); + }else{ + auto sin = reinterpret_cast(addr); + port = ntohs(sin->sin6_port); + inet_ntop(addr->sa_family, (void*)&(sin->sin6_addr), buffer, sizeof(buffer)); + } + host = buffer; +} + +Socket::AddrinfoContainer Socket::getSocketInfoFor(const char* host, unsigned int port, bool wildcardAddress) { + struct addrinfo conf, *res; + memset(&conf, 0, sizeof(conf)); + conf.ai_flags = AI_V4MAPPED; + conf.ai_family = AF_UNSPEC; + + if(wildcardAddress) + conf.ai_flags |= AI_PASSIVE; + + switch(type) { + case TCP_CLIENT: + case TCP_SERVER: + conf.ai_socktype = SOCK_STREAM; + break; + case UDP_PEER: + conf.ai_socktype = SOCK_DGRAM; + break; + default: + throw Exception(Exception::BAD_PROTOCOL); + } + + char portStr[10]; + snprintf(portStr, 10, "%u", port); + + int result = getaddrinfo(host, portStr, &conf, &res); + if(result != 0) + throw Exception(Exception::ERROR_RESOLVING_ADDRESS); + + return AddrinfoContainer(res); +} + +Socket::pos_type Socket::seekoff(Socket::off_type off, std::ios_base::seekdir way, std::ios_base::openmode which) { + switch(way) { + case std::ios_base::beg: + if (off < 0) break; + + if(which & std::ios_base::in) { + if(eback() + off >= egptr()) break; + setg(eback(), eback()+off, egptr()); + } + + if(which & std::ios_base::out) { + if(pbase() + off >= epptr()) break; + setp(pbase(), epptr()); + pbump(off); + } + return off; + case std::ios_base::cur: + if(which == std::ios_base::in) { + if(eback() + off >= egptr() || egptr() + off < eback()) break; + setg(eback(), gptr()+off, egptr()); + return gptr()-eback(); + }else if(which == std::ios_base::out) { + if(pbase() + off >= epptr() || epptr() + off < pbase()) break; + pbump(off); + return pptr()-pbase(); + }else + break; + case std::ios_base::end: + if(off > 0) break; + + if(which == std::ios_base::in) { + if(egptr() + off < eback()) break; + setg(eback(), egptr()+off, egptr()); + return gptr()-eback(); + }else if(which == std::ios_base::out) { + if(epptr() + off < pbase()) break; + setp(pbase(), epptr()); + pbump(off); + return pptr()-pbase(); + }else + break; + } + return EOF; +} + +Socket::pos_type Socket::seekpos(Socket::pos_type sp, std::ios_base::openmode which) { + return Socket::seekoff(sp, std::ios_base::beg, which); +} + +int Socket::sync() { + if(getOutputBufferSize() <= 0) //No output buffer + return EOF; + + if(pptr() == pbase()) //Allready in sync + return 0; + + try { + send(pbase(), pptr()-pbase()); + setp(pbase(), epptr()); + return 0; + } catch(Exception err) { + return EOF; + } +} + +std::streamsize Socket::xsgetn(char_type* buffer, std::streamsize size) { + if(inputIntermediateSize > 0) //Read from input buffer + return super::xsgetn(buffer, size); + + try { + return receive(buffer, size); + } catch(Exception err) { + return 0; + } +} + +Socket::int_type Socket::underflow() { + if(type == UDP_PEER || advanceInputBuffer() <= 0) + return EOF; + return *eback(); +} + +std::streamsize Socket::xsputn(const char_type* buffer, std::streamsize size) { + if(getOutputBufferSize()) //Write into buffer + return super::xsputn(buffer, size); + + try { + return send(buffer, size); + } catch(Exception err) { + return 0; + } +} + +Socket::int_type Socket::overflow(int_type c) { + if(sync() == EOF) + return EOF; + *pbase() = c; + pbump(1); + return c; +} + + + +void Socket::initSocket(bool blockingConnect) { + AddrinfoContainer info; + + if(type == TCP_CLIENT) + info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); + else{ + const char* host; + if(!hostLocal.compare("") || !hostLocal.compare("*")) + host = NULL; + else + host = hostLocal.c_str(); + info = getSocketInfoFor(host, portLocal, true); + } + + struct addrinfo* nextAddr = info.get(); + while(nextAddr) { + handle = socket(nextAddr->ai_family, nextAddr->ai_socktype, nextAddr->ai_protocol); + if(handle == -1) { + nextAddr = nextAddr->ai_next; + continue; + } + + setBlockingMode(blockingConnect); + #ifdef WIN32 + char flag = 1; + #else + int flag = 1; + #endif + if(setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) + throw Exception(Exception::ERROR_SET_SOCK_OPT); + + switch(nextAddr->ai_family) { + case AF_INET: + ipVersion = IPv4; + break; + case AF_INET6: + ipVersion = IPv6; + break; + } + + switch(type) { + case NONE: + case TCP_SERVERS_CLIENT: + throw Exception(Exception::BAD_TYPE); + case TCP_CLIENT: + if(connect(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1 && blockingConnect) { + closesocket(handle); + handle = -1; + }else if(blockingConnect) + status = READY; + else + status = NOT_CONNECTED; + break; + case TCP_SERVER: { + if(bind(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1) { + closesocket(handle); + handle = -1; + } + + if(listen(handle, status) == -1) + throw Exception(Exception::ERROR_INIT); + } break; + case UDP_PEER: { + if(bind(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1) { + closesocket(handle); + handle = -1; + } + + status = READY; + } break; + } + if(handle == -1) { + nextAddr = nextAddr->ai_next; + continue; + } + + if(blockingConnect) + setBlockingMode(false); + break; + } + + if(handle == -1) + throw Exception(Exception::ERROR_INIT); + + #ifdef WIN32 + int size = sizeof(nextAddr->ai_addr); + #else + unsigned int size = sizeof(nextAddr->ai_addr); + #endif + if(getsockname(handle, nextAddr->ai_addr, &size) != 0) + throw Exception(Exception::ERROR_GET_SOCK_NAME); + + readSockaddr(nextAddr->ai_addr, hostLocal, portLocal); +} + +void Socket::setMulticastGroup(const struct sockaddr* addr, bool join) { + if(ipVersion == IPv4) { + auto sin = reinterpret_cast(addr)->sin_addr; + if((ntohl(sin.s_addr) & 0xF0000000) == 0xE0000000) { + struct ip_mreq mreq; + mreq.imr_multiaddr = sin; + mreq.imr_interface.s_addr = 0; + + if(setsockopt(handle, IPPROTO_IP, (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == -1) + throw Exception(Exception::ERROR_SET_SOCK_OPT); + } + }else{ + auto sin = reinterpret_cast(addr)->sin6_addr; + if(sin.s6_addr[0] == 0xFF) { + struct ipv6_mreq mreq; + mreq.ipv6mr_multiaddr = sin; + mreq.ipv6mr_interface = 0; + + if(setsockopt(handle, IPPROTO_IPV6, (join) ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (const char*)&mreq, sizeof(ipv6_mreq)) == -1) + throw Exception(Exception::ERROR_SET_SOCK_OPT); + } + } +} + +Socket::Socket(int _handle, const std::string& _hostLocal, unsigned _portLocal, + struct sockaddr* remoteAddr, IPVersion _ipVersion) + :ipVersion(_ipVersion), type(TCP_SERVERS_CLIENT), status(READY), + handle(_handle), hostLocal(_hostLocal), portLocal(_portLocal) { + readSockaddr(remoteAddr, hostRemote, portRemote); + setBlockingMode(false); +} + +void Socket::initAsTcpClient(const std::string& _hostRemote, unsigned _portRemote, bool waitUntilConnected) { + type = TCP_CLIENT; + hostRemote = _hostRemote; + portRemote = _portRemote; + initSocket(waitUntilConnected); +} + +void Socket::initAsTcpServer(const std::string& _hostLocal, unsigned _portLocal, unsigned _listenQueue) { + type = TCP_SERVER; + hostLocal = _hostLocal; + portLocal = _portLocal; + status = _listenQueue; + initSocket(false); +} + +void Socket::initAsUdpPeer(const std::string& _hostLocal, unsigned _portLocal) { + type = UDP_PEER; + hostLocal = _hostLocal; + portLocal = _portLocal; + initSocket(false); +} + +Socket::~Socket() { + disconnect(); +} + +SocketType Socket::getType() const { + return type; +} + +IPVersion Socket::getIPVersion() const { + return ipVersion; +} + +SocketStatus Socket::getStatus() const { + if(type == TCP_SERVER) + return (status == NOT_INITIALIZED) ? NOT_INITIALIZED : LISTENING; + else + return (SocketStatus)status; +} + +std::streamsize Socket::showmanyc() { + #ifdef WIN32 + unsigned long result = -1; + if(ioctlsocket(handle, FIONREAD, &result)) + #else + int result = -1; + if(ioctl(handle, FIONREAD, &result)) + #endif + throw Exception(Exception::ERROR_IOCTL); + else + return result; +} + +std::streamsize Socket::advanceInputBuffer() { + if(inputIntermediateSize == 0) //No input buffer + return 0; + + std::streamsize inAvail; + if(type == UDP_PEER) + inAvail = 0; + else{ + inAvail = egptr()-gptr(); + memmove(eback(), gptr(), inAvail); + } + + try { + inAvail += receive(eback()+inAvail, inputIntermediateSize-inAvail); + } catch(Exception err) { + + } + + setg(eback(), eback(), eback()+inAvail); + return inAvail; +} + +std::streamsize Socket::receive(char_type* buffer, std::streamsize size) { + size = min(size, showmanyc()); + if(size == 0) return 0; + + switch(type) { + case UDP_PEER: { + struct sockaddr remoteAddr; + #ifdef WIN32 + int addrSize = sizeof(remoteAddr); + #else + unsigned int addrSize = sizeof(remoteAddr); + #endif + int result = recvfrom(handle, (char*)buffer, size, 0, &remoteAddr, &addrSize); + + if(result == -1) { + portRemote = 0; + hostRemote = ""; + throw Exception(Exception::ERROR_READ); + }else + readSockaddr(&remoteAddr, hostRemote, portRemote); + + return result; + } + case TCP_CLIENT: + case TCP_SERVERS_CLIENT: { + int result = recv(handle, (char*)buffer, size, 0); + + if(result == -1) + throw Exception(Exception::ERROR_READ); + + return result; + } + default: + case NONE: + case TCP_SERVER: + throw Exception(Exception::BAD_TYPE); + } +} + +std::streamsize Socket::send(const char_type* buffer, std::streamsize size) { + if(size == 0) return 0; + + switch(type) { + case UDP_PEER: { + AddrinfoContainer info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); + + size_t sentBytes = 0; + while(sentBytes < size) { + int result = ::sendto(handle, (const char*)buffer + sentBytes, size - sentBytes, 0, info->ai_addr, info->ai_addrlen); + + if(result == -1) + throw Exception(Exception::ERROR_SEND); + + sentBytes += result; + } + + return sentBytes; + } + case TCP_CLIENT: + case TCP_SERVERS_CLIENT: { + size_t sentBytes = 0; + while(sentBytes < size) { + int result = ::send(handle, (const char*)buffer + sentBytes, size - sentBytes, 0); + + if(result == -1) + throw Exception(Exception::ERROR_SEND); + + sentBytes += result; + } + return sentBytes; + } + default: + case NONE: + case TCP_SERVER: + throw Exception(Exception::BAD_TYPE); + } +} + +std::streamsize Socket::getInputBufferSize() { + return inputIntermediateSize; +} + +std::streamsize Socket::getOutputBufferSize() { + return epptr()-pbase(); +} + +void Socket::setInputBufferSize(std::streamsize n) { + if(eback()) delete [] eback(); + if(n == 0) return; + if(type == TCP_SERVER) + throw Exception(Exception::BAD_TYPE); + + char_type* readBuffer = new char_type[n]; + setg(readBuffer, readBuffer, readBuffer); + inputIntermediateSize = n; +} + +void Socket::setOutputBufferSize(std::streamsize n) { + if(pbase()) delete [] pbase(); + if(n == 0) return; + if(type == TCP_SERVER) + throw Exception(Exception::BAD_TYPE); + + char_type* writeBuffer = new char_type[n]; + setp(writeBuffer, writeBuffer+n); +} + +void Socket::setBlockingMode(bool blocking) { + #ifdef WIN32 + unsigned long flag = !blocking; + if(ioctlsocket(handle, FIONBIO, &flag) == -1) + #else + int flags = fcntl(handle, F_GETFL); + if(blocking) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + if(fcntl(handle, F_SETFL, flags) == -1) + #endif + throw Exception(Exception::ERROR_IOCTL); +} + +void Socket::setBroadcast(bool active) { + if(type != UDP_PEER || ipVersion != IPv4) + throw Exception(Exception::BAD_PROTOCOL); + + #ifdef WIN32 + char flag = 1; + #else + int flag = 1; + #endif + if(setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) == -1) + throw Exception(Exception::ERROR_SET_SOCK_OPT); +} + +void Socket::setMulticastGroup(const std::string& address, bool join) { + if(type != UDP_PEER) + throw Exception(Exception::BAD_PROTOCOL); + + struct sockaddr_storage addr; + if(ipVersion == IPv4) { + auto sin = reinterpret_cast(&addr); + if(inet_pton(AF_INET, address.c_str(), &sin->sin_addr) == -1) + throw Exception(Exception::ERROR_RESOLVING_ADDRESS); + }else{ + auto sin = reinterpret_cast(&addr); + if(inet_pton(AF_INET6, address.c_str(), &sin->sin6_addr) == -1) + throw Exception(Exception::ERROR_RESOLVING_ADDRESS); + } + + setMulticastGroup(reinterpret_cast(&addr), join); +} + +std::unique_ptr Socket::accept() { + if(type != TCP_SERVER) + throw Exception(Exception::BAD_TYPE); + + struct sockaddr remoteAddr; + #ifdef WIN32 + int addrSize = sizeof(remoteAddr); + #else + unsigned int addrSize = sizeof(remoteAddr); + #endif + + int newHandler = ::accept(handle, &remoteAddr, &addrSize); + if(newHandler == -1) return nullptr; + + return std::unique_ptr(new Socket(newHandler, hostLocal, portLocal, &remoteAddr, ipVersion)); +} + +void Socket::disconnect() { + setInputBufferSize(0); + setOutputBufferSize(0); + if(handle == -1) return; + closesocket(handle); + handle = -1; + status = NOT_CONNECTED; +} + }; \ No newline at end of file diff --git a/SocketManager.cpp b/SocketManager.cpp index fb4eeb9..fd22ec8 100644 --- a/SocketManager.cpp +++ b/SocketManager.cpp @@ -32,7 +32,7 @@ void SocketManager::listen(double secLeft) { int maxHandle = 0; foreach_e(sockets, iterator) { Socket* socket = (*iterator).get(); - maxHandle = std::max(maxHandle, socket->handle); + maxHandle = max(maxHandle, socket->handle); FD_SET(socket->handle, &readfds); if(socket->type != TCP_SERVER) FD_SET(socket->handle, &writefds); @@ -40,10 +40,15 @@ void SocketManager::listen(double secLeft) { FD_SET(socket->handle, &exceptfds); } - struct timeval timeout; - timeout.tv_sec = secLeft; - timeout.tv_usec = fmod(secLeft, 1.0) * 1000000.0; - if(select(maxHandle + 1, &readfds, &writefds, &exceptfds, &timeout) == -1) + struct timeval timeout, *timeoutPtr; + if(secLeft >= 0.0) { + timeout.tv_sec = secLeft; + timeout.tv_usec = fmod(secLeft, 1.0) * 1000000.0; + timeoutPtr = &timeout; + }else + timeoutPtr = NULL; + + if(select(maxHandle + 1, &readfds, &writefds, &exceptfds, timeoutPtr) == -1) throw Exception(Exception::ERROR_SELECT); foreach_e(sockets, iterator) { diff --git a/doc/MsgPack.html b/doc/MsgPack.html index 5512878..d548c28 100644 --- a/doc/MsgPack.html +++ b/doc/MsgPack.html @@ -1,877 +1,877 @@ - - - - - -MsgPack Tutorial - - - - - -
-
-

Serialization

-
-

There are two ways you can put elements into the serializer. The first one is hierarchical using MsgPack::Array or MsgPack::Map. You just have to put all the elements into their container first and then you can push the entire container into the serializer.

-
-
-
MsgPack::Serializer serializer(socket);
-std::vector<std::unique_ptr<MsgPack::Element>> arrayElements;
-std::unique_ptr<MsgPack::Element> abstract(new MsgPack::Primitive(true));
-std::unique_ptr<MsgPack::Element> string(new MsgPack::String("Hello World!"));
-std::unique_ptr<MsgPack::Element> emptyArray(new MsgPack::Array(std::move(arrayElements)));
-
-arrayElements.push_back(std::move(abstract));
-arrayElements.push_back(std::move(emptyArray));
-arrayElements.push_back(std::move(string));
-
-serializer << new MsgPack::Array(std::move(arrayElements));
-serializer.serialize();
-

The second one is to push only hints of how many of the next elements belong into containers. Use MsgPack::ArrayHeader or MsgPack::MapHeader for this approach.

-
-
-
MsgPack::Serializer serializer(socket);
-
-serializer << new MsgPack::ArrayHeader(3);
-serializer << true;
-serializer << new MsgPack::ArrayHeader(0);
-serializer << "Hello World!";
-serializer.serialize();
-

The above two examples generate the same output. In some situations one or the other might be a better match for your needs. You can even mix the two ways of serialization.

-
-
-
-

Deserialization

-
-

The same goes for deserialization. Except that you can’t mix them, but have to define which one to use in the constructor of the Deserializer.

-
-
-
MsgPack::Deserializer deserializer(socket, true);
-
-deserializer.deserialize([](std::unique_ptr<MsgPack::Element> parsed) {
-    std::cout << "Parsed: " << *parsed << "\n";
-    return false;
-});
-

If the second parameter is true, the deserializer will generate a hierarchy of MsgPack::Array and MsgPack::Map else it will just generate a stream of elements sometimes containing a MsgPack::ArrayHeader or MsgPack::MapHeader. If you don’t pass a second parameter then it is true by default.

-
-
-
-

Push vs. Pull

-
-

Until here we only have had examples of a push serializer and deserializer, where the program has to push elements into the serializer and the deserializer pushes back deserialized elements. But the pull approach is also supported. This way you have to define a callback and deliver the next element to be serialized. The serializer will then pull the next element when it is needed.

-
-
-
MsgPack::Serializer serializer(socket);
-
-serializer.serialize([]() {
-    Element* nextElement = new MsgPack:: [...] ;
-    return std::unique_ptr<Element>(nextElement);
-});
-

The same goes for deserialization again.

-
-
-
MsgPack::Deserializer deserializer(socket);
-
-std::unique_ptr<Element> element;
-deserializer >> element;
-if(element)
-    [...]
-

or you can use a callback but return true to cancel the deserializion of further elements, which is nearly the same as a pull parser:

-
-
-
MsgPack::Deserializer deserializer(socket);
-
-std::unique_ptr<Element> element;
-deserializer.deserialize([&element](std::unique_ptr<MsgPack::Element> parsed) {
-    element = std::move(parsed);
-    return true;
-});
-if(element)
-    [...]
-

But be careful, all of the MsgPack streams are non blocking which means that they don’t wait until a element hast been serialized or deserialized. When you use push serialization the elements will be pushed into a queue and a call to serialize() will try to serialize as much as possible and when you use pull serialization then the callback won’t be called until the last element is serialized. It is simular at the deserialization. A push deserializer won’t call the callback until a element is completly deserialized and a pull deserializer might return a empty pointer if there is nothing to pull.

-

Because of this you might have to call serialize() or deserialize() multiple times to do the rest of the last element which might be only partially done. But both methods will try to process as many bytes as possible. Therefore you should call serialize() again, when there is new space in your stream buffer to be written in and deserialize() when there is new data available in your stream buffer. And you will have to call them with the same MsgPack::Serializer or MsgPack::Deserializer again, else the intermediate state of the last time will be lost.

-
-
-
-

Data flow control

-
-

A nice feature is, that you can control the flow of data exactly and decide how many bytes will be processed. Just add a second parameter to the serialize() or deserialize() call.

-
-
-
MsgPack::Serializer serializer(socket);
-
-serializer << new MsgPack::ArrayHeader(3);
-serializer << true;
-serializer << new MsgPack::ArrayHeader(0);
-serializer << "Hello World!";
-std::cout << "Processed " << serializer.serialize(nullptr, 16) << " bytes\n";
-

to seralize only 16 bytes or

-
-
-
MsgPack::Deserializer deserializer(socket);
-
-std::cout << "Processed " << deserializer.deserialize([](std::unique_ptr<MsgPack::Element> parsed) {
-    std::cout << "Parsed: " << *parsed << "\n";
-    return false;
-}, 16) << " bytes\n";
-

to deseralize only 16 bytes. Both methods return how many bytes they actually processed.

-
-
-
-

- - - + + + + + +MsgPack Tutorial + + + + + +
+
+

Serialization

+
+

There are two ways you can put elements into the serializer. The first one is hierarchical using MsgPack::Array or MsgPack::Map. You just have to put all the elements into their container first and then you can push the entire container into the serializer.

+
+
+
MsgPack::Serializer serializer(socket);
+std::vector<std::unique_ptr<MsgPack::Element>> arrayElements;
+std::unique_ptr<MsgPack::Element> abstract(new MsgPack::Primitive(true));
+std::unique_ptr<MsgPack::Element> string(new MsgPack::String("Hello World!"));
+std::unique_ptr<MsgPack::Element> emptyArray(new MsgPack::Array(std::move(arrayElements)));
+
+arrayElements.push_back(std::move(abstract));
+arrayElements.push_back(std::move(emptyArray));
+arrayElements.push_back(std::move(string));
+
+serializer << new MsgPack::Array(std::move(arrayElements));
+serializer.serialize();
+

The second one is to push only hints of how many of the next elements belong into containers. Use MsgPack::ArrayHeader or MsgPack::MapHeader for this approach.

+
+
+
MsgPack::Serializer serializer(socket);
+
+serializer << new MsgPack::ArrayHeader(3);
+serializer << true;
+serializer << new MsgPack::ArrayHeader(0);
+serializer << "Hello World!";
+serializer.serialize();
+

The above two examples generate the same output. In some situations one or the other might be a better match for your needs. You can even mix the two ways of serialization.

+
+
+
+

Deserialization

+
+

The same goes for deserialization. Except that you can’t mix them, but have to define which one to use in the constructor of the Deserializer.

+
+
+
MsgPack::Deserializer deserializer(socket, true);
+
+deserializer.deserialize([](std::unique_ptr<MsgPack::Element> parsed) {
+    std::cout << "Parsed: " << *parsed << "\n";
+    return false;
+});
+

If the second parameter is true, the deserializer will generate a hierarchy of MsgPack::Array and MsgPack::Map else it will just generate a stream of elements sometimes containing a MsgPack::ArrayHeader or MsgPack::MapHeader. If you don’t pass a second parameter then it is true by default.

+
+
+
+

Push vs. Pull

+
+

Until here we only have had examples of a push serializer and deserializer, where the program has to push elements into the serializer and the deserializer pushes back deserialized elements. But the pull approach is also supported. This way you have to define a callback and deliver the next element to be serialized. The serializer will then pull the next element when it is needed.

+
+
+
MsgPack::Serializer serializer(socket);
+
+serializer.serialize([]() {
+    Element* nextElement = new MsgPack:: [...] ;
+    return std::unique_ptr<Element>(nextElement);
+});
+

The same goes for deserialization again.

+
+
+
MsgPack::Deserializer deserializer(socket);
+
+std::unique_ptr<Element> element;
+deserializer >> element;
+if(element)
+    [...]
+

or you can use a callback but return true to cancel the deserializion of further elements, which is nearly the same as a pull parser:

+
+
+
MsgPack::Deserializer deserializer(socket);
+
+std::unique_ptr<Element> element;
+deserializer.deserialize([&element](std::unique_ptr<MsgPack::Element> parsed) {
+    element = std::move(parsed);
+    return true;
+});
+if(element)
+    [...]
+

But be careful, all of the MsgPack streams are non blocking which means that they don’t wait until a element hast been serialized or deserialized. When you use push serialization the elements will be pushed into a queue and a call to serialize() will try to serialize as much as possible and when you use pull serialization then the callback won’t be called until the last element is serialized. It is simular at the deserialization. A push deserializer won’t call the callback until a element is completly deserialized and a pull deserializer might return a empty pointer if there is nothing to pull.

+

Because of this you might have to call serialize() or deserialize() multiple times to do the rest of the last element which might be only partially done. But both methods will try to process as many bytes as possible. Therefore you should call serialize() again, when there is new space in your stream buffer to be written in and deserialize() when there is new data available in your stream buffer. And you will have to call them with the same MsgPack::Serializer or MsgPack::Deserializer again, else the intermediate state of the last time will be lost.

+
+
+
+

Data flow control

+
+

A nice feature is, that you can control the flow of data exactly and decide how many bytes will be processed. Just add a second parameter to the serialize() or deserialize() call.

+
+
+
MsgPack::Serializer serializer(socket);
+
+serializer << new MsgPack::ArrayHeader(3);
+serializer << true;
+serializer << new MsgPack::ArrayHeader(0);
+serializer << "Hello World!";
+std::cout << "Processed " << serializer.serialize(nullptr, 16) << " bytes\n";
+

to seralize only 16 bytes or

+
+
+
MsgPack::Deserializer deserializer(socket);
+
+std::cout << "Processed " << deserializer.deserialize([](std::unique_ptr<MsgPack::Element> parsed) {
+    std::cout << "Parsed: " << *parsed << "\n";
+    return false;
+}, 16) << " bytes\n";
+

to deseralize only 16 bytes. Both methods return how many bytes they actually processed.

+
+
+
+

+ + + diff --git a/examples/tcp.cpp b/examples/tcp.cpp index 1a8cae5..2309df0 100755 --- a/examples/tcp.cpp +++ b/examples/tcp.cpp @@ -1,119 +1,119 @@ -/* - netLink: c++ 11 networking library - Copyright 2013 Alexander Meißner (lichtso@gamefortec.net) - - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include "../include/netLink.h" - -int main(int argc, char** argv) { - netLink::SocketManager socketManager; - - //Alloc a new socket, insert it into the SocketManager - std::shared_ptr socket = socketManager.generateSocket(); - - //Prepare a Deserializer which has to be persistent over serval 'onReceive' calls - std::unique_ptr deserializer; - - //Get hostname - char hostname[256]; - FILE* f = popen("hostname -s", "r"); - fgets(hostname, 128, f); - pclose(f); - int len = strlen(hostname)-1; - hostname[len] = 0; - - //Define a callback, fired when a new client tries to connect - socketManager.onConnectRequest = [&deserializer](netLink::SocketManager* manager, netLink::Socket* serverSocket, netLink::Socket* socket) { - std::cout << "Accepted connection from " << socket->hostRemote << ":" << socket->portRemote << "\n"; - - //Alloc memory and a Deserializer for incoming messages - socket->setInputBufferSize(10000); - deserializer.reset(new MsgPack::Deserializer(socket)); - - //Accept all new connections - return true; - }; - - //Define a callback, fired when a sockets state changes - socketManager.onStateChanged = [&hostname](netLink::SocketManager* manager, netLink::Socket* socket, netLink::SocketStatus prev) { - if(prev == netLink::NOT_CONNECTED) { - std::cout << "Connection got accepted at " << socket->hostRemote << ":" << socket->portRemote << "\n"; - - //Prepare a MsgPack encoded message - MsgPack::Serializer serializer(socket); - serializer << new MsgPack::MapHeader(2); - serializer << "name"; - serializer << hostname; - serializer << "message"; - serializer << "Hello World!"; - - //Write all elements of the queue into the allocated output buffer of the socket and flush it - socket->setOutputBufferSize(10000); - serializer.serialize(); - socket->pubsync(); - } - }; - - //Define a callback, fired when a socket disconnects - socketManager.onDisconnect = [](netLink::SocketManager* manager, netLink::Socket* socket) { - std::cout << "Lost connection of " << socket->hostRemote << ":" << socket->portRemote << "\n"; - - //Quit if the connection to the server is lost - if(socket->getType() == netLink::TCP_CLIENT) { - std::cout << "Quit\n"; - exit(0); - } - }; - - //Define a callback, fired when a socket receives data - socketManager.onReceive = [&deserializer](netLink::SocketManager* manager, netLink::Socket* socket) { - try { - //hostRemote and portRemote are now set to the origin of the last received message - std::cout << "Received data from " << socket->hostRemote << ":" << socket->portRemote << "\n"; - - //Let a MsgPack::Deserializer parse all data at once - deserializer->deserialize([](std::unique_ptr element) { - std::cout << *element << "\n"; - - //Don't stop yet, try to parse more data - return false; - }); - }catch(netLink::Exception exc) { - std::cout << "Exception " << exc.code << "\n"; - } - }; - - //Ask user for a nice IP address - std::cout << "Enter a IP-Adress to connect to a sever or '*' to start a server:\n"; - while(socket->getStatus() == netLink::NOT_INITIALIZED) { - try { - std::cin >> socket->hostRemote; - - //Init socket as TCP server or client on port 3823 - if(socket->hostRemote == "*") - socket->initAsTcpServer("*", 3823); - else - socket->initAsTcpClient(socket->hostRemote, 3823); - }catch(netLink::Exception exc) { - std::cout << "Could not resolve address, please try again...\n"; - } - } - - while(true) { - //Let the SocketManager poll from all sockets, events will be triggered here - socketManager.listen(); - } - - return 0; +/* + netLink: c++ 11 networking library + Copyright 2013 Alexander Meißner (lichtso@gamefortec.net) + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include "../include/netLink.h" + +int main(int argc, char** argv) { + netLink::SocketManager socketManager; + + //Alloc a new socket, insert it into the SocketManager + std::shared_ptr socket = socketManager.generateSocket(); + + //Prepare a Deserializer which has to be persistent over serval 'onReceive' calls + std::unique_ptr deserializer; + + //Get hostname + char hostname[256]; + FILE* f = popen("hostname -s", "r"); + fgets(hostname, 128, f); + pclose(f); + int len = strlen(hostname)-1; + hostname[len] = 0; + + //Define a callback, fired when a new client tries to connect + socketManager.onConnectRequest = [&deserializer](netLink::SocketManager* manager, netLink::Socket* serverSocket, netLink::Socket* socket) { + std::cout << "Accepted connection from " << socket->hostRemote << ":" << socket->portRemote << "\n"; + + //Alloc memory and a Deserializer for incoming messages + socket->setInputBufferSize(10000); + deserializer.reset(new MsgPack::Deserializer(socket)); + + //Accept all new connections + return true; + }; + + //Define a callback, fired when a sockets state changes + socketManager.onStateChanged = [&hostname](netLink::SocketManager* manager, netLink::Socket* socket, netLink::SocketStatus prev) { + if(prev == netLink::NOT_CONNECTED) { + std::cout << "Connection got accepted at " << socket->hostRemote << ":" << socket->portRemote << "\n"; + + //Prepare a MsgPack encoded message + MsgPack::Serializer serializer(socket); + serializer << new MsgPack::MapHeader(2); + serializer << "name"; + serializer << hostname; + serializer << "message"; + serializer << "Hello World!"; + + //Write all elements of the queue into the allocated output buffer of the socket and flush it + socket->setOutputBufferSize(10000); + serializer.serialize(); + socket->pubsync(); + } + }; + + //Define a callback, fired when a socket disconnects + socketManager.onDisconnect = [](netLink::SocketManager* manager, netLink::Socket* socket) { + std::cout << "Lost connection of " << socket->hostRemote << ":" << socket->portRemote << "\n"; + + //Quit if the connection to the server is lost + if(socket->getType() == netLink::TCP_CLIENT) { + std::cout << "Quit\n"; + exit(0); + } + }; + + //Define a callback, fired when a socket receives data + socketManager.onReceive = [&deserializer](netLink::SocketManager* manager, netLink::Socket* socket) { + try { + //hostRemote and portRemote are now set to the origin of the last received message + std::cout << "Received data from " << socket->hostRemote << ":" << socket->portRemote << "\n"; + + //Let a MsgPack::Deserializer parse all data at once + deserializer->deserialize([](std::unique_ptr element) { + std::cout << *element << "\n"; + + //Don't stop yet, try to parse more data + return false; + }); + }catch(netLink::Exception exc) { + std::cout << "Exception " << exc.code << "\n"; + } + }; + + //Ask user for a nice IP address + std::cout << "Enter a IP-Adress to connect to a sever or '*' to start a server:\n"; + while(socket->getStatus() == netLink::NOT_INITIALIZED) { + try { + std::cin >> socket->hostRemote; + + //Init socket as TCP server or client on port 3823 + if(socket->hostRemote == "*") + socket->initAsTcpServer("*", 3823); + else + socket->initAsTcpClient(socket->hostRemote, 3823); + }catch(netLink::Exception exc) { + std::cout << "Could not resolve address, please try again...\n"; + } + } + + while(true) { + //Let the SocketManager poll from all sockets, events will be triggered here + socketManager.listen(); + } + + return 0; } \ No newline at end of file diff --git a/examples/udp.cpp b/examples/udp.cpp index 3c34658..a3c6cdd 100755 --- a/examples/udp.cpp +++ b/examples/udp.cpp @@ -1,83 +1,83 @@ -/* - netLink: c++ 11 networking library - Copyright 2013 Alexander Meißner (lichtso@gamefortec.net) - - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include "../include/netLink.h" - -int main(int argc, char** argv) { - netLink::SocketManager socketManager; - - //Define a callback, fired when a socket receives data - socketManager.onReceive = [](netLink::SocketManager* manager, netLink::Socket* socket) { - try { - //Because we are using UDP we want to ensure that we only read data from the incoming packet - socket->advanceInputBuffer(); - - //hostRemote and portRemote are now set to the origin of the last received message - std::cout << "Received data from " << socket->hostRemote << ":" << socket->portRemote << "\n"; - - //Let a MsgPack::Deserializer parse all data at once - MsgPack::Deserializer deserializer(socket); - deserializer.deserialize([](std::unique_ptr element) { - std::cout << *element << "\n"; - - //Don't stop yet, try to parse more data - return false; - }); - }catch(netLink::Exception exc) { - std::cout << "Exception " << exc.code << "\n"; - } - }; - - //Alloc a new socket and insert it into the SocketManager - std::shared_ptr socket = socketManager.generateSocket(); - - //Init socket as UDP listening to all incoming adresses (let the system choose IPv4 or IPv6) on port 3824 - socket->initAsUdpPeer("*", 3824); - - //Set the size of the intermediate buffers needed when using streams of UDP sockets - socket->setInputBufferSize(10000); - socket->setOutputBufferSize(10000); - - //Define the destination for the next sent message (depending on the choosen IP version) - socket->hostRemote = (socket->getIPVersion() == netLink::IPv4) ? "224.0.0.1" : "FF02:0001::"; - socket->portRemote = socket->portLocal; - - //Join the multicast group to receive messages from the given address - socket->setMulticastGroup(socket->hostRemote, true); - - //Prepare a MsgPack encoded message - MsgPack::Serializer serializer(socket.get()); - serializer << "Test message"; - serializer << new MsgPack::ArrayHeader(3); - serializer << new MsgPack::MapHeader(2); - serializer << "Boolean"; - serializer << true; - serializer << "Number"; - serializer << 2487.348; - serializer << new MsgPack::ArrayHeader(0); - serializer << new MsgPack::Primitive(); - - //Write all elements of the queue into the output buffer of the socket and flush it - serializer.serialize(); - socket->pubsync(); - - while(true) { - //Let the SocketManager poll from all sockets, events will be triggered here - socketManager.listen(); - } - - return 0; +/* + netLink: c++ 11 networking library + Copyright 2013 Alexander Meißner (lichtso@gamefortec.net) + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include "../include/netLink.h" + +int main(int argc, char** argv) { + netLink::SocketManager socketManager; + + //Define a callback, fired when a socket receives data + socketManager.onReceive = [](netLink::SocketManager* manager, netLink::Socket* socket) { + try { + //Because we are using UDP we want to ensure that we only read data from the incoming packet + socket->advanceInputBuffer(); + + //hostRemote and portRemote are now set to the origin of the last received message + std::cout << "Received data from " << socket->hostRemote << ":" << socket->portRemote << "\n"; + + //Let a MsgPack::Deserializer parse all data at once + MsgPack::Deserializer deserializer(socket); + deserializer.deserialize([](std::unique_ptr element) { + std::cout << *element << "\n"; + + //Don't stop yet, try to parse more data + return false; + }); + }catch(netLink::Exception exc) { + std::cout << "Exception " << exc.code << "\n"; + } + }; + + //Alloc a new socket and insert it into the SocketManager + std::shared_ptr socket = socketManager.generateSocket(); + + //Init socket as UDP listening to all incoming adresses (let the system choose IPv4 or IPv6) on port 3824 + socket->initAsUdpPeer("*", 3824); + + //Set the size of the intermediate buffers needed when using streams of UDP sockets + socket->setInputBufferSize(10000); + socket->setOutputBufferSize(10000); + + //Define the destination for the next sent message (depending on the choosen IP version) + socket->hostRemote = (socket->getIPVersion() == netLink::IPv4) ? "224.0.0.1" : "FF02:0001::"; + socket->portRemote = socket->portLocal; + + //Join the multicast group to receive messages from the given address + socket->setMulticastGroup(socket->hostRemote, true); + + //Prepare a MsgPack encoded message + MsgPack::Serializer serializer(socket.get()); + serializer << "Test message"; + serializer << new MsgPack::ArrayHeader(3); + serializer << new MsgPack::MapHeader(2); + serializer << "Boolean"; + serializer << true; + serializer << "Number"; + serializer << 2487.348; + serializer << new MsgPack::ArrayHeader(0); + serializer << new MsgPack::Primitive(); + + //Write all elements of the queue into the output buffer of the socket and flush it + serializer.serialize(); + socket->pubsync(); + + while(true) { + //Let the SocketManager poll from all sockets, events will be triggered here + socketManager.listen(); + } + + return 0; } \ No newline at end of file diff --git a/include/Core.h b/include/Core.h index b1308ea..a6be3c7 100644 --- a/include/Core.h +++ b/include/Core.h @@ -13,16 +13,22 @@ 3. This notice may not be removed or altered from any source distribution. */ -#ifndef netLink_Core -#define netLink_Core +#pragma once #include "MsgPack.h" +#ifdef WIN32 +#include +#include +#else #include #include #include #include #include +#define min std::min +#define max std::max +#endif #include #include @@ -88,6 +94,4 @@ namespace netLink { Exception(CODE _code): code(_code) { } }; -}; - -#endif +}; \ No newline at end of file diff --git a/include/MsgPack.h b/include/MsgPack.h index e2788aa..c37c517 100644 --- a/include/MsgPack.h +++ b/include/MsgPack.h @@ -13,14 +13,15 @@ 3. This notice may not be removed or altered from any source distribution. */ +#pragma once + #include #include #include #include #include - -#ifndef netLink_MsgPack -#define netLink_MsgPack +#include +#include //Store numbers in network endian (big endian) inline void storeUint8(uint8_t* target, uint8_t source); @@ -54,7 +55,7 @@ namespace MsgPack { FIXARRAY = 0x90, FIXSTR = 0xA0, NIL = 0xC0, - ERROR = 0xC1, + UNDEFINED = 0xC1, BOOL_FALSE = 0xC2, BOOL_TRUE = 0xC3, BIN_8 = 0xC4, @@ -408,6 +409,4 @@ namespace MsgPack { }; std::ostream& operator<<(std::ostream& ostream, const Element& obj); -}; - -#endif +}; \ No newline at end of file diff --git a/include/Socket.h b/include/Socket.h index aba961c..bef0d32 100755 --- a/include/Socket.h +++ b/include/Socket.h @@ -13,8 +13,7 @@ 3. This notice may not be removed or altered from any source distribution. */ -#ifndef netLink_Socket -#define netLink_Socket +#pragma once #include "Core.h" @@ -39,7 +38,7 @@ namespace netLink { //Buffer management and positioning pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out); pos_type seekpos(pos_type sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out); - int_type sync(); + int sync(); //Input functions (get) std::streamsize inputIntermediateSize = 0; @@ -155,6 +154,4 @@ namespace netLink { void disconnect(); }; -}; - -#endif +}; \ No newline at end of file diff --git a/include/SocketManager.h b/include/SocketManager.h index 54e043d..fac8079 100644 --- a/include/SocketManager.h +++ b/include/SocketManager.h @@ -13,8 +13,7 @@ 3. This notice may not be removed or altered from any source distribution. */ -#ifndef netLink_SocketManager -#define netLink_SocketManager +#pragma once #include "Socket.h" @@ -38,11 +37,9 @@ namespace netLink { std::shared_ptr generateSocket(); /*! Listens a periode time - @param sec Maximum time to wait in seconds + @param sec Maximum time to wait in seconds or negative values to wait indefinitely */ void listen(double sec = 0.0); }; -}; - -#endif +}; \ No newline at end of file