/* ============================================================================= * scpp_socket.hpp * Copyright (c) 2003 - 2013 Nomura Kei * LICENSE : * LGPL (GNU Lesser General General Public License - Version 3,29 June 2007) * http://www.gnu.org/copyleft/lesser.html * ============================================================================= * * ソケットを扱うモジュール */ #ifndef SCPP_SOCKET_HPP #define SCPP_SOCKET_HPP #include <cstring> #include <scpp_os.hpp> #include <scpp_exception.hpp> #include <scpp_errno.hpp> #if (SCPP_IS_WINDOWS) #include <ws2tcpip.h> #ifdef _MSV_VER #pragma comment(lib, "ws2_32.lib") #endif typedef SOCKET socket_t; #define SHUT_RD SD_RECEIVE #define SHUT_WR SD_SEND #define SHUT_RDWR SD_BOTH #define socket_close(sockfd) closesocket(sockfd) #else // ! SCPP_IS_WINDOWS #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> #include <stdlib.h> #include <unistd.h> typedef int socket_t; #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) #define socket_close(sockfd) close(sockfd) #endif namespace scpp { /** * Socket に関するエラーが発生した際に throw される Exception クラスです. */ class SocketException : public Exception { public: SocketException() throw(); SocketException(const SocketException& t) throw(); SocketException(const std::string& msg) throw(); virtual ~SocketException() throw(); }; /** * 抽象ソケットクラス. * TCP の場合は, Socket クラスを, * UDP の場合は, DatagramSocket クラスを使用してください. */ class AbstractSocket { public: AbstractSocket(int type, int family = AF_UNSPEC); virtual ~AbstractSocket(); AbstractSocket& operator=(const AbstractSocket& sock); virtual const char* getAddress(char* addr, size_t addrLen) const; virtual int getPort() const; virtual const char* getLocalAddress(char* addr, size_t addrLen) const; virtual int getLocalPort() const; virtual const socket_t getSocket() const; virtual const int getSocketType() const; virtual const int getSocketFamily() const; virtual void bind(const char* addr, int port); virtual void bind(const char* addr, const char* service) = 0; protected: virtual void getAddrInfo(addrinfo** result, const char* addr, const char* service, bool isPassive); virtual void clone(const AbstractSocket& sock); socket_t sockFd; int sockType; int sockFamily; sockaddr_storage remoteAddr; socklen_t remoteAddrLen; sockaddr_storage localAddr; socklen_t localAddrLen; }; /** * TCP ソケットを扱います. * * @code * 使用例) サーバ側の例 * Socket server(AF_INET); // IPv4 を扱う * sock->bind("127.0.0.1", "5000"); // 127.0.0.1:5000 番ポートにバインドする * sock->listen(10); // バックログ 10 * * SocketSet readSet; * timeval timeout; * int ret; * ssize_t recvSize; * Socket client; * char buff[1024]; * * for (;;) * { * // select で扱うディスクリプタのセットを初期化 * readSet.reset(); * readSet.add(server); * * // タイムアウト時間設定 * timeout.tv_sec = 0; * timeout.tv_usec = 100000; // 100ms * * ret = Select::select(&readSet, 0, 0, &timeout); * if (ret > 0) * { * if (readSet.isSet(server)) * { // 接続された! * server.accept(&client); * * // クライアントソケットに対する処理 * // 例) データを受信 * recvService = client.recv(buff, sizeof(buff)); * client.close(); * } * } * } * * @endcode */ class Socket : public AbstractSocket { public: Socket(int family = AF_UNSPEC); Socket(const Socket&); virtual ~Socket(); Socket& operator=(const Socket&); virtual void bind(const char* addr, const char* service); virtual void listen(int backlog); virtual void accept(Socket* clientSocket); virtual void connect(const char* addr, int port, const char* loAddr= 0, int loPort = -1); virtual void connect(const char* addr, const char* service, const char* loAddr = 0, const char* loService = 0); virtual void close(); virtual ssize_t send(const char* buff, size_t size, int flag = 0); virtual ssize_t recv(char* buff, size_t size, int flag = 0); virtual int write(const char* buff, size_t size); virtual int read(char* buff, size_t size); protected: private: void bindAndConnect(const char* addr, const char* service, const char* loAddr, const char* loService); bool connect(socket_t sock, const char* addr, const char* service); }; /** * UDP ソケットを扱います. * * @code * 使用例) サーバ側の例 * DatagramSocket server(AF_INET); // IPv4 を扱う * sock->bind("127.0.0.1", "5000"); // 127.0.0.1:5000 番ポートにバインドする * * SocketSet readSet; * timeval timeout; * int ret; * * ssize_t recvSize; * char buff[1024]; * * for (;;) * { * // select で扱うディスクリプタのセットを初期化 * readSet.reset(); * readSet.add(server); * * // タイムアウト時間設定 * timeout.tv_sec = 0; * timeout.tv_usec = 100000; // 100ms * * ret = Select::select(&readSet, 0, 0, &timeout); * if (ret > 0) * { * if (readSet.isSet(server)) * { // データを受信した! * // データ受信時の処理 * } * } * } * * @endcode */ class DatagramSocket : public AbstractSocket { public: DatagramSocket(int family = AF_UNSPEC); DatagramSocket(const DatagramSocket&); virtual ~DatagramSocket(); DatagramSocket& operator=(const DatagramSocket&); virtual void bind(const char* addr, const char* service); virtual ssize_t sendto (const char* buff, size_t len, int flags, const char* addr , int portf); virtual ssize_t sendto (const char* buff, size_t len, int flags, const char* addr , const char* service); virtual ssize_t sendto (const char* buff, size_t len, int flags, const sockaddr* addr, socklen_t addrlen); virtual ssize_t recvfrom( char* buff, size_t len, int flags, char* srcAddr, size_t srcAddrLen, char* srcPort, size_t srcPortLen); virtual ssize_t recvfrom( char* buff, size_t len, int flags, sockaddr* srcAddr, socklen_t* addrlen); virtual void close(); virtual void join(const sockaddr* sa, socklen_t salen, const char* ifname, unsigned int ifindex); virtual void leave(const sockaddr* sa, socklen_t salen); virtual void setTTL(int val); protected: private: void joinIPv4(const sockaddr_in* grp, socklen_t grplen, const char* ifname, unsigned int ifindex); void joinIPv6(const sockaddr_in6* grp, socklen_t grplen, const char* ifname, unsigned int ifindex); void leaveIPv4(const sockaddr_in* grp, socklen_t grplen); void leaveIPv6(const sockaddr_in6* grp, socklen_t grplen); }; /** * Select で用いるための ソケット集合を表します. */ class SocketSet { public: SocketSet(); void reset(); void add(const AbstractSocket& s); void add(socket_t sockfd); void remove(const AbstractSocket& s); void remove(socket_t sockfd); int isSet(const AbstractSocket& s) const; int isSet(socket_t sockfd) const; int selectn() const; fd_set* getFdSet(); private: SocketSet(const SocketSet&); SocketSet& operator=(const SocketSet&); fd_set fdset; int maxfd; }; namespace Select { int select(SocketSet* readSet, SocketSet* writeSet, SocketSet* errSet, timeval* timeout); } // namespace Select } // namespace scpp #endif // SCPP_SOCKET_HPP