/* ============================================================================= * scpp_socket.cpp * Copyright (c) 2003 - 2011 Nomura Kei * LICENSE : * LGPL (GNU Lesser General General Public License - Version 3,29 June 2007) * http://www.gnu.org/copyleft/lesser.html * ============================================================================= * * ライブラリの動的ロードを行うモジュール * */ #include <cstdio> #include <cstdlib> #include <iostream> #include <sstream> #include <scpp_socket.hpp> namespace scpp { namespace { /** 最大ソケットアドレス長. */ const int MAXSOCKADDR = 128; /** close 時の read バッファサイズ. */ const int SOCKET_BUFFER_SIZE = 1024; /** * ソケットをクリーンアップします. */ void cleanupSocket() { #if (SCPP_IS_WINDOWS) WSACleanup(); #endif } /** * ソケットを初期化します. */ void initSocket() { #if (SCPP_IS_WINDOWS) static bool isInit = false; if (!isInit) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 0), &wsaData); atexit(cleanupSocket); } #endif } } // namespace 無名名前空間 //////////////////////////////////////////////////////////////////////////////// // // SocketException // /** * 最後に発生した errno に対するメッセージをもつ SocketException を構築します. * エラーメッセージを取得できない場合, 空文字がメッセージに設定されます. */ SocketException::SocketException() throw() : Exception() { #if (SCPP_WINDOWS) int errnum = WSAGetLastError(); Errno::getErrorMessage(&this->message, errnum); #endif } /** * コピーコンストラクタ. * * @param t コピー元 */ SocketException::SocketException(const SocketException& t) throw() : Exception(t) { } /** * 指定されたメッセージをもつ SocketException を構築します. * * @param msg メッセージ */ SocketException::SocketException(const std::string& msg) throw() : Exception(msg) { } /** * デストラクタ. */ SocketException::~SocketException() throw() { } //////////////////////////////////////////////////////////////////////////////// // // AbstractSocket // /** * AbstractSocket を構築します. * * @param type SOCK_STREAM/SOCK_DGRAM/SOCK_RAW * @param family AF_UNSPEC/AF_INET/AF_INET6) */ AbstractSocket::AbstractSocket(int type, int family) : sockFd(INVALID_SOCKET), sockType(type), sockFamily(family) { initSocket(); memset(&remoteAddr, 0x00, sizeof(sockaddr_storage)); memset(&localAddr, 0x00, sizeof(sockaddr_storage)); } /** * AbstractSocket を破棄します. */ AbstractSocket::~AbstractSocket() { // NOP } /** * ソケットをコピーします. * * @param sock コピー元ソケット * @return コピー後のソケット */ AbstractSocket& AbstractSocket::operator=(const AbstractSocket& sock) { clone(sock); return *this; } /** * 本ソケットの接続先アドレスを指定された addr に格納します. * 通常 addr のバッファサイズは, NI_MAXHOST とすべきです. * 取得に成功した場合は, 接続先アドレスを格納した addr のポインタを返します. * 失敗した場合は, null を返します. * * @param addr アドレス格納用バッファ * @param addrLen アドレス格納用バッファ長 * @return addr へのポインタ */ const char* AbstractSocket::getAddress(char* addr, size_t addrLen) const { int ret = ::getnameinfo(reinterpret_cast<const sockaddr*>(&remoteAddr), remoteAddrLen, addr, addrLen, 0, 0, NI_NUMERICHOST); if (ret != 0) { return 0; } return addr; } /** * 本ソケットの接続先ポート番号を返します. * 接続先ポート番号が取得できなかった場合, -1 を返します. * * @return ポート番号 */ int AbstractSocket::getPort() const { char tmpPort[NI_MAXSERV]; int ret = ::getnameinfo(reinterpret_cast<const sockaddr*>(&remoteAddr), remoteAddrLen, 0, 0, tmpPort, sizeof(tmpPort), NI_NUMERICSERV); if (ret != 0) { return -1; } return atoi(tmpPort); } /** * 本ソケットのローカルアドレスを指定された addr に格納します. * 通常 addr のバッファサイズは, NI_MAXHOST とすべきです. * 取得に成功した場合は, 接続先アドレスを格納した addr のポインタを返します. * 失敗した場合は, null を返します. * * @param addr アドレス格納用バッファ * @param addrLen アドレス格納用バッファ長 * @return addr へのポインタ */ const char* AbstractSocket::getLocalAddress(char* addr, size_t addrLen) const { int ret = ::getnameinfo(reinterpret_cast<const sockaddr*>(&localAddr), localAddrLen, addr, addrLen, 0, 0, NI_NUMERICHOST); if (ret != 0) { return 0; } return addr; } /** * 本ソケットのローカルポート番号を返します. * ローカルポート番号が取得できなかった場合, -1 を返します. * * @return ポート番号 */ int AbstractSocket::getLocalPort() const { char tmpPort[NI_MAXSERV]; int ret = ::getnameinfo(reinterpret_cast<const sockaddr*>(&localAddr), localAddrLen, 0, 0, tmpPort, sizeof(tmpPort), NI_NUMERICSERV); if (ret != 0) { return -1; } return atoi(tmpPort); } /** * ソケットディスクリプタを返します. * * @return ソケットディスクリプタ */ const socket_t AbstractSocket::getSocket() const { return sockFd; } /** * ソケットタイプを返します. * * @return ソケットタイプ */ const int AbstractSocket::getSocketType() const { return sockType; } /** * ソケットファミリを返します. * 本メソッドで返されるソケットファミリは, * 実際にバインドあるいは, 接続されているソケットのファミリとなります. * * @return ソケットファミリ */ const int AbstractSocket::getSocketFamily() const { union { sockaddr sa; char data[MAXSOCKADDR]; } un; socklen_t len = MAXSOCKADDR; if (::getsockname(sockFd, reinterpret_cast<sockaddr*>(un.data), &len) < 0) { return INVALID_SOCKET; } return (un.sa.sa_family); } /** * 指定されたアドレス, ポートにバインドします. * ANY アドレスを指定する場合, アドレスに 0 (null) を指定します. * ANY ポート番号を指定する場合, ポート番号に 不の値を指定します. * * @param addr アドレス * @param port ポート番号 */ void AbstractSocket::bind(const char* addr, int port) { std::stringstream ss; ss << port; bind(addr, ss.str().c_str()); } /** * 指定されたアドレス, サービスのアドレス情報を取得します. * * @param result アドレス情報格納用のポインタ * @param addr アドレス * @param service サービス * @param isPassive サーバソケット用の場合 true を指定してください */ void AbstractSocket::getAddrInfo( addrinfo** result, const char* addr, const char* service, bool isPassive) { addrinfo hints; memset(&hints, 0x00, sizeof(addrinfo)); hints.ai_family = sockFamily; hints.ai_socktype = sockType; if (isPassive) { hints.ai_flags = AI_PASSIVE; } int ret = ::getaddrinfo(addr, service, &hints, result); if (ret != 0) { throw SocketException(); } } /** * 指定されたソケットをコピーします. * * @param sock ソケット */ void AbstractSocket::clone(const AbstractSocket& sock) { this->sockFd = sock.sockFd; this->sockType = sock.sockType; this->sockFamily = sock.sockFamily; if (sock.remoteAddrLen != 0) { memcpy(&(this->remoteAddr), &(sock.remoteAddr), sock.remoteAddrLen); } if (sock.localAddrLen != 0) { memcpy(&(this->localAddr), &(sock.localAddr), sock.localAddrLen); } } //////////////////////////////////////////////////////////////////////////////// // // Socket // /** * TCP用ソケットを生成します. * * @param family AF_UNSPEC/AF_INET/AF_INET6 */ Socket::Socket(int family) : AbstractSocket(SOCK_STREAM, family) { // NOP } /** * TCP用ソケットのコピーコンストラクタです. * * @param sock ソケット */ Socket::Socket(const Socket& sock) : AbstractSocket(sock.sockType, sock.sockFamily) { clone(sock); } /** * Socket を破棄します. */ Socket::~Socket() { // NOP } /** * TCP用ソケットをコピーします. * * @param sock コピー元ソケット * @return コピー後のソケット */ Socket& Socket::operator=(const Socket& sock) { clone(sock); return *this; } /** * 指定されたアドレス, サービスにバインドします. * 指定されたアドレスとサービスにより複数候補がある場合は, * 最初に見つかったアドレスとポートにバインドします. * * @param addr アドレス * @param service サービス */ void Socket::bind(const char* addr, const char* service) { addrinfo* result; getAddrInfo(&result, addr, service, true); socket_t tmpSock; addrinfo* rp; int ret; for (rp = result; rp != 0; rp = rp->ai_next) { tmpSock = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (tmpSock < 0) { continue; } // 生成失敗 // bind 前の setsockopt は常に成功のためノーチェック const int on = 1; ::setsockopt(tmpSock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)); ret = ::bind(tmpSock, rp->ai_addr, rp->ai_addrlen); if (ret == 0) { break; } // Bind 成功 // bind 失敗したのでソケットをクローズしておく ::socket_close(tmpSock); } if (rp == 0) { freeaddrinfo(result); throw SocketException(); } sockFd = tmpSock; memcpy(&localAddr, rp->ai_addr, rp->ai_addrlen); localAddrLen = rp->ai_addrlen; freeaddrinfo(result); } /** * ソケットを接続待ちソケットとしてマークをつけます. * 保留中の接続のキュー最大長を指定します. * * @param backlog バックログ */ void Socket::listen(int backlog) { int ret = ::listen(sockFd, backlog); if (ret == SOCKET_ERROR) { throw SocketException(); } } /** * ソケットへの接続を受け付けます. * * @return 受け付けたソケット */ void Socket::accept(Socket* clientSocket) { sockaddr_storage addr; socklen_t addrLen = sizeof(sockaddr_storage); clientSocket->sockFd = ::accept(sockFd, reinterpret_cast<sockaddr*>(&addr), &addrLen); if (clientSocket->sockFd < 0) { throw SocketException(); } memcpy(&clientSocket->remoteAddr, &addr, addrLen); clientSocket->remoteAddrLen = addrLen; clientSocket->sockType = SOCK_STREAM; clientSocket->sockFamily = addr.ss_family; } /** * 指定されたアドレス, ポートに接続します. * loAddr に 0 を指定した場合, 自動的にローカルのアドレスが設定されます. * loPort に -1 を指定した場合, 自動的にローカルのポート番号が割り付けられます. * * @param addr アドレス * @param port ポート * @param loAddr ローカルアドレス * @param loPort ローカルポート */ void Socket::connect(const char* addr, int port, const char* loAddr, int loPort) { std::stringstream ssPort; ssPort << port; if ((0 <= loPort) && (loPort < 65535)) { std::stringstream ssLocalPort; ssLocalPort << loPort; ssLocalPort.str().c_str(); connect(addr, ssPort.str().c_str(), loAddr, ssLocalPort.str().c_str()); } else { connect(addr, ssPort.str().c_str(), loAddr, 0); } } /** * 指定されたアドレス, ポートに接続します. * loAddr に 0 を指定した場合, 自動的にローカルのアドレスが設定されます. * loService に 0 を指定した場合, 自動的にローカルのポート番号が割り付けられます. * * @param addr アド/sockレス * @param service ポート * @param loAddr ローカルアドレス * @param loService ローカルポート */ void Socket::connect( const char* addr, const char* service, const char* loAddr, const char* loService) { if ((loAddr != 0) || (loService != 0)) { // ローカルアドレス, サービスの指定あり bindAndConnect(addr, service, loAddr, loService); return; } addrinfo* result; getAddrInfo(&result, addr, service, false); socket_t tmpSock; addrinfo* rp; int ret; for (rp = result; rp != 0; rp = rp->ai_next) { tmpSock = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (tmpSock < 0) { continue; } // 生成失敗 ret = ::connect(tmpSock, rp->ai_addr, rp->ai_addrlen); if (ret != SOCKET_ERROR) { break; } // 接続成功 // 接続失敗したのでソケットをクローズしておく ::socket_close(tmpSock); } if (rp == 0) { freeaddrinfo(result); throw SocketException(); } sockFd = tmpSock; memcpy(&remoteAddr, rp->ai_addr, rp->ai_addrlen); remoteAddrLen = rp->ai_addrlen; freeaddrinfo(result); } /** * ソケットをクローズします. */ void Socket::close() { int ret = ::shutdown(sockFd, SHUT_WR); if (ret == 0) { int readSize; char buff[SOCKET_BUFFER_SIZE]; do { // 届いているデータを全て読み取る readSize = ::recv(sockFd, buff, sizeof(buff), 0); } while (readSize > 0); } ret = ::socket_close(sockFd); } /** * ソケットへメッセージを送信します. * * * @param buff バッファ * @param size サイズ * @param flag フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) * @return 書き込んだデータサイズ */ ssize_t Socket::send(const char* buff, size_t size, int flag) { return ::send(sockFd, buff, size, flag); } /** * ソケットからメッセージを受信します. * * @param buff バッファ * @param size サイズ * @param flag フラグ (MSG_CMSG_CLOEXEC/MSG_DONTWAIT/MSG_ERRQUEUE/MSG_OOB/MSG_PEEK/MSG_TRUNC/MSG_WAITALL) * @return 読み取ったデータサイズ */ ssize_t Socket::recv(char* buff, size_t size, int flag) { return ::recv(sockFd, buff, size, flag); } /** * ソケットへデータを書き込みます. * * @param buff バッファ * @param size サイズ * @return 書き込んだサイズ */ int Socket::write(const char* buff, size_t size) { ssize_t writeSize; int sockErr; do { // SCPP_EINTR の場合は再度書き込む Errno::setError(0); sockErr = 0; writeSize = send(buff, size); if (writeSize < 0) { sockErr = Errno::getError(); } } while (sockErr == SCPP_EINTR); return writeSize; } /** * ソケットからデータを読み取ります. * * @param buff バッファ * @param size サイズ * @return 読み取ったサイズ */ int Socket::read(char* buff, size_t size) { ssize_t readSize; int sockErr; do { // SCPP_EINTR の場合は再度読み取る Errno::setError(0); sockErr = 0; readSize = recv(buff, size); if (readSize < 0) { sockErr = Errno::getError(); } } while (sockErr == SCPP_EINTR); return readSize; } /** * 指定されたローカルのアドレス, サービスにバインドし, * 指定されたアドレス, サービスに接続します. * * @param addr 接続先アドレス * @param service 接続先サービス * @param loAddr 接続元アドレス * @param loService 接続元サービス */ void Socket::bindAndConnect( const char* addr, const char* service, const char* loAddr, const char* loService) { addrinfo* result; getAddrInfo(&result, loAddr, loService, false); socket_t tmpSock; addrinfo* rp; int ret; bool isConnected; for (rp = result; rp != 0; rp = rp->ai_next) { tmpSock = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (tmpSock < 0) { continue; } // 生成失敗 // bind 前の setsockopt は常に成功のためノーチェック const int on = 1; ::setsockopt(tmpSock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)); ret = ::bind(tmpSock, rp->ai_addr, rp->ai_addrlen); if (ret != 0) { // bind 失敗 ::socket_close(tmpSock); continue; } // addr, service に接続 isConnected = connect(tmpSock, addr, service); if (isConnected) { break; } // 接続成功 // 接続失敗したのでソケットをクローズしておく ::socket_close(tmpSock); } if (rp == 0) { freeaddrinfo(result); throw SocketException(); } sockFd = tmpSock; memcpy(&localAddr, rp->ai_addr, rp->ai_addrlen); localAddrLen = rp->ai_addrlen; freeaddrinfo(result); } /** * 指定されたソケットを用いて, 指定されたアドレス, サービスに接続します. * 接続に成功した場合, true を返します. * * @param sock ソケット * @param addr 接続先アドレス * @param service 接続先サービス * @return true/false (接続成功/失敗) */ bool Socket::connect(socket_t sock, const char* addr, const char* service) { addrinfo* result; getAddrInfo(&result, addr, service, false); addrinfo* rp; int ret; for (rp = result; rp != 0; rp = rp->ai_next) { ret = ::connect(sock, rp->ai_addr, rp->ai_addrlen); if (ret != SOCKET_ERROR) { break; } // 接続成功 } if (rp == 0) { freeaddrinfo(result); return false; } memcpy(&remoteAddr, rp->ai_addr, rp->ai_addrlen); remoteAddrLen = rp->ai_addrlen; freeaddrinfo(result); return true; } //////////////////////////////////////////////////////////////////////////////// // // DatagramSocket // /** * データグラムソケットを生成します. * * @param family AF_UNSPEC/AF_INET/AF_INET6 の何れかを指定します. */ DatagramSocket::DatagramSocket(int family) : AbstractSocket(SOCK_DGRAM, family) { // NOP } /** * UDP用ソケットのコピーコンストラクタです. * * @param sock ソケット */ DatagramSocket::DatagramSocket(const DatagramSocket& sock) : AbstractSocket(sock.sockType, sock.sockFamily) { clone(sock); } /** * DatagramSocket を破棄します. */ DatagramSocket::~DatagramSocket() { // NOP } /** * UDP用ソケットをコピーします. * * @param sock コピー元のソケット * @return コピー後のソケット */ DatagramSocket& DatagramSocket::operator=(const DatagramSocket& sock) { clone(sock); return *this; } /** * 指定されたアドレス, サービスにバインドします. * 指定されたアドレスとサービスにより複数候補がある場合は, * 最初に見つかったアドレスとポートにバインドします. * * @param addr アドレス * @param service サービス */ void DatagramSocket::bind(const char* addr, const char* service) { addrinfo* result; getAddrInfo(&result, addr, service, true); socket_t tmpSock; addrinfo* rp; int ret; for (rp = result; rp != 0; rp = rp->ai_next) { tmpSock = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (tmpSock < 0) { continue; } // 生成失敗 // bind 前の setsockopt は常に成功のためノーチェック const int on = 1; ::setsockopt(tmpSock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)); ret = ::bind(tmpSock, rp->ai_addr, rp->ai_addrlen); if (ret == 0) { break; } // Bind 成功 // bind 失敗したのでソケットをクローズしておく ::socket_close(tmpSock); } if (rp == 0) { freeaddrinfo(result); throw SocketException(); } sockFd = tmpSock; memcpy(&localAddr, rp->ai_addr, rp->ai_addrlen); localAddrLen = rp->ai_addrlen; freeaddrinfo(result); } /** * 指定されたアドレス, ポートにメッセージを送信します. * * @param buff 送信メッセージ * @param len 送信メッセージサイズ * @param flags フラグ * @param addr アドレス * @param port ポート番号 * @return 送信メッセージサイズ, エラー発生時は -1 */ ssize_t DatagramSocket::sendto( const char* buff, size_t len, int flags, const char* addr, int port) { std::stringstream ss; ss << port; return sendto(buff, len,flags, addr, ss.str().c_str()); } /** * 指定されたアドレス, サービスにメッセージを送信します. * * @param buff 送信メッセージ * @param len 送信メッセージのサイズ * @param flags フラグ * @param addr アドレス * @param service サービス名 * @return 送信メッセージサイズ, エラー発生時は -1 */ ssize_t DatagramSocket::sendto( const char* buff, size_t len, int flags, const char* addr, const char* service) { addrinfo* result; getAddrInfo(&result, addr, service, false); addrinfo* rp; ssize_t ret; for (rp = result; rp != 0; rp = rp->ai_next) { ret = ::sendto(sockFd, buff, len, flags, rp->ai_addr, rp->ai_addrlen); if (ret != SOCKET_ERROR) { break; } // 接続成功 } if (rp == 0) { freeaddrinfo(result); return ret; } memcpy(&remoteAddr, rp->ai_addr, rp->ai_addrlen); remoteAddrLen = rp->ai_addrlen; freeaddrinfo(result); return ret; } /** * 指定されたアドレス, サービスにメッセージを送信します. * * @param buff 送信メッセージ * @param len 送信メッセージのサイズ * @param flags フラグ * @param addr アドレス * @param addrLen アドレス長 * @return 送信メッセージサイズ, エラー発生時 -1 */ ssize_t DatagramSocket::sendto( const char* buff, size_t len, int flags, const sockaddr* addr, socklen_t addrLen) { return ::sendto(sockFd, buff, len, flags, addr, addrLen); } /** * メッセージを受信します. * srcAddr, srcPort がどちらも null でない場合, * 送信元のアドレスとポートが srcAddr, srcPort に格納されます. * * @param buff 受信メッセージバッファ * @param len 受信メッセージバッファのサイズ * @param flags フラグ * @param srcAddr 送信元アドレス格納用バッファ * @param srcAddrLen 送信元アドレス格納用バッファサイズ * @param srcPort 送信元ポート格納用バッファ * @param srcPortLen 送信元ポート格納用バッファサイズ * @return 受信メッセージサイズ, エラー発生時は -1, 接続先が正しく shutdown した場合 0 */ ssize_t DatagramSocket::recvfrom( char* buff, size_t len, int flags, char* srcAddr, size_t srcAddrLen, char* srcPort, size_t srcPortLen) { sockaddr_storage srcAddrStorage; socklen_t srcAddrStorageLen = sizeof(sockaddr_storage); ssize_t size = ::recvfrom(sockFd, buff, len, flags, reinterpret_cast<sockaddr*>(&srcAddrStorage), &srcAddrStorageLen); if ((srcAddr != 0) && (srcPort != 0)) { int ret = ::getnameinfo(reinterpret_cast<sockaddr*>(&srcAddrStorage), srcAddrStorageLen, srcAddr, srcAddrLen, srcPort, srcPortLen, (NI_NUMERICHOST | NI_NUMERICSERV)); if (ret != 0) { srcAddr[0] = '\0'; srcPort[0] = '\0'; } } return size; } /** * メッセージを受信します. * srcAddr, srcPort がどちらも null でない場合, * 送信元のアドレスとポートが srcAddr, srcPort に格納されます. * * @param buff 受信メッセージバッファ * @param len 受信メッセージバッファのサイズ * @param flags フラグ * @param addr 送信元アドレス格納用 * @param addrLen 送信元アドレスサイズ * @return 受信メッセージサイズ, エラー発生時は -1, 接続先が正しく shutdown した場合 0 */ ssize_t DatagramSocket::recvfrom( char* buff, size_t len, int flags, sockaddr* addr, socklen_t* addrLen) { return ::recvfrom(sockFd, buff, len, flags, addr, addrLen); } /** * ソケットをクローズします. */ void DatagramSocket::close() { // UDP なのでいきなり閉じちゃう ::socket_close(sockFd); } /** * TTL を設定します. * * @param val 値 */ void DatagramSocket::setTTL(int val) { int family = getSocketFamily(); int ret = -1; switch (family) { case AF_INET: unsigned char ttl; ttl = static_cast<unsigned char>(val); ret = setsockopt(sockFd, IPPROTO_IP, IP_MULTICAST_TTL, reinterpret_cast<char*>(&ttl), sizeof(ttl)); break; case AF_INET6: int hop; hop = val; ret = setsockopt(sockFd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, reinterpret_cast<char*>(&hop), sizeof(hop)); break; default: throw SocketException("unsupported socket family"); } if (ret != 0); { throw SocketException(); } } /** * マルチキャストグループに参加するために JOIN します. * * @param sa マルチキャストアドレス * @param salen マルチキャストアドレス構造体のサイズ * @param ifname インタフェース名 * @param ifindex インタフェースインデックス */ void DatagramSocket::join(const sockaddr* sa, socklen_t salen, const char* ifname, unsigned int ifindex) { int family = getSocketFamily(); switch (family) { case AF_INET: joinIPv4(reinterpret_cast<const sockaddr_in*>(sa), salen, ifname, ifindex); break; case AF_INET6: joinIPv6(reinterpret_cast<const sockaddr_in6*>(sa), salen, ifname, ifindex); break; default: // NOP break; } } /** * IPv4 マルチキャストグループに参加するために JOIN します. * * @param sa マルチキャストアドレス * @param salen マルチキャストアドレス構造体のサイズ * @param ifname インタフェース名 * @param ifindex インタフェースインデックス * */ void DatagramSocket::joinIPv4(const sockaddr_in* grp, socklen_t grplen , const char* ifname, unsigned int ifindex) { ip_mreq mreq; memcpy(&mreq.imr_multiaddr, &(reinterpret_cast<const sockaddr_in*>(grp))->sin_addr, sizeof(in_addr)); mreq.imr_interface.s_addr = htonl(INADDR_ANY); int ret = setsockopt(sockFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char*>(&mreq), sizeof(mreq)); if (ret != 0); { throw SocketException(); } } /** * IPv6 マルチキャストグループに参加するために JOIN します. * * @param sa マルチキャストアドレス * @param salen マルチキャストアドレス構造体のサイズ * @param ifname インタフェース名 * @param ifindex インタフェースインデックス * */ void DatagramSocket::joinIPv6(const sockaddr_in6* grp, socklen_t grplen, const char* ifname, unsigned int ifindex) { ipv6_mreq mreq6; memcpy(&mreq6.ipv6mr_multiaddr, &grp->sin6_addr, sizeof(in6_addr)); if (ifindex > 0) { mreq6.ipv6mr_interface = ifindex; } else { mreq6.ipv6mr_interface = 0; } int ret = setsockopt(sockFd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, reinterpret_cast<char*>(&mreq6), sizeof(mreq6)); if (ret != 0) { throw SocketException(); } } /** * マルチキャストグループから LEAVE します. * * @param sa マルチキャストアドレス * @param salen マルチキャストアドレス構造体のサイズ */ void DatagramSocket::leave(const sockaddr* sa, socklen_t salen) { int family = getSocketFamily(); switch (family) { case AF_INET: leaveIPv4(reinterpret_cast<const sockaddr_in*>(sa), salen); break; case AF_INET6: leaveIPv6(reinterpret_cast<const sockaddr_in6*>(sa), salen); break; default: // NOP break; } } /** * IPv4 マルチキャストグループから LEAVE します. * * @param sa マルチキャストアドレス * @param salen マルチキャストアドレス構造体のサイズ * */ void DatagramSocket::leaveIPv4(const sockaddr_in* grp, socklen_t grplen) { ip_mreq mreq; memcpy(&mreq.imr_multiaddr, &(reinterpret_cast<const sockaddr_in*>(grp))->sin_addr, sizeof(in_addr)); mreq.imr_interface.s_addr = htonl(INADDR_ANY); int ret = setsockopt(sockFd, IPPROTO_IP, IP_DROP_MEMBERSHIP, reinterpret_cast<char*>(&mreq), sizeof(mreq)); if (ret != 0); { throw SocketException(); } } /** * IPv6 マルチキャストグループから LEAVE します. * * @param sa マルチキャストアドレス * @param salen マルチキャストアドレス構造体のサイズ * */ void DatagramSocket::leaveIPv6(const sockaddr_in6* grp, socklen_t grplen) { ipv6_mreq mreq6; memcpy(&mreq6.ipv6mr_multiaddr, &grp->sin6_addr, sizeof(in6_addr)); mreq6.ipv6mr_interface = 0; int ret = setsockopt(sockFd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, reinterpret_cast<char*>(&mreq6), sizeof(mreq6)); if (ret != 0) { throw SocketException(); } } //////////////////////////////////////////////////////////////////////////////// // // SocketSet // /** * Select で用いるためのソケット集合を構築します. */ SocketSet::SocketSet() { reset(); } /** * SoketSet をリセットして, 保持ソケットを 0個の状態にします. */ void SocketSet::reset() { FD_ZERO(&fdset); maxfd = -1; } /** * ソケットを追加します. * 最大値を超えて追加しないでください. * * @param s ソケット */ void SocketSet::add(const AbstractSocket& s) { add(s.getSocket()); } /** * ソケットを追加します. * 最大値を超えて追加しないでください. * * @param sockfd ソケット */ void SocketSet::add(socket_t sockfd) { FD_SET(sockfd, &fdset); int sockfdVal = static_cast<int>(sockfd); if (sockfdVal > maxfd) { maxfd = sockfdVal; } } /** * ソケットを取り除きます. * * @param s 取り除くソケット */ void SocketSet::remove(const AbstractSocket& s) { remove(s.getSocket()); } /** * ソケットを取り除きます. * * @param sockfd 取り除くソケット */ void SocketSet::remove(socket_t sockfd) { FD_CLR(sockfd, &fdset); } /** * ソケットがこの集合に含まれていれば非0を返します. * * @param s ソケット * @return 非0/0 (含まれている/含まれていない) */ int SocketSet::isSet(const AbstractSocket& s) const { return isSet(s.getSocket()); } /** * ソケットがこの集合に含まれていれば非0を返します. * * @param sockfd ソケット * @return 非0/0 (含まれている/含まれていない) */ int SocketSet::isSet(socket_t sockfd) const { return FD_ISSET(sockfd, &fdset); } /** * SocketSet が保持しているファイルディスクリプタの最大値 + 1 の値を返します. * 本値は, select の第一引数で使用する値となります. * * @return ファイルディスクリプタの最大値 + 1 */ int SocketSet::selectn() const { return (maxfd + 1); } fd_set* SocketSet::getFdSet() { return &fdset; } //////////////////////////////////////////////////////////////////////////////// // // Select // /** * select. * * @param readSet 読み取り用セット * @param writeSet 書き込み用セット * @param errSet エラー発生用セット * @param timeout タイムアウト * @return 変化のあったディスクリプタ数, タイムアウト時には 0 を返す場合がある. エラーの場合 -1 を返す. */ int Select::select( SocketSet* readSet, SocketSet* writeSet, SocketSet* errSet, timeval* timeout) { fd_set* readfd = 0; fd_set* writefd = 0; fd_set* errfd = 0; int maxfd = 0; if (readSet != 0) { maxfd = readSet->selectn(); readfd = readSet->getFdSet(); } if (writeSet != 0) { maxfd = (writeSet->selectn() > maxfd) ? writeSet->selectn() : maxfd; writefd = writeSet->getFdSet(); } if (errSet != 0) { maxfd = (errSet->selectn() > maxfd) ? errSet->selectn() : maxfd; errfd = errSet->getFdSet(); } return ::select(maxfd, readfd, writefd, errfd, timeout); } } // namespace scpp