/** General Socket Wrapper * Created By Kiritow. (https://github.com/kiritow) * Licensed under MIT */ #ifndef _gsock_h #define _gsock_h #include #include #include #include int InitNativeSocket(); enum { GSOCK_OK = 0, GSOCK_API_ERROR = -1, // API call failed, See Errno GSOCK_INVALID_SOCKET = -2, // Invalid socket GSOCK_ERROR_CREAT = -3, // Socket cannot be created, See Errno GSOCK_INVALID_IP = -4, // Invalid IP Address (IPv4,IPv6) GSOCK_UNKNOWN_PROTOCOL = -5, // Unknown Protocol GSOCK_ERROR_NTOP = -6, // inet_ntop failed. GSOCK_MISMATCH_PROTOCOL = -7, // Protocol mismatch. GSOCK_BAD_PROTOCOL = -8, // Bad protocol. GSOCK_ERROR_SETMODE = -9, // Failed to set nonblocking GSOCK_MISMATCH_MODE = -10, // Example: calling blocking method on a non-blocking socket. }; // Internal Socket Call Errcode // Values of all errors are positive number. enum gerrno { OK = 0, UnknownError, WouldBlock, InProgress, Already, IsConnected, Interrupted, }; // For Debug purpose. int GetNativeErrCode(); gerrno TranslateNativeErrToGErr(int native_errcode); class vsock { public: int setNonblocking(); protected: vsock(); vsock(const vsock&)=delete; vsock& operator = (const vsock&)=delete; vsock(vsock&& v); vsock& operator = (vsock&& v); ~vsock(); //vsock(int); struct _impl; _impl* _vp; friend class selector; #ifdef WIN32 #else friend class epoll; #endif }; class NBConnectResult { public: NBConnectResult(); bool isFinished(); // Wait until the connection is finished. (via while loop) void wait(); bool isSuccess(); // ErrCode is only usable when the connection is finished and failed. gerrno getErrCode(); private: struct _impl; std::shared_ptr<_impl> _p; friend class sock; }; class NBSendResult { public: NBSendResult(); // Is the operation finished. bool isFinished(); // Wait until all data is sent. void wait(); // Is all data sent successfully. bool isSuccess(); int getBytesDone(); // If connection is closed while sending data, // the state changes to [Finished,Failed]. And errcode will be 0. gerrno getErrCode(); private: struct _impl; std::shared_ptr<_impl> _p; friend class sock; }; class NBRecvResult { public: NBRecvResult(); void setStopAtEdge(bool flag); bool isFinished(); void wait(); bool isSuccess(); int getBytesDone(); // If connection is closed while receiving data, // the state changes to [Finished,Failed]. And errcode will be 0. // If setStopAtEdge(true) is called and there's no more data while isFinished() is called, // the state changes to [Finished,Failed]. And errcode will be gerrno::WouldBlock gerrno getErrCode(); private: struct _impl; std::shared_ptr<_impl> _p; friend class sock; }; class sock : public vsock { public: // Return: // GSOCK_OK: Connection Established. No Error. // GSOCK_API_ERROR: connect() call error. See errno. // GSOCK_INVALID_SOCKET: This socket has been connected before. // GSOCK_ERROR_CREAT // GSOCK_INVALID_IP // GSOCK_ERROR_SETMODE: Failed to set socket to non-blocking. int connect(const std::string& IPStr,int Port); NBConnectResult connect_nb(const std::string& IPStr, int Port); // Return: // return what send() and recv() call returns. int send(const void* Buffer,int Length); int recv(void* Buffer, int MaxToRecv); NBSendResult send_nb(const void* Buffer, int Length); NBRecvResult recv_nb(void* Buffer, int MaxToRecv); // Return: // GSOCK_OK // GSOCK_API_ERROR int getsendtime(int& _out_Second,int& _out_uSecond); int getrecvtime(int& _out_Second,int& _out_uSecond); int setsendtime(int Second,int Millisecond); int setrecvtime(int Second,int Millisecond); int setkeepalive(bool op); // Return: // 0: Success. No Error. IPv4 // 1: Success. No Error. IPv6 // GSOCK_API_ERROR: getlocalname() or getpeername() call error. See errno. // GSOCK_INVALID_SOCKET: Socket not created. int getpeer(std::string& IPStr, int& Port); int getlocal(std::string& IPStr,int& Port); friend class serversock; private: struct _impl; }; class NBAcceptResult { public: NBAcceptResult(); bool isFinished(); bool isSuccess(); sock& get(); gerrno getErrCode(); private: struct _impl; std::shared_ptr<_impl> _sp; friend class serversock; }; class serversock : public vsock { public: // use_family: // 0: Auto (Undecided now) (default) // 1: IPv4 (If family cannot be automatically decided, then IPv4 will be the default option) // 2: IPv6 serversock(int use_family=0); ~serversock(); // Notice that bind() should be called before setNonblocking() // Return: // GSOCK_OK: Bind Succeed. No Error. // GSOCK_API_ERROR: bind() call error. See errno. // GSOCK_INVALID_SOCKET: This socket has been created before. // GSOCK_ERROR_CREAT int bind(int Port); // Return: // GSOCK_OK // GSOCK_ERROR_CREAT // GSOCK_API_ERROR: setsockopt() call error. int set_reuse(); // Notice that listen() should be called before setNonblocking() // Return: // GSOCK_OK // GSOCK_API_ERROR: listen() call error. // GSOCK_INVALID_SOCKET int listen(int MaxCount); // Return: // GSOCK_OK: Accept Succeed. No Error. _out_s holds the new socket. // GSOCK_API_ERROR: accept() call error. See errno. // GSOCK_INVALID_SOCKET: _out_s is not an empty socket, which should not be passed in. int accept(sock& _out_s); // Notice that bind() and listen() should be called before setNonBlocking() NBAcceptResult accept_nb(sock& _out_s); private: struct _impl; _impl* _pp; }; class udpsock : public vsock { public: // use_family: // 0: Auto (Undecided now) (default) // 1: IPv4 (If family cannot be automatically decided, then IPv4 will be the default option) // 2: IPv6 udpsock(int use_family=0); ~udpsock(); // Use udp socket as tcp socket. (but of course it is not). // connect call just copy the target socket data to kernel. See connect() for more info. // Return: // GSOCK_OK: data copied. // GSOCK_API_ERROR: connect() call error. // GSOCK_INVALID_IP // GSOCK_MISMATCH_PROTOCOL // GSOCK_INVALID_SOCKET // GSOCK_ERROR_CREAT int connect(const std::string& IPStr,int Port); // Return: // Besides all returns of connect(...), adding the following: // GSOCK_BAD_PROTOCOL: broadcast is not supported. int broadcast_at(int Port); // Must be called in broadcast mode before any broadcasting. // Return: // GSOCK_OK // GSOCK_MISMATCH_PROTOCOL // GSOCK_INVALID_SOCKET // GSOCK_ERROR_CREAT int set_broadcast(); // Explict bind() call is only need when you have to receive data. // Return: // GSOCK_OK // GSOCK_MISMATCH_PROTOCOL // GSOCK_INVALID_SOCKET // GSOCK_ERROR_CREAT int bind(int Port); // Return: // ret>=0: sendto() returns // GSOCK_API_ERROR(-1): sendto() call error. // GSOCK_INVALID_IP // GSOCK_MISMATCH_PROTOCOL // GSOCK_INVALID_SOCKET // GSOCK_ERROR_CREAT int sendto(const std::string& IPStr, int Port, const void* buffer, int length); // Return: // Besides all returns of sendto(...), adding the following: // GSOCK_BAD_PROTOCOL: broadcast is not supported. int broadcast(int Port,const void* buffer,int length); // Must call bind() before calling recvfrom(). // Return: // ret>=0: recvfrom() returns // GSOCK_API_ERROR(-1): recvfrom() call error. // GSOCK_ERROR_NTOP // GSOCK_UNKNOWN_PROTOCOL // GSOCK_MISMATCH_PROTOCOL // GSOCK_INVALID_SOCKET // GSOCK_ERROR_CREAT int recvfrom(std::string& fromIP, int& fromPort, void* buffer, int bufferLength); // send() and recv() should only be called after connect(). Or it will fail. // Return: // ret>=0: send(), recv() returns. // GSOCK_API_ERROR(-1): send(), recv() call error. // GSOCK_INVALID_SOCKET: socket not created, and connect() has not been called yet. int send(const void* buffer,int length); int recv(void* buffer,int bufferLength); private: struct _impl; _impl* _pp; }; /// Select class selector { public: selector(); ~selector(); void clear(); void add_read(const vsock&); void add_write(const vsock&); void add_error(const vsock&); int wait_for(int second, int ms = 0); int wait(); bool can_read(const vsock&); bool can_write(const vsock&); bool is_error(const vsock&); private: struct _impl; _impl* _pp; }; #ifdef WIN32 // Windows: IOCP. Coming soon... #else // Linux: epoll #include #include class epoll { public: epoll(int MaxListen); // EPOLLIN, EPOLLOUT, ... int add(vsock& v,int event); int mod(vsock& v,int event); int del(vsock& v,int event); // >0: Event counts. // =0: Timeout. // <0: Error. // Set timeout to -1 for infinity waiting. // Call handle() to handle events int wait(int timeout); // callback: void event_handler(vsock& s,int event) void handle(const std::function& callback); ~epoll(); private: std::vector _evec; int _n; int _fd; }; #endif // End of Platform specific /// Net Tools // Return: // >=0: Number of fetched results from getaddrinfo() call. // -1: getaddrinfo() call failed. int DNSResolve(const std::string& HostName, std::vector& _out_IPStrVec); // A wrapper of the vector version of DNSResolve. // _out_IPStr will be assigned with the first result in vector. // Return: // 0: Success. // -1: getaddrinfo() call failed. // -2: Failed to resolve. (No results in vector) int DNSResolve(const std::string& HostName,std::string& _out_IPStr); #endif // _gsock_h