From a9a24d8f8098d85bf6e1ca30a1ed0d782c78eb19 Mon Sep 17 00:00:00 2001 From: "HuYingzhuo(hugo/hyzboy)" Date: Wed, 25 May 2022 19:03:06 +0800 Subject: [PATCH] second commit --- CMakeLists.txt | 11 + inc/hgl/network/AcceptServer.h | 48 ++ inc/hgl/network/DirectSocketIOUserThread.h | 108 +++ inc/hgl/network/HTTPInputStream.h | 76 ++ inc/hgl/network/HTTPTools.h | 35 + inc/hgl/network/IOSocket.h | 114 +++ inc/hgl/network/IP.h | 440 +++++++++++ .../network/JavaDirectSocketIOUserThread.h | 17 + inc/hgl/network/MTTCPServer.h | 163 ++++ inc/hgl/network/MultiThreadAccept.h | 88 +++ inc/hgl/network/SCTPClient.h | 28 + inc/hgl/network/SCTPServer.h | 48 ++ inc/hgl/network/SCTPSocket.h | 76 ++ inc/hgl/network/ServerSocket.h | 51 ++ inc/hgl/network/Socket.h | 155 ++++ inc/hgl/network/SocketEvent.h | 24 + inc/hgl/network/SocketInputStream.h | 56 ++ inc/hgl/network/SocketManage.h | 68 ++ inc/hgl/network/SocketManageThread.h | 153 ++++ inc/hgl/network/SocketOutputStream.h | 54 ++ inc/hgl/network/TCPAccept.h | 83 ++ inc/hgl/network/TCPClient.h | 52 ++ inc/hgl/network/TCPServer.h | 26 + inc/hgl/network/TCPSocket.h | 43 ++ inc/hgl/network/UdpSocket.h | 104 +++ inc/hgl/network/WebSocket.h | 13 + inc/hgl/network/WebSocketAccept.h | 82 ++ inc/hgl/network/WinFireWall.h | 92 +++ path_config.cmake | 9 + src/AcceptServer.cpp | 80 ++ src/CMakeLists.txt | 107 +++ src/HTTPInputStream.cpp | 271 +++++++ src/HTTPOutputStream.cpp | 66 ++ src/HTTPTools.cpp | 85 +++ src/IPAddress.cpp | 434 +++++++++++ src/MultiThreadAccept.cpp | 65 ++ src/SCTPO2MServer.cpp | 72 ++ src/SCTPO2OClient.cpp | 75 ++ src/SCTPO2OServer.cpp | 12 + src/SCTPSocket.cpp | 227 ++++++ src/ServerSocket.cpp | 88 +++ src/Socket.cpp | 497 ++++++++++++ src/SocketInputStream.cpp | 249 ++++++ src/SocketManage.cpp | 223 ++++++ src/SocketManageBase.h | 35 + src/SocketManageEpoll.cpp | 255 +++++++ src/SocketManageSelect.cpp | 150 ++++ src/SocketOutputStream.cpp | 178 +++++ src/TCPAccept.cpp | 34 + src/TCPAcceptPacket.cpp | 104 +++ src/TCPClient.cpp | 167 ++++ src/TCPServer.cpp | 34 + src/TCPSocket.cpp | 123 +++ src/UdpLiteSocket.cpp | 89 +++ src/UdpSocket.cpp | 167 ++++ src/WebApi_Currency.cpp | 32 + src/WebSocket.cpp | 105 +++ src/WebSocketAccept.cpp | 352 +++++++++ src/WebapiUserAgent.cpp | 68 ++ src/WinFireWall.cpp | 721 ++++++++++++++++++ 60 files changed, 7482 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 inc/hgl/network/AcceptServer.h create mode 100644 inc/hgl/network/DirectSocketIOUserThread.h create mode 100644 inc/hgl/network/HTTPInputStream.h create mode 100644 inc/hgl/network/HTTPTools.h create mode 100644 inc/hgl/network/IOSocket.h create mode 100644 inc/hgl/network/IP.h create mode 100644 inc/hgl/network/JavaDirectSocketIOUserThread.h create mode 100644 inc/hgl/network/MTTCPServer.h create mode 100644 inc/hgl/network/MultiThreadAccept.h create mode 100644 inc/hgl/network/SCTPClient.h create mode 100644 inc/hgl/network/SCTPServer.h create mode 100644 inc/hgl/network/SCTPSocket.h create mode 100644 inc/hgl/network/ServerSocket.h create mode 100644 inc/hgl/network/Socket.h create mode 100644 inc/hgl/network/SocketEvent.h create mode 100644 inc/hgl/network/SocketInputStream.h create mode 100644 inc/hgl/network/SocketManage.h create mode 100644 inc/hgl/network/SocketManageThread.h create mode 100644 inc/hgl/network/SocketOutputStream.h create mode 100644 inc/hgl/network/TCPAccept.h create mode 100644 inc/hgl/network/TCPClient.h create mode 100644 inc/hgl/network/TCPServer.h create mode 100644 inc/hgl/network/TCPSocket.h create mode 100644 inc/hgl/network/UdpSocket.h create mode 100644 inc/hgl/network/WebSocket.h create mode 100644 inc/hgl/network/WebSocketAccept.h create mode 100644 inc/hgl/network/WinFireWall.h create mode 100644 path_config.cmake create mode 100644 src/AcceptServer.cpp create mode 100644 src/CMakeLists.txt create mode 100644 src/HTTPInputStream.cpp create mode 100644 src/HTTPOutputStream.cpp create mode 100644 src/HTTPTools.cpp create mode 100644 src/IPAddress.cpp create mode 100644 src/MultiThreadAccept.cpp create mode 100644 src/SCTPO2MServer.cpp create mode 100644 src/SCTPO2OClient.cpp create mode 100644 src/SCTPO2OServer.cpp create mode 100644 src/SCTPSocket.cpp create mode 100644 src/ServerSocket.cpp create mode 100644 src/Socket.cpp create mode 100644 src/SocketInputStream.cpp create mode 100644 src/SocketManage.cpp create mode 100644 src/SocketManageBase.h create mode 100644 src/SocketManageEpoll.cpp create mode 100644 src/SocketManageSelect.cpp create mode 100644 src/SocketOutputStream.cpp create mode 100644 src/TCPAccept.cpp create mode 100644 src/TCPAcceptPacket.cpp create mode 100644 src/TCPClient.cpp create mode 100644 src/TCPServer.cpp create mode 100644 src/TCPSocket.cpp create mode 100644 src/UdpLiteSocket.cpp create mode 100644 src/UdpSocket.cpp create mode 100644 src/WebApi_Currency.cpp create mode 100644 src/WebSocket.cpp create mode 100644 src/WebSocketAccept.cpp create mode 100644 src/WebapiUserAgent.cpp create mode 100644 src/WinFireWall.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ecb42b4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.0) + +project(CMNetwork) + +include(path_config.cmake) +CMAudioSetup(${CMAKE_CURRENT_SOURCE_DIR}) + +add_subdirectory(${CMNETWORK_ROOT_SOURCE_PATH}) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Plug-Ins) + diff --git a/inc/hgl/network/AcceptServer.h b/inc/hgl/network/AcceptServer.h new file mode 100644 index 0000000..08a5158 --- /dev/null +++ b/inc/hgl/network/AcceptServer.h @@ -0,0 +1,48 @@ +#ifndef HGL_NETWORK_ACCEPT_SERVER_INCLUDE +#define HGL_NETWORK_ACCEPT_SERVER_INCLUDE + +#include +namespace hgl +{ + namespace network + { + /** + * 使用Accept处理接入的服务器基类 + */ + class AcceptServer:public ServerSocket ///使用Accept创建接入的服务器基类 + { + char *ipstr; + + fd_set accept_set; + struct timeval accept_timeout,ato; + + protected: + + virtual int CreateServerSocket()=0; ///<创建Server Socket + + protected: + + double overload_wait; + + public: + + AcceptServer() + { + overload_wait=HGL_SERVER_OVERLOAD_RESUME_TIME; + ipstr = nullptr; + + FD_ZERO(&accept_set); + hgl_zero(accept_timeout); + + SetTimeOut(HGL_NETWORK_TIME_OUT); + } + + virtual ~AcceptServer(){SAFE_CLEAR(ipstr);} + + void SetTimeOut(const double); + + virtual int Accept(IPAddress *); ///<接入一个socket连接 + };//class AcceptServer + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_ACCEPT_SERVER_INCLUDE diff --git a/inc/hgl/network/DirectSocketIOUserThread.h b/inc/hgl/network/DirectSocketIOUserThread.h new file mode 100644 index 0000000..864219f --- /dev/null +++ b/inc/hgl/network/DirectSocketIOUserThread.h @@ -0,0 +1,108 @@ +#ifndef HGL_NETWORK_DIRECT_SOCKET_IO_USER_THREAD_INCLUDE +#define HGL_NETWORK_DIRECT_SOCKET_IO_USER_THREAD_INCLUDE + +#include + +#ifdef HGL_NETWORK_SCTP_SUPPORT +#include +#endif//HGL_NETWORK_SCTP_SUPPORT + +#include +#include +#include +#include +#include + +namespace hgl +{ + using namespace hgl::io; + + namespace network + { + template + class DirectSocketIOUserThread:public Thread + { + bool IsExitDelete()const override {return true;} ///<返回在退出线程时,是否删除本对象 + + protected: + + int sock; + IPAddress *addr; + S *s; + SocketInputStream *sis; + SocketOutputStream *sos; + DataInputStream *dis; + DataOutputStream *dos; + + public: + + bool block; ///<是否使用阻塞模式 + double block_send_time; ///<阻塞时间 + double block_recv_time; + + double wait_time; ///<数据等待时间 + + public: + + DirectSocketIOUserThread(int s,const IPAddress *sa) + { + sock=s; + addr=sa->CreateCopy(); + + block=true; + block_recv_time=1; + block_send_time=1; + + wait_time=10; + } + + virtual bool ProcStartThread() override + { + s=new S(sock,addr); + + sis=new SocketInputStream(sock); + sos=new SocketOutputStream(sock); + dis=new DIS(sis); + dos=new DOS(sos); + + s->SetBlock(block,block_send_time,block_recv_time); + + return(true); + } + + virtual ~DirectSocketIOUserThread() + { + SAFE_CLEAR(addr); + SAFE_CLEAR(dis); + SAFE_CLEAR(dos); + SAFE_CLEAR(sos); + SAFE_CLEAR(sis); + SAFE_CLEAR(s); + } + + virtual bool Update()=0; + virtual bool IdleUpdate(){return(true);} + + virtual bool Execute() override + { + int wr=s->WaitRecv(wait_time); + + if(wr<0) + RETURN_FALSE; + + if(wr==0) + return IdleUpdate(); + + return Update(); + } + };//class DirectSocketIOUserThread + + typedef DirectSocketIOUserThread LETCPSocketIOUserThread; + typedef DirectSocketIOUserThread BETCPSocketIOUserThread; +#ifdef HGL_NETWORK_SCTP_SUPPORT + typedef DirectSocketIOUserThread LESCTPSocketIOUserThread; + typedef DirectSocketIOUserThread BESCTPSocketIOUserThread; +#endif//HGL_NETWORK_SCTP_SUPPORT + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_DIRECT_SOCKET_IO_USER_THREAD_INCLUDE diff --git a/inc/hgl/network/HTTPInputStream.h b/inc/hgl/network/HTTPInputStream.h new file mode 100644 index 0000000..4170492 --- /dev/null +++ b/inc/hgl/network/HTTPInputStream.h @@ -0,0 +1,76 @@ +#ifndef HGL_NETWORK_HTTP_INPUT_STREAM_INCLUDE +#define HGL_NETWORK_HTTP_INPUT_STREAM_INCLUDE + +#include +#include +#include +namespace hgl +{ + namespace network + { + using namespace io; + class TCPClient; + + /** + * HTTPInputStream流是一个针对HTTP服务器的流式访问类,用它可以从HTTP服务器上下载文件,它从InputStream类派生。
+ * 需要注意的是,这个类只能读,不能写。Position和Size也只能读不能修改
+ */ + class HTTPInputStream:public io::InputStream ///HTTP流式访问类 + { + TCPClient *tcp; + + InputStream *tcp_is; + + private: + + char *http_header; + uint http_header_size; + + void ParseHttpResponse(); + int PraseHttpHeader(); + + int ReturnError(); + + protected: + + int64 pos; + int64 filelength; + + uint response_code; //HTTP响应代码 + UTF8String response_info; //HTTP响应信息 + + UTF8PList response_list; //响应信息 + + public: + + HTTPInputStream(); + ~HTTPInputStream(); + + bool Open(IPAddress *,const UTF8String &,const UTF8String &); ///<打开一个网址 + void Close() override; ///< + + uint GetResponseCode()const{return response_code;} ///<返回HTTP响应代码 + const UTF8String & GetResponseInfo()const{return response_info;} ///<返回HTTP响应信息 + const UTF8PList & GetResponseList()const{return response_list;} ///<返回HTTP响应信息列表 + + int64 Read(void *,int64) override; ///<读取数据 + int64 Peek(void *,int64) override{return 0;} ///<预览数据 + int64 ReadFully(void *buf,int64 buf_size)override{return Read(buf,buf_size);} ///<充分读取,保证读取到指定长度的数据(不计算超时) + + bool CanRestart()const override{return false;} ///<是否可以复位 + bool CanSeek()const override{return false;} ///<是否可以定位 + bool CanSize()const override{return false;} ///<是否可以取得尺寸 + bool CanPeek()const override{return false;} ///<是否可以预览数据 + + bool Restart() override{return false;} ///<复位访问指针 + int64 Skip(int64) override{return 0;} ///<跳过指定字节不访问 + int64 Seek(int64,SeekOrigin=soBegin) override {return false;} ///<移动访问指针 + int64 Tell()const override{return pos;} ///<返回当前访问位置 + int64 GetSize()const override{return filelength;} ///<取得流长度 + int64 Available()const override{return filelength-pos;} ///<剩下的可以不受阻塞访问的字节数 + };//class HTTPInputStream + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_NETWORK_HTTP_INPUT_STREAM_INCLUDE diff --git a/inc/hgl/network/HTTPTools.h b/inc/hgl/network/HTTPTools.h new file mode 100644 index 0000000..147adc6 --- /dev/null +++ b/inc/hgl/network/HTTPTools.h @@ -0,0 +1,35 @@ +#ifndef HGL_NETWORK_HTTP_TOOLS_INCLUDE +#define HGL_NETWORK_HTTP_TOOLS_INCLUDE + +#include +#include +namespace hgl +{ + namespace network + { + /** + * 网络通信中HTTP/HTTPS所使用的功能实现 + */ + namespace http + { + /** + * 建立一个HTTP/HTTPS链接,并发送GET操作得到返回信息
+ * 注:返回信息会包含HTTP信息头,用于下载文件时,需自行将信息头去掉 + */ + int get(io::OutputStream *,const char *,const char *user_agent=nullptr); /// + +namespace hgl +{ + namespace network + { + /** + * 输入输出用Socket + */ + class IOSocket:public Socket ///传输数据用Socket基类 + { + protected: + + int64 send_total; ///<发送字节数统计 + int64 recv_total; ///<接收字节数统计 + + double recv_time_out; ///<数据接收超时 + double last_recv_time; ///<最后一次接收数据的时间 + + public: + + IOSocket() + { + Clear(); + } + + virtual ~IOSocket()=default; + + virtual void Clear() + { + send_total=0; + recv_total=0; + recv_time_out=HGL_NETWORK_DOUBLE_TIME_OUT; + last_recv_time=0; + } + + virtual void CloseSocket()override ///<关闭连接 + { + Socket::CloseSocket(); + Clear(); + } + + virtual const double &GetRecvTimeOut()const{return recv_time_out;} ///<取得接收数据超时时间 + virtual void SetRecvTimeOut(const double to){recv_time_out=to;} ///<设置接收数据超时时间 + + public: //事件函数 + + /** + * 接收数据处理事件函数 + * @param recv_buf_size 缓冲区内的数据长度(注:此参数并不是所有平台都可以获取,所以在值为-1时,需要不停的获取数据直到出错) + * @param ct 当前时间 + * @return 成功获取的数据字节数 + */ + virtual int OnRecv(int recv_buf_size=-1,const double ct=0) + { + last_recv_time=ct; + return 0; + } + + /** + * 发送数据处理事件函数 + * @param send_buf_size 发送数据缓冲区内可用最大长度(注:此参数并不是所有平台都可以获取,所以在值为-1时,依然可以发送数据) + * @param left_bytes 剩余未发送的字节数(如果为0,下一次将不参与轮循。此功能待测试是否需要,因为将SOCKET从列表中反复添加删除,也会影响效能) + * @return 成功发送的数据字节数 + */ + virtual int OnSend(int send_buf_size/*,int &left_bytes*/) + { + return 0; + } + + /** + * 错误处理事件函数 + * @param errno_number 错误号 + */ + virtual void OnError(int errno_number) + { + } + + /** + * 关闭事件函数 + */ + virtual void OnClose() + { + } + + /** + * 刷新事件函数,由开发者重写实现。这里故意写为纯虚函数,是为了提醒开发者此函数的存在,即便什么都不做,也必须由开发者自行实现。 + * @return 是否正常,如返回否则会让管理器认为此socket出错需要退出 + */ + virtual bool OnUpdate()=0; + + public: //属性函数 + + const int64 GetSendTotal()const{return send_total;} ///<取得发送字节数统计 + const int64 GetRecvTotal()const{return recv_total;} ///<取得接收字节数统计 + + const void RestartLastRecvTime(){last_recv_time=0;} ///<复位最后获取数据时间 + const double GetLastRecvTime()const{return last_recv_time;} ///<取得最后获取数据时间 + const bool CheckRecvTimeOut(const double ct) ///<检测是否超时 + { + if((last_recv_time>0) + &&(last_recv_time+recv_time_out + +#if HGL_OS == HGL_OS_Windows + #include + + #if SOMAXCONN == 5 + #error Please use + #endif// + + typedef int socklen_t; + typedef ULONG in_addr_t; + + #define GetLastSocketError() WSAGetLastError() + + #ifndef SOCK_DCCP + #define SOCK_DCCP 6 + #endif//SOCK_DCCP + + #ifndef SOCK_PACKET + #define SOCK_PACKET 10 + #endif//SOCK_PACKET + + #ifndef IPPROTO_DCCP + #define IPPROTO_DCCP 33 + #endif//IPPROTO_DCCP + + #ifndef IPPROTO_UDPLITE + #define IPPROTO_UDPLITE 136 + #endif//IPPROTO_UDPLITE +#else + #include + #include + #include + #include + #include + #include + #include + #include + + #define GetLastSocketError() (errno) + + #if HGL_OS == HGL_OS_Linux + #include + + inline int sendfile(int tfd,int sfd,size_t size) + { + return sendfile(tfd,sfd,nullptr,size); + } + #endif//HGL_OS == HGL_OS_Linux + + #if HGL_OS == HGL_OS_FreeBSD + #include + + inline int sendfile(int tfd,int sfd,size_t size) + { + return sendfile(tfd,sfd,0,size,nullptr,nullptr,0); + } + #endif//HGL_OS == HGL_OS_FreeBSD + + #if (HGL_OS == HGL_OS_macOS)||(HGL_OS == HGL_OS_iOS) + #ifndef IPPROTO_UDPLITE + #define IPPROTO_UDPLITE 136 + #endif//IPPROTO_UDPLITE + #endif// +#endif//HGL_OS == HGL_OS_Windows + +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + /** + * IP类型枚举 + */ + enum IPType + { + iptNone=0, + + iptV4, + iptV6, + + iptEnd + };//enum IPType + + inline IPType CheckIPType(const char *name) + { + if(!name)return(iptNone); + + while(*name) + { + if(*name==':')return(iptV6); + if(*name=='.')return(iptV4); + + ++name; + } + + return(iptNone); + } + + struct IPSupport + { + uint family; ///<协议家族:AF_INET,AF_INET6,AF_NETBIOS + uint socktype; /// &); ///<取得本机IP支持列表 + bool CheckIPSupport(const List &ips_list,uint family,uint socktype,uint protocol); + bool CheckIPSupport(uint family,uint socktype,uint protocol); + + inline bool CheckIPv4SupportTCP (){return CheckIPSupport(AF_INET,SOCK_STREAM, IPPROTO_TCP );} + inline bool CheckIPv4SupportUDP (){return CheckIPSupport(AF_INET,SOCK_DGRAM, IPPROTO_UDP );} + inline bool CheckIPv4SupportUDPLite (){return CheckIPSupport(AF_INET,SOCK_DGRAM, IPPROTO_UDPLITE );} + inline bool CheckIPv4SupportSCTP (){return CheckIPSupport(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP );} + + inline bool CheckIPv6SupportTCP (){return CheckIPSupport(AF_INET6,SOCK_STREAM, IPPROTO_TCP );} + inline bool CheckIPv6SupportUDP (){return CheckIPSupport(AF_INET6,SOCK_DGRAM, IPPROTO_UDP );} + inline bool CheckIPv6SupportUDPLite (){return CheckIPSupport(AF_INET6,SOCK_DGRAM, IPPROTO_UDPLITE );} + inline bool CheckIPv6SupportSCTP (){return CheckIPSupport(AF_INET6,SOCK_SEQPACKET,IPPROTO_SCTP );} + + /** + * IP地址类 + */ + class IPAddress + { + protected: + + int socktype; + int protocol; + + const char *protocol_name; + + void RefreshProtocolName(); + + public: + + IPAddress() + { + socktype=0; + protocol=0; + protocol_name=nullptr; + } + + IPAddress(int s,int p) + { + socktype=s; + protocol=p; + + RefreshProtocolName(); + } + + virtual ~IPAddress()=default; + + virtual const int GetFamily()const=0; ///<返回网络家族 + const int GetSocketType()const{return socktype;} ///<返回Socket类型 + const int GetProtocol()const{return protocol;} ///<返回协议类型 + virtual const uint GetIPSize()const=0; ///<取得IP地址的长度 + virtual const uint GetSockAddrInSize()const=0; ///<取得SockAddrIn变量长度 + virtual const uint GetIPStringMaxSize()const=0; ///<取得IP字符串最大长度 + + virtual const bool IsBoradcast()const=0; ///<是否为广播 + + virtual const char *GetProtocolName()const{return protocol_name;} ///<取得协议名称 + + /** + * 设置IP地址 + * @param _name 域名或地址字符串 + * @param _port 端口号 + * @param socktype Socket类型(可以为SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_RDM、SOCK_SEQPACKET等值),默认为所有类型。 + * @param protocol 协议类型(可以为IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP),默认为所有类型。 + */ + virtual bool Set(const char *_name,ushort _port,int socktype,int protocol)=0; + + bool SetTCP (const char *_name,ushort _port){return Set(_name,_port,SOCK_STREAM, IPPROTO_TCP );} + bool SetUDP (const char *_name,ushort _port){return Set(_name,_port,SOCK_DGRAM, IPPROTO_UDP );} + bool SetUDPLite (const char *_name,ushort _port){return Set(_name,_port,SOCK_DGRAM, IPPROTO_UDPLITE );} + bool SetSCTP (const char *_name,ushort _port){return Set(_name,_port,SOCK_SEQPACKET, IPPROTO_SCTP );} + + const bool IsTCP ()const{if(socktype!=SOCK_STREAM )return(false);if(protocol!=IPPROTO_TCP )return(false);return(true);} + const bool IsUDP ()const{if(socktype!=SOCK_DGRAM )return(false);if(protocol!=IPPROTO_UDP )return(false);return(true);} + const bool IsUDPLite ()const{if(socktype!=SOCK_DGRAM )return(false);if(protocol!=IPPROTO_UDPLITE )return(false);return(true);} + const bool IsSCTP ()const{if(socktype!=SOCK_SEQPACKET )return(false);if(protocol!=IPPROTO_SCTP )return(false);return(true);} + + /** + * 设置一个仅有端口号的地址,一般用于服务器监听本机所有地址 + */ + virtual void Set(ushort port)=0; + + /** + * 绑定当前IP地址到一个socket上 + * @param ThisSocket Socket号 + * @param reuse 是否可以复用这个IP,默认为true + */ + virtual bool Bind(int ThisSocket,int reuse=1)const=0; + + /** + * 获取当前地址的主机名称 + */ + virtual bool GetHostname(UTF8String &)const=0; + + virtual sockaddr *GetSockAddr()=0; + + virtual void *GetIP()=0; + + /** + * 获取当前地址的IP信息 + */ + virtual void GetIP(void *)=0; + + /** + * 取得当前地址的端口号 + */ + virtual const ushort GetPort()const=0; + + /** + * 转换当前地址到一个可视字符串,字符串所需长度请使用GetIPStringMaxSize()获取 + */ + virtual void ToString(char *,int)const=0; + + /** + * 创建一个可视字符串地址,需自行delete[] + */ + virtual char *CreateString()const + { + const int max_size=GetIPStringMaxSize(); + char *ipstr=new char[max_size+1]; + + ToString(ipstr,max_size); + + return ipstr; + } + + /** + * 创建一个当前地址的副本 + */ + virtual IPAddress *CreateCopy()const=0; + + /** + * 创建一个空的IP地址副本 + */ + virtual IPAddress *Create()const=0; + + /** + * 与较与另一个IP地址是否一样 + */ + virtual bool Comp(const IPAddress *)const=0; + };//class IPAddress + + /** + * IPv4地址 + */ + class IPv4Address:public IPAddress + { + sockaddr_in addr; + + public: + + IPv4Address(){hgl_zero(addr);} + IPv4Address(const uint32 _addr,ushort port,int _socktype,int _protocol):IPAddress(_socktype,_protocol) + { + Set(_addr,port); + } + + IPv4Address(const char *name,ushort port,int _socktype,int _protocol) + { + Set(name,port,_socktype,_protocol); + } + + IPv4Address(ushort port,int _socktype,int _protocol) + { + Set(nullptr,port,_socktype,_protocol); + } + + IPv4Address(const IPv4Address *src) + { + hgl_cpy(addr,src->addr); + socktype=src->socktype; + protocol=src->protocol; + } + + const int GetFamily()const override{return AF_INET;} + const uint GetIPSize()const override{return sizeof(in_addr);} + const uint GetSockAddrInSize()const override{return sizeof(sockaddr_in);} + const uint GetIPStringMaxSize()const override{return INET_ADDRSTRLEN+6;} + + const bool IsBoradcast()const override{return(addr.sin_addr.s_addr==htonl(INADDR_BROADCAST));} + + bool Set(const char *name,ushort port,int _socktype,int _protocol) override; + void Set(const uint32 _addr,ushort port) + { + hgl_zero(addr); + addr.sin_family =AF_INET; + addr.sin_addr.s_addr=_addr; + addr.sin_port =htons(port); + } + void Set(ushort port) override; + bool Bind(int ThisSocket,int reuse=1)const override; + bool GetHostname(UTF8String &)const override; + + sockaddr *GetSockAddr()override{return (sockaddr *)&addr;} + + void *GetIP() override {return &(addr.sin_addr);} + void GetIP(void *data) override { memcpy(data,&(addr.sin_addr),sizeof(in_addr)); } + + const uint32 GetInt32IP()const{return addr.sin_addr.s_addr;} + const ushort GetPort()const override; + + static void ToString(char *str,const int,const in_addr *); + static void ToString(char *str,const int,const sockaddr_in *); + void ToString(char *str,const int)const override; + + static int GetDomainIPList(List &addr_list,const char *domain,int _socktype,int _protocol); ///<取得当指定域名的IPv4地址列表 + static int GetLocalIPList(List &addr_list,int _socktype,int _protocol); ///<取得本机的IPv4地址列表 + + IPAddress *CreateCopy()const override{return(new IPv4Address(this));} + IPAddress *Create()const override{return(new IPv4Address());} + + bool Comp(const IPAddress *ipa)const override; + };//class IPv4Address + + /** + * IPv6地址 + */ + class IPv6Address:public IPAddress + { + sockaddr_in6 addr; + + public: + + IPv6Address(){hgl_zero(addr);} + IPv6Address(const in6_addr *ip,ushort port,int _socktype,int _protocol):IPAddress(_socktype,_protocol) + { + hgl_zero(addr); + + addr.sin6_family=AF_INET6; + memcpy(&(addr.sin6_addr),ip,sizeof(in6_addr)); + addr.sin6_port=htons(port); + } + + IPv6Address(const char *name,ushort port,int _socktype,int _protocol) + { + Set(name,port,_socktype,_protocol); + } + + IPv6Address(ushort port,int _socktype,int _protocol) + { + Set(nullptr,port,_socktype,_protocol); + } + + IPv6Address(const IPv6Address *src) + { + hgl_cpy(addr,src->addr); + socktype=src->socktype; + protocol=src->protocol; + } + + const int GetFamily()const override{return AF_INET6;} + const uint GetIPSize()const override{return sizeof(in6_addr);} + const uint GetSockAddrInSize()const override{return sizeof(sockaddr_in6);} + const uint GetIPStringMaxSize()const override{return INET6_ADDRSTRLEN+6;} + + const bool IsBoradcast()const override{return(false);} + + bool Set(const char *name,ushort port,int _socktype,int _protocol) override; + void Set(ushort port) override; + bool Bind(int ThisSocket,int reuse=1)const override; + bool GetHostname(UTF8String &)const override; + + sockaddr *GetSockAddr() override{return (sockaddr *)&addr;} + + void *GetIP() override {return &(addr.sin6_addr);} + void GetIP(void *data) override{memcpy(data,&(addr.sin6_addr),sizeof(in6_addr));} + + const ushort GetPort()const override; + + static void ToString(char *str,const int,const in6_addr *); + static void ToString(char *str,const int,const sockaddr_in6 *); + void ToString(char *str,const int)const override; + + static int GetDomainIPList(List &addr_list,const char *domain,int _socktype,int _protocol); ///<取得指定域名的IPv6地址列表 + static int GetLocalIPList(List &addr_list,int _socktype,int _protocol); ///<取得本机的IPv6地址列表 + + IPAddress *CreateCopy()const override{return(new IPv6Address(this));} + IPAddress *Create()const override{return(new IPv6Address());} + + bool Comp(const IPAddress *ipa)const override; + };//class IPv6Address + + inline IPv4Address *CreateIPv4TCP (const char *name,ushort port){return(new IPv4Address(name,port,SOCK_STREAM, IPPROTO_TCP));} + inline IPv6Address *CreateIPv6TCP (const char *name,ushort port){return(new IPv6Address(name,port,SOCK_STREAM, IPPROTO_TCP));} + inline IPv4Address *CreateIPv4UDP (const char *name,ushort port){return(new IPv4Address(name,port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv6Address *CreateIPv6UDP (const char *name,ushort port){return(new IPv6Address(name,port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv4Address *CreateIPv4UDPLite (const char *name,ushort port){return(new IPv4Address(name,port,SOCK_DGRAM, IPPROTO_UDPLITE));} + inline IPv6Address *CreateIPv6UDPLite (const char *name,ushort port){return(new IPv6Address(name,port,SOCK_DGRAM, IPPROTO_UDPLITE));} + inline IPv4Address *CreateIPv4SCTP (const char *name,ushort port){return(new IPv4Address(name,port,SOCK_SEQPACKET, IPPROTO_SCTP));} + inline IPv6Address *CreateIPv6SCTP (const char *name,ushort port){return(new IPv6Address(name,port,SOCK_SEQPACKET, IPPROTO_SCTP));} + + inline IPv4Address *CreateIPv4TCP (const uint32 &ip,ushort port){return(new IPv4Address(ip,port,SOCK_STREAM, IPPROTO_TCP));} + inline IPv4Address *CreateIPv4UDP (const uint32 &ip,ushort port){return(new IPv4Address(ip,port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv4Address *CreateIPv4UDPLite (const uint32 &ip,ushort port){return(new IPv4Address(ip,port,SOCK_DGRAM, IPPROTO_UDPLITE));} + inline IPv4Address *CreateIPv4SCTP (const uint32 &ip,ushort port){return(new IPv4Address(ip,port,SOCK_SEQPACKET, IPPROTO_SCTP));} + + inline IPv6Address *CreateIPv6TCP (const in6_addr *ip,ushort port){return(new IPv6Address(ip,port,SOCK_STREAM, IPPROTO_TCP));} + inline IPv6Address *CreateIPv6UDP (const in6_addr *ip,ushort port){return(new IPv6Address(ip,port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv6Address *CreateIPv6UDPLite (const in6_addr *ip,ushort port){return(new IPv6Address(ip,port,SOCK_DGRAM, IPPROTO_UDPLITE));} + inline IPv6Address *CreateIPv6SCTP (const in6_addr *ip,ushort port){return(new IPv6Address(ip,port,SOCK_SEQPACKET, IPPROTO_SCTP));} + + inline IPv4Address *CreateIPv4TCP (ushort port){return(new IPv4Address(port,SOCK_STREAM, IPPROTO_TCP));} + inline IPv6Address *CreateIPv6TCP (ushort port){return(new IPv6Address(port,SOCK_STREAM, IPPROTO_TCP));} + inline IPv4Address *CreateIPv4UDP (ushort port){return(new IPv4Address(port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv6Address *CreateIPv6UDP (ushort port){return(new IPv6Address(port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv4Address *CreateIPv4UDPLite (ushort port){return(new IPv4Address(port,SOCK_DGRAM, IPPROTO_UDPLITE));} + inline IPv6Address *CreateIPv6UDPLite (ushort port){return(new IPv6Address(port,SOCK_DGRAM, IPPROTO_UDPLITE));} + inline IPv4Address *CreateIPv4SCTP (ushort port){return(new IPv4Address(port,SOCK_SEQPACKET, IPPROTO_SCTP));} + inline IPv6Address *CreateIPv6SCTP (ushort port){return(new IPv6Address(port,SOCK_SEQPACKET, IPPROTO_SCTP));} + + inline IPv4Address *CreateIPv4UDPBoradcast (ushort port){return(new IPv4Address(htonl(INADDR_BROADCAST),port,SOCK_DGRAM, IPPROTO_UDP));} + inline IPv4Address *CreateIPv4UDPLiteBoradcast (ushort port){return(new IPv4Address(htonl(INADDR_BROADCAST),port,SOCK_DGRAM, IPPROTO_UDPLITE));} + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_IP_TOOL_INCLUDE diff --git a/inc/hgl/network/JavaDirectSocketIOUserThread.h b/inc/hgl/network/JavaDirectSocketIOUserThread.h new file mode 100644 index 0000000..5119e86 --- /dev/null +++ b/inc/hgl/network/JavaDirectSocketIOUserThread.h @@ -0,0 +1,17 @@ +#ifndef HGL_NETWORK_JAVA_DIRECT_SOCKET_IO_USER_THREAD_INCLUDE +#define HGL_NETWORK_JAVA_DIRECT_SOCKET_IO_USER_THREAD_INCLUDE + +#include +#include +#include + +namespace hgl +{ + using namespace hgl::io; + + namespace network + { + typedef DirectSocketIOUserThread JavaDirectSocketIOUserThread; + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_JAVA_DIRECT_SOCKET_IO_USER_THREAD_INCLUDE diff --git a/inc/hgl/network/MTTCPServer.h b/inc/hgl/network/MTTCPServer.h new file mode 100644 index 0000000..6577156 --- /dev/null +++ b/inc/hgl/network/MTTCPServer.h @@ -0,0 +1,163 @@ +#ifndef HGL_NETWORK_MULTI_THREAD_TCP_SERVER_INCLUDE +#define HGL_NETWORK_MULTI_THREAD_TCP_SERVER_INCLUDE + +#include +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + /** + * 多线程TCP服务器 + */ + template class MTTCPServer + { + protected: + + class Accept2SocketManageThread:public AcceptThread + { + SOCKET_MANAGE_THREAD *sm_thread; + + public: + + using AcceptThread::AcceptThread; + + void SetSocketManage(SOCKET_MANAGE_THREAD *smt) + { + sm_thread=smt; + } + + bool OnAccept(int client_sock,IPAddress *ip_address) override + { + if(!sm_thread)return(false); + + USER_ACCEPT *us; + + us=new USER_ACCEPT(client_sock,ip_address); //这个new非常占时间,未来放到各自的线程去做 + + auto &sl=sm_thread->JoinBegin(); + + sl.Add(us); + + sm_thread->JoinEnd(); + + return(true); + } + };//class Accept2SocketManageThread:public AcceptThread + + protected: + + IPAddress * server_ip=nullptr; + TCPServer server; + + MultiThreadAccept accept_manage; + MultiThreadManage sock_manage; + + protected: + + virtual SOCKET_MANAGE_THREAD *CreateSocketManageThread(int max_user) + { + SocketManage *sm=new SocketManage(max_user); + SOCKET_MANAGE_THREAD *smt=new SOCKET_MANAGE_THREAD(sm); + + return smt; + } + + public: + + /** + * 服务器初始化信息结构 + */ + struct InitInfomation + { + IPAddress * server_ip =nullptr; ///<服务器IP地址 + bool port_reuse =false; ///<端口复用 + bool ipv6_only =false; ///<是否使用IPv6 Only + bool block =true; ///<是否使用阻塞模式 + uint defer_accept_time =1; ///<延迟Accept成功超时时间,单位:秒(第一次收到数据才会成功Accept,这里指从connect到第一个包的超时时间) + double accept_time_out =1; ///GetFamily()==AF_INET6) //如果是IPv6地址 + server.SetIPv6Only(info.ipv6_only); //设置是否仅使用IPv6,这个有可能失败,但是不管它 + + server.SetBlock(true); //设置使用阻塞模式 + +#if HGL_OS != HGL_OS_Windows + server.SetDeferAccept(info.defer_accept_time); //指定时间内收到数据才会产生accept +#endif + + server.SetTimeOut(info.accept_time_out); //设置accept超时时间 + + if(!accept_manage.Init(&server,info.thread_count)) + return(false); + + for(int i=0;iSetSocketManage(smt); + + sock_manage.Add(smt); + } + + if(!sock_manage.Start()) + return(false); + + if(!accept_manage.Start()) + { + sock_manage.Close(); + return(false); + } + + server_ip=info.server_ip; + + return(true); + } + + int IsLive() + { + return(sock_manage.IsLive()+accept_manage.IsLive()); + } + + bool Wait(const double &time_out=HGL_NETWORK_TIME_OUT) + { + WaitTime(time_out); + + const int live_s=sock_manage.IsLive(); + const int live_a=accept_manage.IsLive(); + + if(live_s+live_s<=0) + return(false); + +#ifdef _DEBUG + std::cout<<"live accept thread "< class MTTCPServer + + template + using MTTCPServerStd=MTTCPServer>; + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_MULTI_THREAD_TCP_SERVER_INCLUDE diff --git a/inc/hgl/network/MultiThreadAccept.h b/inc/hgl/network/MultiThreadAccept.h new file mode 100644 index 0000000..1340332 --- /dev/null +++ b/inc/hgl/network/MultiThreadAccept.h @@ -0,0 +1,88 @@ +#ifndef HGL_NETWORK_MULTI_THREAD_ACCEPT_INCLUDE +#define HGL_NETWORK_MULTI_THREAD_ACCEPT_INCLUDE + +#include +#include +#include +namespace hgl +{ + namespace network + { + class IPAddress; + class AcceptServer; + + using IPAddressStack=Stack; + + /** + * Socket接入线程 + */ + class AcceptThread:public Thread + { + AcceptServer *server; + Semaphore *active_semaphore; + + protected: + + IPAddressStack ip_stack; /// class MultiThreadAccept + { + MultiThreadManage accept_thread_manage; + + Semaphore active_semaphore; + + public: + + virtual ~MultiThreadAccept()=default; + + bool Init(AcceptServer *server,const uint thread_count) + { + if(!server)return(false); + if(thread_count<=0)return(false); + + for(int i=0;i0;} + int IsLive (){return accept_thread_manage.IsLive();} + bool Close (){return accept_thread_manage.Close();} + + /** + * 等待接收信号 + */ + bool Wait(const double &time_over=5) + { + return active_semaphore.Acquire(time_over); + } + };//template class MultiThreadAccept + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_MULTI_THREAD_ACCEPT_INCLUDE diff --git a/inc/hgl/network/SCTPClient.h b/inc/hgl/network/SCTPClient.h new file mode 100644 index 0000000..0a10b0a --- /dev/null +++ b/inc/hgl/network/SCTPClient.h @@ -0,0 +1,28 @@ +#ifndef HGL_NETWORK_SCTP_CLIENT_INCLUDE +#define HGL_NETWORK_SCTP_CLIENT_INCLUDE + +#include +namespace hgl +{ + namespace network + { + /** + * SCTP一对一模式(one to one,tcp-style)客户端
+ * SCTP一对一模式(one to one,tcp-style)通信处理的客户端封装 + */ + class SCTPO2OClient:public SCTPO2OSocket ///SCTP Client (tcp-style) + { + public: + + SCTPO2OClient(); + SCTPO2OClient(int); + ~SCTPO2OClient(); + + virtual bool Connect(const sockaddr_in &); ///<连接到服务器 + bool Connect(const char *,int); ///<接连到服务器 + };//class SCTPO2OClient:public Socket + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_NETWORK_SCTP_CLIENT_INCLUDE diff --git a/inc/hgl/network/SCTPServer.h b/inc/hgl/network/SCTPServer.h new file mode 100644 index 0000000..e61901f --- /dev/null +++ b/inc/hgl/network/SCTPServer.h @@ -0,0 +1,48 @@ +#ifndef HGL_NETWORK_SCTP_SERVER_INCLUDE +#define HGL_NETWORK_SCTP_SERVER_INCLUDE + +#include +#include +namespace hgl +{ + namespace network + { + /** + * SCTP一对一模式(one to one,tcp-style)服务器端 + */ + class SCTPO2OServer:public AcceptServer + { + int CreateServerSocket() override final; ///<创建Server Socket + };//class SCTPO2OServer + + /** + * SCTP一对多模式(one to many,udp-style)服务器端 + */ + class SCTPO2MServer:public SCTPO2MSocket + { + public: + + virtual bool CreateServer(const sockaddr_in &,const uint ml=HGL_SERVER_LISTEN_COUNT); ///<创建服务器 + bool CreateServer(const char *,uint,const uint ml=HGL_SERVER_LISTEN_COUNT); ///<创建服务器 + bool CreateServer(uint port,const uint ml=HGL_SERVER_LISTEN_COUNT) ///<创建服务器 + {return CreateServer(nullptr,port,ml);} + + virtual void CloseServer(); ///<关闭服务器 + + /** + * 设置是否使用堵塞方式传输 + * @param block 是否使用堵塞方式(true/false) + * @param sto 发送数据超时时间(单位: 秒) + * @param rto 接收数据超时时间(单位: 秒) + */ + void SetBlock(bool block,double sto=HGL_NETWORK_TIME_OUT, + double rto=HGL_NETWORK_TIME_OUT) ///<设置是否使用堵塞方式 + { + SetSocketBlock(ThisSocket,block,sto,rto); + } + + void SetAutoClose(const int to); ///<设置自动关闭时间 + };//class SCTPO2MServer + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SCTP_SERVER_INCLUDE diff --git a/inc/hgl/network/SCTPSocket.h b/inc/hgl/network/SCTPSocket.h new file mode 100644 index 0000000..c0a4687 --- /dev/null +++ b/inc/hgl/network/SCTPSocket.h @@ -0,0 +1,76 @@ +#ifndef HGL_NETWORK_SCTP_SOCKET_INCLUDE +#define HGL_NETWORK_SCTP_SOCKET_INCLUDE + +#include +#include +#include +namespace hgl +{ + namespace network + { + /** + * sctp socket功能基类 + */ + class SCTPSocket:public Socket + { + protected: + + int out_max_stream; + int in_max_stream; + int init_max_attempts; + int init_max_init_timeo; + + sockaddr_in address; + + bool GetMsg(); + bool InitDataIOEvent(); + bool InitMsg(int out_stream=0,int in_stream=0,int attempts=0,int init_time_out=0); + + protected: + + void ProcDisconnect()=default; + int ProcRecv(int)=default; + int ProcSend(int,int &)=default; + + public: + + virtual bool SetNodelay(bool); ///<设置是否使用无延迟方式 + virtual bool SetMaxStream(int); ///<设置最大流数 + + public: + + SCTPSocket(); + virtual ~SCTPSocket()=default; + + virtual void UseSocket(int,const sockaddr_in *addr=nullptr); ///<使用指定socket + };//class SCTPSocket + + class SCTPO2OSocket:public SCTPSocket + { + struct sctp_sndrcvinfo sri_recv; + struct sctp_sndrcvinfo sri_send; + + public: + + using SCTPSocket::SCTPSocket; + virtual ~SCTPO2OSocket()=default; + + virtual bool SendMsg(const void *,int len,uint16 stream); ///<发送一个数据包 + virtual bool RecvMsg(MemBlock *,uint16 &stream); ///<接收一个数据包 + }; + + class SCTPO2MSocket:public SCTPSocket + { + public: + + using SCTPSocket::SCTPSocket; + virtual ~SCTPO2MSocket()=default; + + virtual bool Create(); ///<创建一个Socket + + virtual bool SendMsg(const sockaddr_in *,void *,int len,uint16); ///<发送一个数据包 + virtual bool RecvMsg(sockaddr_in &,void *,int max_len,int &len,uint16 &); ///<接收一个数据包 + };// + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SCTP_SOCKET_INCLUDE diff --git a/inc/hgl/network/ServerSocket.h b/inc/hgl/network/ServerSocket.h new file mode 100644 index 0000000..c7335b1 --- /dev/null +++ b/inc/hgl/network/ServerSocket.h @@ -0,0 +1,51 @@ +#ifndef HGL_NETWORK_ThisSocket_INCLUDE +#define HGL_NETWORK_ThisSocket_INCLUDE + +#include +namespace hgl +{ + namespace network + { + /** + * 服务器端Socket共用基类 + */ + class ServerSocket ///服务器端Socket共用基类 + { + protected: + + int ThisSocket; + IPAddress *server_address; + + virtual int CreateServerSocket()=0; ///<创建Server Socket + + public: + + ServerSocket(); + virtual ~ServerSocket(); + + const IPAddress *GetServerAddress()const{return server_address;} ///<取得服务器IP地址 + IPAddress *CreateIPAddress()const + {return server_address?server_address->Create():nullptr;} ///<创建一个空的IP地址空间 + + bool CreateIPAddress(IPAddress **ip_buffer,int count)const; ///<创建多个空的IP地址空间 + + virtual bool CreateServer(const IPAddress *,const uint ml=HGL_SERVER_LISTEN_COUNT,bool reuse=false); ///<创建服务器 + virtual void CloseServer(); ///<关闭服务器 + + /** + * 设置是否使用堵塞方式传输 + * @param block 是否使用堵塞方式(true/false) + * @param sto 发送数据超时时间(单位: 秒) + * @param rto 接收数据超时时间(单位: 秒) + */ + void SetBlock(bool block,double sto=HGL_NETWORK_TIME_OUT, + double rto=HGL_NETWORK_TIME_OUT) ///<设置是否使用堵塞方式 + { + SetSocketBlock(ThisSocket,block,sto,rto); + } + + bool SetIPv6Only(bool); ///<设置仅使用IPv6 + };//class ServerSocket + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_ThisSocket_INCLUDE diff --git a/inc/hgl/network/Socket.h b/inc/hgl/network/Socket.h new file mode 100644 index 0000000..5bdeb69 --- /dev/null +++ b/inc/hgl/network/Socket.h @@ -0,0 +1,155 @@ +#ifndef HGL_SOCKET_INCLUDE +#define HGL_SOCKET_INCLUDE + +#include + +//#define HGL_RECV_BYTE_COUNT ///<接收字节数统计(调试用) +//#define HGL_SEND_BYTE_COUNT ///<发送字节数统计(调试用) + +//#define HGL_SOCKET_SEND_LIMIT_SIZE ///<发送限制包尺寸,默认为不限制 + +namespace hgl +{ + namespace io + { + class DataInputStream; + class DataOutputStream; + }//namespace io + + namespace network ///网络相关处理模块名字空间 + { + constexpr uint HGL_NETWORK_MAX_PORT =65535; ///<最大端口号 + constexpr uint HGL_NETWORK_IPv4_STR_MIN =7; /// +#include +namespace hgl +{ + namespace network + { + struct SocketEvent + { + int sock; + + union + { + int size; //数据长度(此属性为BSD系统独有) + int error; //错误号 + }; + };//struct SocketEvent + + using SocketEventList=List; + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SOCKET_EVENT_INCLUDE diff --git a/inc/hgl/network/SocketInputStream.h b/inc/hgl/network/SocketInputStream.h new file mode 100644 index 0000000..d6790f3 --- /dev/null +++ b/inc/hgl/network/SocketInputStream.h @@ -0,0 +1,56 @@ +#ifndef HGL_NETWORK_SOCKET_INPUT_STREAM_INCLUDE +#define HGL_NETWORK_SOCKET_INPUT_STREAM_INCLUDE + +#include +namespace hgl +{ + template class MemBlock; + + namespace network + { + /** + * Socket输入流,用于TCP/SCTP协议在无包封装处理的情况下 + */ + class SocketInputStream:public io::InputStream + { + protected: + + int sock; + MemBlock *mb; + + int64 total; //累计字节数 + + public: + + SocketInputStream(int=0); + ~SocketInputStream(); + + void SetSocket(int s) + { + sock=s; + total=0; + } + + int64 GetTotal()const{return total;} ///<取得累计字节数 + + void Close(){} ///<关闭输入流 + + int64 Read(void *,int64); ///<从socket中读取指定的字节数 + int64 Peek(void *,int64); ///<从socket中读取指定的字节数,但不从缓存队列中删除 + int64 ReadFully(void *,int64); ///<充分读取指定字节的数据 + + bool CanRestart()const{return false;} ///<是否可以复位 + bool CanSeek()const{return false;} ///<是否可以定位 + bool CanSize()const{return false;} ///<是否可以取得尺寸 + bool CanPeek()const{return true;} ///<是否可以预览数据 + + bool Restart(){return false;} ///<复位访问指针 + int64 Skip(int64); ///<跳过指定字节不访问 + int64 Seek(int64,io::SeekOrigin=io::soBegin){return -1;} ///<移动访问指针 + int64 Tell()const{return -1;} ///<返回当前访问位置 + int64 GetSize()const{return -1;} ///<取得流长度 + int64 Available()const; ///<剩下的可以不受阻塞访问的字节数 + };//class SocketInputStream + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SOCKET_INPUT_STREAM_INCLUDE diff --git a/inc/hgl/network/SocketManage.h b/inc/hgl/network/SocketManage.h new file mode 100644 index 0000000..03ece3e --- /dev/null +++ b/inc/hgl/network/SocketManage.h @@ -0,0 +1,68 @@ +#ifndef HGL_NETWORK_SOCKET_MANAGE_INCLUDE +#define HGL_NETWORK_SOCKET_MANAGE_INCLUDE + +#include +#include +#include +namespace hgl +{ + namespace network + { + class SocketManageBase; + + /** + * 最简单的服Socket管理类,直接在一个Update内处理socket的轮循和处理事件(不关心是recv还是send)
+ * 该类所有函数均为非线程安全,所以不可以直接在多线程中使用 + */ + class SocketManage + { + protected: + + using SocketList=Map; + + protected: + + SocketList socket_list; + + SocketManageBase *manage; ///<实际的Socket管理器 + + SocketEventList sock_recv_list, + sock_send_list, + sock_error_list; + + Set error_set; + + protected: + + void ProcSocketRecvList(); + void ProcSocketSendList(); + void ProcSocketErrorList(); + + void ProcErrorList(); + + public: + + const Set &GetErrorSocketSet(){return error_set;} ///<获取错误SOCKET合集 + + public: + + SocketManage(int max_user); + virtual ~SocketManage(); + + bool Join(TCPAccept *s); + int Join(TCPAccept **s_list,int count); + + bool Unjoin(TCPAccept *s); + int Unjoin(TCPAccept **s_list,int count); + + /** + * 刷新所有操作(删除错误Socket,轮循可用Socket,发送,接收
+ * 需要注意的是,Update中轮循到的错误/关闭Socket列表,将在下一次Update时清除。所以在每次调用Update后,请调用GetErrorSocketSet获取错误Socket合集并处理出错Socket + */ + virtual int Update(const double &time_out=HGL_NETWORK_TIME_OUT); + + virtual void Clear(); + };//class SocketManage + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SOCKET_MANAGE_INCLUDE diff --git a/inc/hgl/network/SocketManageThread.h b/inc/hgl/network/SocketManageThread.h new file mode 100644 index 0000000..1b9d283 --- /dev/null +++ b/inc/hgl/network/SocketManageThread.h @@ -0,0 +1,153 @@ +#ifndef HGL_NETWORK_SOCKET_MANAGE_THREAD_INCLUDE +#define HGL_NETWORK_SOCKET_MANAGE_THREAD_INCLUDE + +#include +#include +#include +namespace hgl +{ + namespace network + { + /** + * 简单的Socket管理器线程 + */ + template class SocketManageThread:public Thread + { + public: + + using AcceptSocketList=List; ///<工作对象列表定义 + + protected: + + SocketManage *sock_manage; + + protected: + + SemSwapData join_list; ///<待添加的Socket对象列表 + SemSwapData unjoin_list; ///<待移出的Socket对象列表 + + virtual void OnSocketClear(USER_ACCEPT *us){delete us;} /// + void ClearAcceptSocketList(ST &sl) + { + const int count=sl.GetCount(); + USER_ACCEPT **us=sl.GetData(); + + for(int i=0;iJoin(us);} ///<单个工作对象接入处理函数 + virtual bool Unjoin(USER_ACCEPT *us){return sock_manage->Unjoin(us);} ///<单个工作对象退出处理函数 + + /** + *处理要接入的工作对象列表 + */ + void ProcJoinList() + { + AcceptSocketList &usl=join_list.GetReceive(); + + const int count=usl.GetCount(); + USER_ACCEPT **us=usl.GetData(); + + for(int i=0;iClear(); + + //unjoin_list中的理论上都已经在wo_list/join_list里了,所以不需要走Clear,直接清空列表 + unjoin_list.GetReceive().ClearData(); + unjoin_list.Swap(); + unjoin_list.GetReceive().ClearData(); + } + + virtual bool Execute() override + { + if(join_list.TrySemSwap()) + ProcJoinList(); + + if(unjoin_list.TrySemSwap()) + ProcUnjoinList(); + + sock_manage->Update(0.1); //这里写0.1秒,只是为了不卡住主轮循。这是个错误的设计,未来要将epoll(recv)完全独立一个线程跑 + + const auto &error_set=sock_manage->GetErrorSocketSet(); + USER_ACCEPT **us=(USER_ACCEPT **)error_set.GetData(); + + for(int i=0;i class SocketManageThread:public Thread + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SOCKET_MANAGE_THREAD_INCLUDE diff --git a/inc/hgl/network/SocketOutputStream.h b/inc/hgl/network/SocketOutputStream.h new file mode 100644 index 0000000..03eebcc --- /dev/null +++ b/inc/hgl/network/SocketOutputStream.h @@ -0,0 +1,54 @@ +#ifndef HGL_NETWORK_SOCKET_OUTPUT_STREAM_INCLUDE +#define HGL_NETWORK_SOCKET_OUTPUT_STREAM_INCLUDE + +#include +namespace hgl +{ + namespace network + { + /** + * Socket输出流,用于TCP/SCTP协议在无包封装处理的情况下 + */ + class SocketOutputStream:public io::OutputStream + { + protected: + + int sock; + + int64 total; //累计字节数 + + public: + + SocketOutputStream(int s=-1) + { + SetSocket(s); + } + + ~SocketOutputStream()=default; + + void SetSocket(int s) + { + sock=s; + total=0; + } + + void Close(){} ///<关闭输出流 + + int64 GetTotal()const{return total;} ///<取得累计字节数 + + int64 Write(const void *,int64); ///<向socket中写入指定的字节数 + int64 WriteFully(const void *,int64); ///<充分写入指定字节的数据 + + bool CanRestart()const{return false;} ///<是否可以复位 + bool CanSeek()const{return false;} ///<是否可以定位 + bool CanSize()const{return false;} ///<是否可以取得尺寸 + + bool Restart(){return false;} ///<复位访问指针 + int64 Seek(int64,io::SeekOrigin=io::soBegin){return -1;} ///<移动访问指针 + int64 Tell()const{return -1;} ///<返回当前访问位置 + int64 GetSize()const{return -1;} ///<取得流长度 + int64 Available()const; ///<剩下的可以不受阻塞写入的字节数 + };//class SocketOutputStream + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SOCKET_OUTPUT_STREAM_INCLUDE diff --git a/inc/hgl/network/TCPAccept.h b/inc/hgl/network/TCPAccept.h new file mode 100644 index 0000000..730d2d0 --- /dev/null +++ b/inc/hgl/network/TCPAccept.h @@ -0,0 +1,83 @@ +#ifndef HGL_NETWORK_TCP_ACCEPT_INCLUDE +#define HGL_NETWORK_TCP_ACCEPT_INCLUDE + +#include +#include +namespace hgl +{ + namespace network + { + /** + * TCPAccept与SocketManage + * + * TCPAccept: 客户端连接到服务器后的控制对象 + * SocketManage: 服务器对批量TCPAccept进行管理的对象 + * SocketManageThread: SocketManage的异步封装 + * + * 收包流程: + * 1.SocketManage通过OnSocketRecv函数通知TCPAccept接收数据 + * 2.TCPAccept在收满4个字节的包长后,根据包长向MultiLevelMemoryPool申请缓冲区 + * 3.TCPAccept收满一个包后,通过OnRecvPacket事件函数通知开发者有包了 + * 4.TCPAccept将缓冲区送回BufferPool + */ + + using PACKET_SIZE_TYPE=uint32; ///<描述包长度的数据类型 + constexpr uint PACKET_SIZE_TYPE_BYTES=sizeof(PACKET_SIZE_TYPE); ///<描述包长度的数据类型的字节长度 + + class SocketInputStream; + class SocketOutputStream; + + /** + * TCP服务器接入用户处理基类,为各种Server管理器提供统一调用接口
+ * *******************************************************************
+ * 收包和发包是可以在不同线程异步同时工作的,现在写一起只是暂时,未来分开 + */ + class TCPAccept:public TCPSocket + { + protected: + + SocketInputStream *sis=nullptr; + SocketOutputStream *sos=nullptr; + + protected://事件函数,由SocketManage调用 + + friend class SocketManage; + + virtual int OnSocketRecv(int)=0; /// recv_buffer; + uint recv_length=0; + + uint64 recv_total=0; + + protected: + + virtual int OnSocketRecv(int) override; /// +#include +#include +namespace hgl +{ + namespace network + { + /** + * TCP客户端处理类,程序模式为多线程阻塞模式。该类会创建2个独立的线程分别用于收包和发包。 + */ + class TCPClient:public TCPSocket ///TCP客户端处理类 + { + io::InputStream *sis; + io::OutputStream *sos; + + char *ipstr; + + virtual void InitPrivate(int); + + public: + + double Heart; ///<心跳间隔时间(单位:秒,默认参见HGL_TCP_HEART_TIME) + double TimeOut; ///<超时时间(单位:秒,默认参见HGL_NETWORK_TIME_OUT) + + public: + + TCPClient(); ///<本类构造函数 + TCPClient(int,const IPAddress *); ///<本类构造函数 + virtual ~TCPClient(); ///<本类析构函数 + + virtual bool Connect(); ///<连接到服务器 + virtual bool CreateConnect(const IPAddress *); ///<创建一个连接 + virtual void Disconnect(); ///<断开连接 + virtual bool UseSocket(int,const IPAddress *addr) override; ///<使用指定socket + + const char *GetIPString()const{return ipstr;} ///<取得IP可视字符串 + + public: + + virtual io::InputStream *GetInputStream(){return sis;} ///<取得输入流 + virtual io::OutputStream *GetOutputStream(){return sos;} ///<取得输出流 + };//class TCPClient + + TCPClient *CreateTCPClient(IPAddress *); + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_TCP_CLIENT_INCLUDE diff --git a/inc/hgl/network/TCPServer.h b/inc/hgl/network/TCPServer.h new file mode 100644 index 0000000..f163311 --- /dev/null +++ b/inc/hgl/network/TCPServer.h @@ -0,0 +1,26 @@ +#ifndef HGL_TCP_SERVER_INCLUDE +#define HGL_TCP_SERVER_INCLUDE + +#include +namespace hgl +{ + namespace network + { + /** + * TCPServer是对应TCP连接处理的通用服务器端 + */ + class TCPServer:public AcceptServer ///TCP服务器端实现基类 + { + int CreateServerSocket() override final; ///<创建一个服务器用Socket + + public: + +#if (HGL_OS != HGL_OS_Windows)&&(HGL_OS != HGL_OS_macOS) + void SetDeferAccept(const int); ///<设置推迟Accept +#endif//no windows&mac + };//class TCPServer + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_TCP_SERVER_INCLUDE diff --git a/inc/hgl/network/TCPSocket.h b/inc/hgl/network/TCPSocket.h new file mode 100644 index 0000000..30909d1 --- /dev/null +++ b/inc/hgl/network/TCPSocket.h @@ -0,0 +1,43 @@ +#ifndef HGL_TCP_SOCKET_INCLUDE +#define HGL_TCP_SOCKET_INCLUDE + +#include +namespace hgl +{ + namespace network + { + int CreateTCPConnect(IPAddress *); ///<创建一个tcp连接 + + /** + * TCP连接处理基类
+ * 提供统一的Recv/Send函数以及缓冲区,但请注意这个recv/send都只是针对缓冲区的,真正的send/recv在各自的派生类中。 + */ + class TCPSocket:public Socket ///TCP连接处理基类 + { + protected: + + timeval time_out; + fd_set local_set,recv_set,err_set; + + void ResetConnect(); + + public: //方法 + + TCPSocket() :Socket() {} + TCPSocket(int sock,const IPAddress *addr):Socket(sock,addr){ResetConnect();} + virtual ~TCPSocket()=default; ///<本类析构函数 + + bool SetNodelay(bool); ///<设置是否使用无延迟方式 + void SetKeepAlive(bool,const int=7200,const int=75,const int=9); ///<设置自动保持连接机制 + + virtual bool UseSocket(int,const IPAddress *) override; ///<使用指定socket + + virtual bool IsConnect(); ///<当前socket是否在连接状态 + + virtual int WaitRecv(double); ///<等待可接收数据 + };//class TCPSocket + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_TCP_SOCKET_INCLUDE diff --git a/inc/hgl/network/UdpSocket.h b/inc/hgl/network/UdpSocket.h new file mode 100644 index 0000000..39f067d --- /dev/null +++ b/inc/hgl/network/UdpSocket.h @@ -0,0 +1,104 @@ +#ifndef HGL_UDPSOCKET_INCLUDE +#define HGL_UDPSOCKET_INCLUDE + +#include +#include +namespace hgl +{ + namespace network + { + /** + * 这个类提供使用UDP协议的通信,但它并不提供可靠数据传输的支持。 + */ + class UDPSocket:public Socket ///UDP通信类 + { + IPAddress *bind_addr; + IPAddress *tar_addr; + + public: //事件函数 + + virtual int ProcRecv(int=-1){return -1;} + virtual int ProcSend(int,int &left_bytes){return -1;} + + public: + + UDPSocket(); ///<本类构造函数 + virtual ~UDPSocket(); ///<本类析构函数 + + virtual bool Create(const IPAddress *); ///<创建一个udp,并绑定一个IP地址与指定端口 +// virtual bool Create(int family); ///<创建一个udp + + uint GetBindPort()const{return bind_addr->GetPort();} ///<取得绑定端口 + + bool SetSendAddr(const IPAddress *); ///<设定发送地址 + + int SendPacket(const void *,int); ///<发送数据包 + int SendPacket(IPAddress *,const void *,int); ///<向指定地址发送数据包 + int RecvPacket(void *,int,IPAddress *); ///<接收数据包 + };//class UDPSocket + + /** + * UDPLite协议封装使用 + */ + class UDPLiteSocket:public UDPSocket + { + public: + + virtual ~UDPLiteSocket()=default; + + bool Create(const IPAddress *)override; ///<创建一个udp lite,并绑定一个IP地址与指定端口 +// bool Create(int family)override; ///<创建一个udp lite + + void SetChecksumCoverage(int send_val=20,int recv_val=20); ///<设定UDPLite检验位长度,最小20 + };//class UDPLiteSocket + + /** + * 使用回呼事件机制的UDPSocket + */ + template class _UDPSocketCB:public BASE + { + public: //事件函数 + + DefEvent(void, OnDisconnect, (BASE *)); + DefEvent(int, OnRecv, (BASE *,int)); + DefEvent(int, OnSend, (BASE *,int,int &)); + + virtual void ClearEvent() + { + OnDisconnect=nullptr; + OnRecv =nullptr; + OnSend =nullptr; + } + + public: + + _UDPSocketCB(){ClearEvent();} ///<本类构造函数 + virtual ~_UDPSocketCB()=default; + + virtual void ProcDisconnect() + { + SafeCallEvent(OnDisconnect,(this)); + } + + virtual int ProcRecv(int size) + { + if(OnRecv==nullptr)return(-1); + + return OnRecv(this,size); + } + + virtual int ProcSend(int size,int &left_bytes) + { + if(OnSend==nullptr)return(-1); + + return OnSend(this,size,left_bytes); + } + };//class _UDPSocketCB + + using UDPSocketCB =_UDPSocketCB; + using UDPLiteSocketCB =_UDPSocketCB; + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_UDPSOCKET_INCLUDE diff --git a/inc/hgl/network/WebSocket.h b/inc/hgl/network/WebSocket.h new file mode 100644 index 0000000..521793a --- /dev/null +++ b/inc/hgl/network/WebSocket.h @@ -0,0 +1,13 @@ +#ifndef HGL_NETWORK_WEBSOCKET_INCLUDE +#define HGL_NETWORK_WEBSOCKET_INCLUDE + +#include +namespace hgl +{ + namespace network + { + bool GetWebSocketInfo(UTF8String &sec_websocket_key,UTF8String &sec_websocket_protocol,uint &sec_websocket_version,const char *data,const uint size); + void MakeWebSocketAccept(UTF8String &result,const UTF8String &sec_websocket_key,const UTF8String &sec_websocket_protocol); + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_WEBSOCKET_INCLUDE diff --git a/inc/hgl/network/WebSocketAccept.h b/inc/hgl/network/WebSocketAccept.h new file mode 100644 index 0000000..5823328 --- /dev/null +++ b/inc/hgl/network/WebSocketAccept.h @@ -0,0 +1,82 @@ +#ifndef HGL_NETWORK_WEBSOCKET_ACCEPT_INCLUDE +#define HGL_NETWORK_WEBSOCKET_ACCEPT_INCLUDE + +#include +#include +namespace hgl +{ + namespace network + { + /** + * WebSocket接入管理 + */ + class WebSocketAccept:public TCPAccept + { + #ifdef _DEBUG + MemBlock data_out_str; + #endif//_DEBUG + protected: + + MemBlock recv_buffer; + uint recv_length=0; + + uint64 recv_total=0; + + protected: + + virtual int OnSocketRecv(int) override; /// + * 原作者:Kim Youngjin + */ + class WinFireWall + { + INetFwProfile* m_pFireWallProfile; + + public: + + enum PROTOCOL + { + ANY=256, + UDP=17, + TCP=6, + }; + + public: + + WinFireWall(); + ~WinFireWall(); + + FW_ERROR_CODE Init(); + FW_ERROR_CODE Close(); + + FW_ERROR_CODE CheckFirewall(bool &); + FW_ERROR_CODE OpenFirewall(); + FW_ERROR_CODE CloseFirewall(); + + FW_ERROR_CODE CheckApplication(const wchar_t *,bool &); + FW_ERROR_CODE AddApplication(const wchar_t *,const wchar_t *); + FW_ERROR_CODE RemoveApplication(const wchar_t *); + + FW_ERROR_CODE CheckPort(unsigned int,PROTOCOL,bool &); + FW_ERROR_CODE OpenPort(unsigned int,PROTOCOL,const wchar_t *); + FW_ERROR_CODE ClosePort(unsigned int,PROTOCOL); + + + FW_ERROR_CODE IsExceptionNotAllowed(bool &); + FW_ERROR_CODE SetExceptionNotAllowed(bool); + + FW_ERROR_CODE IsNotificationDiabled(bool &); + FW_ERROR_CODE SetNotificationDiabled(bool); + + FW_ERROR_CODE IsUnicastResponsesToMulticastBroadcastDisabled(bool &); + FW_ERROR_CODE SetUnicastResponsesToMulticastBroadcastDisabled(bool); + }; + }//namespace network + + using namespace network; +}//namespace hgl +#endif//HGL_NETWORK_WINDOW_FIREWALL_INCLUDE diff --git a/path_config.cmake b/path_config.cmake new file mode 100644 index 0000000..c509f17 --- /dev/null +++ b/path_config.cmake @@ -0,0 +1,9 @@ +macro(CMNetworkSetup CMNETWORK_ROOT_PATH) + + message("CMNETWORK_ROOT_PATH: " ${CMNETWORK_ROOT_PATH}) + + set(CMNETWORK_ROOT_INCLUDE_PATH ${CMNETWORK_ROOT_PATH}/inc) + set(CMNETWORK_ROOT_SOURCE_PATH ${CMNETWORK_ROOT_PATH}/src) + + include_directories(${CMNETWORK_ROOT_INCLUDE_PATH}) +endmacro() diff --git a/src/AcceptServer.cpp b/src/AcceptServer.cpp new file mode 100644 index 0000000..c51a429 --- /dev/null +++ b/src/AcceptServer.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +namespace hgl +{ + void SetTimeVal(timeval &tv,const double t_sec); + + namespace network + { + /** + * 刷新服务器,并等待一个处理消息 + * @return >0 有用户接入 + * @return =0 正常,但无用户接入 + * @return <0 出错 + */ + int AcceptServer::Accept(IPAddress *addr) + { + if(!addr) + return(-1); + + socklen_t sockaddr_size=server_address->GetSockAddrInSize(); + + if(accept_timeout.tv_sec + ||accept_timeout.tv_usec) + { + int result; + + hgl_cpy(ato,accept_timeout); //下面的select会将数据清0,所以必须是复制一份出来用 + + FD_ZERO(&accept_set); + FD_SET(ThisSocket,&accept_set); + result=select(ThisSocket+1,&accept_set,nullptr,nullptr,&ato); + + if(result<=0) + return(0); + } + + int new_sock=accept(ThisSocket,addr->GetSockAddr(),&sockaddr_size); + + if(new_sock<0) + { + const int err=GetLastSocketError(); + + if(err==nseTimeOut //超时 + ||err==nseNoError // 0 没有错误 + ||err==4 //Interrupted system call(比如ctrl+c,一般DEBUG下才有) + ||err==11 //资源临时不可用 + ) + return(0); + + LOG_HINT(OS_TEXT("AcceptServer Accept error,errno=")+OSString(err)); + + if(err==nseTooManyLink) //太多的人accept + { + WaitTime(overload_wait); //强制等待指定时间 + return(0); + } + + return(-1); + } + + const int IP_STR_MAX_SIZE=server_address->GetIPStringMaxSize(); + + if(!ipstr) + ipstr=new char[IP_STR_MAX_SIZE+1]; + + addr->ToString(ipstr,IP_STR_MAX_SIZE); + + LOG_INFO(U8_TEXT("AcceptServer Accept IP:")+UTF8String(ipstr)+U8_TEXT(" ,sock:")+UTF8String(new_sock)); + + return(new_sock); + } + + void AcceptServer::SetTimeOut(const double time_out) + { + SetTimeVal(accept_timeout,time_out); + } + }//namespace network +}//namespace hgl diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..ea1d523 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,107 @@ +SET(NETWORK_BASE_SOURCE + IPAddress.cpp + Socket.cpp + ) + +SET(NETWORK_UDP_SOURCE + UdpSocket.cpp) + +IF(BUILD_NETWORK_UDP_LITE) + SET(NETWORK_UDP_SOURCE ${NETWORK_UDP_SOURCE} UdpLiteSocket.cpp) +ENDIF() + +SET(NETWORK_TCP_COMMON_SOURCE + TCPSocket.cpp + SocketInputStream.cpp + SocketOutputStream.cpp +) + +SET(NETWORK_TCP_CLIENT_SOURCE + TCPClient.cpp) + +SET(NETWORK_TCP_SERVER_SOURCE + ServerSocket.cpp + AcceptServer.cpp + MultiThreadAccept.cpp + TCPServer.cpp + TCPAccept.cpp + TCPAcceptPacket.cpp + SocketManage.cpp +) + +SET(NETWORK_SCTP_SOURCE + SCTPSocket.cpp + SCTPO2OClient.cpp + SCTPO2OServer.cpp + SCTPO2MServer.cpp +) + +SET(NETWORK_HTTP_SOURCE + HTTPInputStream.cpp +# HTTPOutputStream.cpp + HTTPTools.cpp + WebApi_Currency.cpp) + +SET(NETWORK_WEBSOCKET_SOURCE + WebSocket.cpp + WebSocketAccept.cpp) + +SOURCE_GROUP("Base" FILES ${NETWORK_BASE_SOURCE}) +SOURCE_GROUP("Transport\\UDP" FILES ${NETWORK_UDP_SOURCE}) +SOURCE_GROUP("Transport\\TCP" FILES ${NETWORK_TCP_COMMON_SOURCE}) +SOURCE_GROUP("Transport\\TCP\\Client" FILES ${NETWORK_TCP_CLIENT_SOURCE}) +SOURCE_GROUP("Transport\\TCP\\Server" FILES ${NETWORK_TCP_SERVER_SOURCE}) +SOURCE_GROUP("Application\\HTTP" FILES ${NETWORK_HTTP_SOURCE}) +SOURCE_GROUP("Application\\WebSocket" FILES ${NETWORK_WEBSOCKET_SOURCE}) + +IF(BUILD_NETWORK_SCTP) + SOURCE_GROUP("Transport\\SCTP" FILES ${NETWORK_SCTP_SOURCE}) + + SET(NETWORK_BASE_SOURCE ${NETWORK_BASE_SOURCE} ${NETWORK_SCTP_SOURCE}) +ENDIF(BUILD_NETWORK_SCTP) + +IF(WIN32) + SET(NETWORK_OS_SOURCE + SocketManageSelect.cpp + WinFireWall.cpp) +ENDIF(WIN32) + +IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + SET(NETWORK_OS_SOURCE SocketManageEpoll.cpp) +ENDIF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + +IF(APPLE) + SET(NETWORK_OS_SOURCE SocketManageKqueue.cpp) +elseif(APPLE) + IF(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + SET(NETWORK_OS_SOURCE SocketManageKqueue.cpp) + ENDIF(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + + IF(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") + SET(NETWORK_OS_SOURCE SocketManageKqueue.cpp) + ENDIF(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") + + IF(${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD") + SET(NETWORK_OS_SOURCE SocketManageKqueue.cpp) + ENDIF(${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD") +ENDIF(APPLE) + +SET(NETWORK_OS_SOURCE_GROUP ${CMAKE_SYSTEM_NAME}) +SOURCE_GROUP(${NETWORK_OS_SOURCE_GROUP} FILES ${NETWORK_OS_SOURCE}) + +SET(CM_NETWORK_ALL_SOURCE + ${NETWORK_BASE_SOURCE} + ${NETWORK_UDP_SOURCE} + ${NETWORK_TCP_COMMON_SOURCE} + ${NETWORK_TCP_CLIENT_SOURCE} + ${NETWORK_TCP_SERVER_SOURCE} + ${NETWORK_WEBSOCKET_SOURCE} + ${NETWORK_OS_SOURCE} + WebapiUserAgent.cpp) + + +IF(WIN32) + add_cm_library(CMNetwork "CM" ${CM_NETWORK_ALL_SOURCE}) +ELSE() + add_cm_library(CMNetwork "CM" ${CM_NETWORK_ALL_SOURCE} ${NETWORK_HTTP_SOURCE}) +ENDIF() diff --git a/src/HTTPInputStream.cpp b/src/HTTPInputStream.cpp new file mode 100644 index 0000000..416cb41 --- /dev/null +++ b/src/HTTPInputStream.cpp @@ -0,0 +1,271 @@ +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + namespace + { + constexpr char HTTP_REQUEST_HEADER_BEGIN[]= " HTTP/1.1\r\n" + "Host: "; + constexpr uint HTTP_REQUEST_HEADER_BEGIN_SIZE=sizeof(HTTP_REQUEST_HEADER_BEGIN)-1; + + constexpr char HTTP_REQUEST_HEADER_END[]= "\r\n" + "Accept: */*\r\n" + "User-Agent: Mozilla/5.0\r\n" + "Connection: Keep-Alive\r\n\r\n"; + + constexpr uint HTTP_REQUEST_HEADER_END_SIZE=sizeof(HTTP_REQUEST_HEADER_END)-1; + + constexpr uint HTTP_HEADER_BUFFER_SIZE=HGL_SIZE_1KB; + } + + HTTPInputStream::HTTPInputStream() + { + tcp=nullptr; + + pos=-1; + filelength=-1; + + tcp_is=nullptr; + + http_header=new char[HTTP_HEADER_BUFFER_SIZE]; + http_header_size=0; + + response_code=0; + } + + HTTPInputStream::~HTTPInputStream() + { + Close(); + delete[] http_header; + } + + /** + * 创建流并打开一个文件 + * @param host 服务器地址 www.hyzgame.org.cn 或 127.0.0.1 之类 + * @param filename 路径及文件名 /download/hgl.rar 之类 + * @return 打开文件是否成功 + */ + bool HTTPInputStream::Open(IPAddress *host_ip,const UTF8String &host_name,const UTF8String &filename) + { + Close(); + + response_code=0; + response_info.Clear(); + response_list.Clear(); + + if(!host_ip) + RETURN_FALSE; + + if(filename.IsEmpty()) + RETURN_FALSE; + + tcp=CreateTCPClient(host_ip); + + char *host_ip_str=host_ip->CreateString(); + SharedArray self_clear(host_ip_str); + + if(!tcp) + { + LOG_ERROR(U8_TEXT("Connect to HTTPServer failed: ")+UTF8String(host_ip_str)); + RETURN_FALSE; + } + + //设定为非堵塞模式 + tcp->SetBlock(false); + + //发送HTTP GET请求 + int len=0; + + len =strcpy(http_header ,HTTP_HEADER_BUFFER_SIZE ,"GET ",4); + len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,filename.c_str(), filename.Length()); + len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,HTTP_REQUEST_HEADER_BEGIN, HTTP_REQUEST_HEADER_BEGIN_SIZE); + len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,host_name.c_str(), host_name.Length()); + len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,HTTP_REQUEST_HEADER_END, HTTP_REQUEST_HEADER_END_SIZE); + + OutputStream *tcp_os=tcp->GetOutputStream(); + + if(tcp_os->WriteFully(http_header,len)!=len) + { + LOG_ERROR(U8_TEXT("Send HTTP Get Info failed:")+UTF8String(host_ip_str)); + delete tcp; + tcp=nullptr; + RETURN_FALSE; + } + + *http_header=0; + + tcp_is=tcp->GetInputStream(); + return(true); + } + + /** + * 关闭HTTP流 + */ + void HTTPInputStream::Close() + { + pos=0; + filelength=-1; + + SAFE_CLEAR(tcp); + + *http_header=0; + http_header_size=0; + } + + constexpr char HTTP_HEADER_SPLITE[]="\r\n"; + constexpr uint HTTP_HEADER_SPLITE_SIZE=sizeof(HTTP_HEADER_SPLITE)-1; + + void HTTPInputStream::ParseHttpResponse() + { + char *offset=strstr(http_header,http_header_size,HTTP_HEADER_SPLITE,HTTP_HEADER_SPLITE_SIZE); + + if(!offset) + return; + + response_info.Set(http_header,offset-http_header); + + char *first=strchr(http_header,' '); + + if(!first) + return; + + ++first; + char *second=strchr(first,' '); + + stou(first,second-first,response_code); + + while(true) + { + first=offset+HTTP_HEADER_SPLITE_SIZE; + + second=strchr(first,':',http_header_size-(first-http_header)); + + if(!second)break; + + UTF8String key; + UTF8String value; + + key.Set(first,second-first); + + first=second+2; + second=strstr(first,http_header_size-(first-http_header),HTTP_HEADER_SPLITE,HTTP_HEADER_SPLITE_SIZE); + + if(!second)break; + + value.Set(first,second-first); + offset=second; + + response_list.Add(key,value); + } + } + + constexpr char HTTP_HEADER_FINISH[]="\r\n\r\n"; + constexpr uint HTTP_HEADER_FINISH_SIZE=sizeof(HTTP_HEADER_FINISH)-1; + + constexpr char HTTP_CONTENT_LENGTH[]="Content-Length: "; + constexpr uint HTTP_CONTENT_LENGTH_SIZE=sizeof(HTTP_CONTENT_LENGTH)-1; + + int HTTPInputStream::PraseHttpHeader() + { + char *offset; + int size; + + offset=strstr(http_header,http_header_size,HTTP_HEADER_FINISH,HTTP_HEADER_FINISH_SIZE); + + if(!offset) + return 0; + + ParseHttpResponse(); + + *offset=0; + + size=http_header_size-(offset-http_header)-HTTP_HEADER_FINISH_SIZE; + + if(response_code==200) + { + offset=strstr(http_header,http_header_size,HTTP_CONTENT_LENGTH,HTTP_CONTENT_LENGTH_SIZE); + + if(offset) + { + offset+=HTTP_CONTENT_LENGTH_SIZE; + stou(offset,filelength); + } + + //有些HTTP下载就是不提供文件长度 + + pos=size; + return(pos); + } + else + { + LOG_ERROR(U8_TEXT("HTTPServer error info: ")+UTF8String(http_header)); + return(-1); + } + } + + int HTTPInputStream::ReturnError() + { + const int err=GetLastSocketError(); + + if(err==nseWouldBlock)return(0); //不能立即完成 + if(err==0)return(0); + + LOG_ERROR(OSString("Socket Error: ")+GetSocketString(err)); + + Close(); + RETURN_ERROR(-2); + } + + /** + * 从HTTP流中读取数据,但实际读取出来的数据长度不固定 + * @param buf 保存读出数据的缓冲区指针 + * @param bufsize 缓冲区长度,最小1024 + * @return >=0 实际读取出来的数据长度 + * @return -1 读取失败 + */ + int64 HTTPInputStream::Read(void *buf,int64 bufsize) + { + if(!tcp) + RETURN_ERROR(-1); + + int readsize; + + if(response_code==0) //HTTP头尚未解析完成 + { + readsize=tcp_is->Read(http_header+http_header_size,HTTP_HEADER_BUFFER_SIZE-http_header_size); + + if(readsize<=0) + return ReturnError(); + + http_header_size+=readsize; + + readsize=PraseHttpHeader(); + if(readsize==-1) + { + Close(); + RETURN_ERROR(-3); + } + + if(pos>0) + memcpy(buf,http_header+http_header_size-pos,pos); + + return(pos); + } + else + { + readsize=tcp_is->Read((char *)buf,bufsize); + + if(readsize<=0) + return ReturnError(); + + pos+=readsize; + return(readsize); + } + } + }//namespace network +}//namespace hgl diff --git a/src/HTTPOutputStream.cpp b/src/HTTPOutputStream.cpp new file mode 100644 index 0000000..b8ec73f --- /dev/null +++ b/src/HTTPOutputStream.cpp @@ -0,0 +1,66 @@ +/* +////////////////////////////////////////////////////////////////// +//SDK post (masterz) +/////////////////////////////////////////////////////////////////////////// +#include "stdafx.h" +#include "winsock.h" +#pragma comment(lib,"ws2_32.lib") +#define winsock_version 0x0101 +void main() +{ + //I create C:\Inetpub\wwwroot\test\test.asp ,start the web service + //start my program, the result is OK. + //If it works,it is written by masterz,otherwise I don't know who write it. + SOCKADDR_IN saServer; + LPHOSTENT lphostent; + WSADATA wsadata; + SOCKET hsocket; + int nRet; + const char* host_name="127.0.0.1"; + char* req="POST /test/test.asp HTTP/1.0\r\n" + "From: local\r\n" + "User-Agent: post_test/1.0\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 20\r\n\r\n" + "type=12345&name=aaaa"; + if(WSAStartup(winsock_version,&wsadata)) + printf("can't initial socket"); + lphostent=gethostbyname(host_name); + if(lphostent==nullptr) + printf("lphostent is null"); + hsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + saServer.sin_family = AF_INET; + // Use def. now, need to handle general case + saServer.sin_port = htons(80); + saServer.sin_addr = *((LPIN_ADDR)*lphostent->h_addr_list); + nRet = connect(hsocket, (LPSOCKADDR)&saServer, sizeof(SOCKADDR_IN)); + if (nRet == SOCKET_ERROR) + { + printf("can't connect"); + closesocket(hsocket); + return; + } + else + printf("connected with %s\n",host_name); + nRet = send(hsocket, req, strlen(req), 0); + if (nRet == SOCKET_ERROR) + { + printf("send() failed"); + closesocket(hsocket); + + } + else + printf("send() OK\n"); + char dest[1000]; + nRet=1; + while(nRet>0) + { + nRet=recv(hsocket,(LPSTR)dest,sizeof(dest),0); + if(nRet>0) + dest[nRet]=0; + else + dest[0]=0; + printf("\nReceived bytes:%d\n",nRet); + printf("Result:\n%s",dest); + } +} */ \ No newline at end of file diff --git a/src/HTTPTools.cpp b/src/HTTPTools.cpp new file mode 100644 index 0000000..4813faf --- /dev/null +++ b/src/HTTPTools.cpp @@ -0,0 +1,85 @@ +#include +#include +#include + +namespace hgl +{ + namespace network + { + namespace http + { + namespace + { + size_t http_get_to_output_stream(void *ptr,size_t size,size_t number,void *stream) + { + io::OutputStream *os=(io::OutputStream *)stream; + + int ss=size*number; + + os->WriteFully(ptr,ss); + + return ss; + } + } + + /** + * 执行http get指令 + * @param os 执行后下载到的数据输出流 + * @param url 要执行的网址 + * @return 下载下来的数据长度 + * @return <0 出错 + */ + int get(io::OutputStream *os,const char *url,const char *user_agent) + { + LOG_INFO(U8_TEXT("http get,url:")+UTF8String(url)); + + CURLcode res; + + CURL *curl=curl_easy_init(); + + if(!curl) + return(-1); + + int cur=os->Tell(); + + curl_easy_setopt(curl,CURLOPT_URL,url); + if(user_agent) + curl_easy_setopt(curl,CURLOPT_USERAGENT,user_agent); + curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1); //重定向支持 + curl_easy_setopt(curl,CURLOPT_TIMEOUT,30); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,os); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,http_get_to_output_stream); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + + return(os->Tell()-cur); + } + + int post(io::OutputStream *os,const char *url,const void *post_data,const int post_data_size,const char *user_agent) + { + CURLcode res; + + CURL *curl=curl_easy_init(); + + if(!curl) + return(-1); + + int cur=os->Tell(); + + curl_easy_setopt(curl,CURLOPT_URL,url); + if(user_agent) + curl_easy_setopt(curl,CURLOPT_USERAGENT,user_agent); + curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1); //重定向支持 + curl_easy_setopt(curl,CURLOPT_TIMEOUT,30); + curl_easy_setopt(curl,CURLOPT_POSTFIELDS,post_data); + curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,post_data_size); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,os); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,http_get_to_output_stream); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + + return(os->Tell()-cur); + } + }//namespace http + }//namespace network +}//namespace hgl diff --git a/src/IPAddress.cpp b/src/IPAddress.cpp new file mode 100644 index 0000000..cf64aae --- /dev/null +++ b/src/IPAddress.cpp @@ -0,0 +1,434 @@ +#include +#include + +namespace hgl +{ + namespace network + { + bool FillAddr(sockaddr_in &addr,const char *name,int socktype,int protocol) + { + if(name) + { + struct addrinfo hints, *answer; + + hgl_zero(hints); + hints.ai_family = AF_INET; + hints.ai_socktype=socktype; + hints.ai_protocol=protocol; + + if (getaddrinfo(name, nullptr, &hints, &answer)) //此函数最低WindowsXP SP2 + RETURN_FALSE; + + memcpy(&addr,answer->ai_addr,sizeof(sockaddr_in)); + freeaddrinfo(answer); + } + else + { + hgl_zero(addr); + + addr.sin_family=AF_INET; + } + + return(true); + } + + bool FillAddr(sockaddr_in6 &addr,const char *name,int socktype,int protocol) + { + if(name) + { + struct addrinfo hints, *answer; + + hgl_zero(hints); + hints.ai_family = AF_INET6; + hints.ai_socktype=socktype; + hints.ai_protocol=protocol; + + if (getaddrinfo(name, nullptr, &hints, &answer)) //此函数最低WindowsXP SP2 + RETURN_FALSE; + + memcpy(&addr,answer->ai_addr,sizeof(sockaddr_in6)); + freeaddrinfo(answer); + } + else + { + hgl_zero(addr); + + addr.sin6_family=AF_INET6; + } + + return(true); + } + + /** + * 绑定指定域名/IP和port到当前socket + * @param ThisSocket 要指定的当前socket + * @param addr 指定的ip/port + * @param reuse 是否复用当前地址,默认为1 + * @return 是否成功 + */ + template + bool BindAddr(int ThisSocket,const SockAddrIn &addr,int reuse=1) + { + const int val = reuse; + +#if HGL_OS == HGL_OS_Windows + setsockopt(ThisSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(BOOL)); //win下的BOOL本质也是int,所以唯一区分只在于val的传入类型 +#else + setsockopt(ThisSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)); +#endif//HGL_OS == HGL_OS_Windows + + if (bind(ThisSocket, (SockAddr *)&addr, sizeof(SockAddrIn))) + { + LOG_ERROR(OS_TEXT("Bind Socket Error! errno: ") + OSString(GetLastSocketError())); + return(false); + } + + return(true); + } + + bool GetHostname(UTF8String &name,const sockaddr *addr) + { + char hostname[NI_MAXHOST]; + char server_info[NI_MAXSERV]; + + if(getnameinfo(addr,sizeof(struct sockaddr),hostname,NI_MAXHOST,server_info,NI_MAXSERV,NI_NUMERICSERV)) + return(false); + + name=hostname; + return(true); + } + + void AddAddrToList(List &addr_list, const sockaddr_in *sai) + { + addr_list.Add(sai->sin_addr); + } + + void AddAddrToList(List &addr_list, const sockaddr_in6 *sai) + { + addr_list.Add(sai->sin6_addr); + } + + template + int GetIPList(List &addr_list,const char *addr_string, int socktype,int protocol) + { + struct addrinfo hints, *answer, *ptr; + + hgl_zero(hints); + hints.ai_family = FAMILY; + hints.ai_socktype=socktype; + hints.ai_protocol=protocol; + + if (getaddrinfo(addr_string, nullptr, &hints, &answer)) //此函数最低Windows 2003/Vista + return(-1); + + int count = 0; + for (ptr = answer; ptr; ptr = ptr->ai_next) + { + AddAddrToList(addr_list,(SockAddrIn *)(ptr->ai_addr)); + ++count; + } + + freeaddrinfo(answer); + return(count); + } + + /** + * 返回本机的IP支持情况 + * @return 支持的协议数量 + * @return -1 出错,无法获取主机名称 + * @return -2 出错,无法获取地址信息 + */ + int GetIPSupport(List &ipsl) + { + char hostname[NI_MAXHOST]; + + if(gethostname(hostname, NI_MAXHOST)) + return(-1); + + struct addrinfo hints, *answer, *ptr; + + hgl_zero(hints); + + if (getaddrinfo(hostname, nullptr, &hints, &answer)) //此函数最低Windows 2003/Vista + return(-1); + + int count = 0; + + for (ptr = answer; ptr; ptr = ptr->ai_next) + { + IPSupport s; + + s.family =ptr->ai_family; + s.socktype =ptr->ai_socktype; + s.protocol =ptr->ai_protocol; + + if(ptr->ai_family==AF_INET) + { + memcpy(&(s.ipv4),ptr->ai_addr,ptr->ai_addrlen); + + inet_ntop(AF_INET,&(s.ipv4.sin_addr),s.ipv4str,INET_ADDRSTRLEN); + } + else + if(ptr->ai_family==AF_INET6) + { + memcpy(&(s.ipv6),ptr->ai_addr,ptr->ai_addrlen); + + inet_ntop(AF_INET6,&(s.ipv6.sin6_addr),s.ipv6str,INET6_ADDRSTRLEN); + } + + ipsl.Add(s); + + ++count; + } + + freeaddrinfo(answer); + return(count); + } + + /** + * 检测这个IP类型是否在支持列表中 + * @param ips_list IP协议支持列表 + * @param family IP家族 + * @param socktype socket类型 + * @param protocol 协议类型 + * @return 是否支持 + */ + bool CheckIPSupport(const List &ips_list,uint family,uint socktype,uint protocol) + { + int count=ips_list.GetCount(); + + if(count<=0) + return(false); + + const IPSupport *ips=ips_list.GetData(); + + while(count--) + { + if(ips->family==family + &&ips->socktype==socktype + &&ips->protocol==protocol) + return(true); + + ++ips; + } + + return(false); + } + + /** + * 检测本机是否支持指定协议 + * @param family IP家族 + * @param socktype socket类型 + * @param protocol 协议类型 + * @return 是否支持 + */ + bool CheckIPSupport(uint family,uint socktype,uint protocol) + { + List ips_list; + + const int count=GetIPSupport(ips_list); + + if(count<=0)return(false); + + return CheckIPSupport(ips_list,family,socktype,protocol); + } + + namespace + { + const char *protocol_name_tcp="TCP"; + const char *protocol_name_udp="UDP"; + const char *protocol_name_udp_lite="UDPLite"; + const char *protocol_name_sctp="SCTP"; + const char *protocol_name_unknow="unknow protocol"; + } + + void IPAddress::RefreshProtocolName() + { + if(IsTCP ())protocol_name=protocol_name_tcp;else + if(IsUDP ())protocol_name=protocol_name_udp;else + if(IsUDPLite())protocol_name=protocol_name_udp_lite;else + if(IsSCTP ())protocol_name=protocol_name_sctp;else + protocol_name=protocol_name_unknow; + } + }//namespace network + + namespace network + { + bool IPv4Address::Set(const char *name,ushort port,int _socktype,int _protocol) + { + socktype=_socktype; + protocol=_protocol; + + if(!FillAddr(addr,name,socktype,protocol)) + RETURN_FALSE; + + addr.sin_port=htons(port); + RefreshProtocolName(); + return(true); + } + + void IPv4Address::Set(ushort port) + { + hgl_zero(addr); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + } + + bool IPv4Address::Bind(int ThisSocket,int reuse)const{return BindAddr(ThisSocket,addr,reuse);} + bool IPv4Address::GetHostname(UTF8String &name)const{return hgl::network::GetHostname(name,(sockaddr *)&addr);} + + const ushort IPv4Address::GetPort()const{return addr.sin_port;} + + void IPv4Address::ToString(char *str,const int max_size,const in_addr *ip_addr) + { + inet_ntop(AF_INET,(void *)&ip_addr,str,INET_ADDRSTRLEN); + } + + void IPv4Address::ToString(char *str,const int max_size,const sockaddr_in *ip_addr) + { + ToString(str,max_size,&(ip_addr->sin_addr)); + + int size=strlen(str); + str[size]=':'; + + hgl::utos(str+size+1,max_size-1-size,ip_addr->sin_port); + } + + void IPv4Address::ToString(char *str,int max_size)const + { + ToString(str,max_size,&addr); + } + + /** + * 取得指定域名的IPv4地址列表 + * @param addr_list 存放结果的地址列表 + * @param domain 域名或地址字符串 + * @param _socktype Socket类型(可以为SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_RDM、SOCK_SEQPACKET等值),默认为所有类型。 + * @param _protocol 协议类型(可以为IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP),默认为所有类型。 + * @return 地址个数,-1表示出错 + */ + int IPv4Address::GetDomainIPList(List &addr_list,const char *domain,int socktype,int protocol) + { + return GetIPList(addr_list,domain,socktype,protocol); + } + + /** + * 取得本机的IPv4地址列表 + * @param addr_list 存放结果的地址列表 + * @param _socktype Socket类型(可以为SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_RDM、SOCK_SEQPACKET等值),默认为所有类型。 + * @param _protocol 协议类型(可以为IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP),默认为所有类型。 + * @return 地址个数,-1表示出错 + */ + int IPv4Address::GetLocalIPList(List &addr_list,int _socktype,int _protocol) + { + char hostname[NI_MAXHOST]; + + if(gethostname(hostname, NI_MAXHOST)) + return(-1); + + return GetDomainIPList(addr_list,hostname,_socktype,_protocol); + } + + bool IPv4Address::Comp(const IPAddress *ipa)const + { + if(this==ipa)return(true); + if(!ipa)return(false); + + if(ipa->GetFamily()!=AF_INET)return(false); + if(ipa->GetProtocol()!=protocol)return(false); + + return (memcmp(&addr,&(((IPv4Address *)ipa)->addr),sizeof(sockaddr_in))==0); + } + }//namespace network + + namespace network + { + bool IPv6Address::Set(const char *name,ushort port,int _socktype,int _protocol) + { + socktype=_socktype; + protocol=_protocol; + + if(!FillAddr(addr,name,socktype,protocol)) + RETURN_FALSE; + + addr.sin6_port=htons(port); + RefreshProtocolName(); + return(true); + } + + void IPv6Address::Set(ushort port) + { + hgl_zero(addr); + + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + } + + bool IPv6Address::Bind(int ThisSocket,int reuse)const{return BindAddr(ThisSocket,addr,reuse);} + bool IPv6Address::GetHostname(UTF8String &name)const{return hgl::network::GetHostname(name,(sockaddr *)&addr);} + + const ushort IPv6Address::GetPort()const{return addr.sin6_port;} + + void IPv6Address::ToString(char *str,const int max_size,const in6_addr *ip_addr) + { + inet_ntop(AF_INET6, (void *)&ip_addr,str,INET6_ADDRSTRLEN); + } + + void IPv6Address::ToString(char *str,const int max_size,const sockaddr_in6 *ip_addr) + { + ToString(str,max_size,&(ip_addr->sin6_addr)); + + int size=strlen(str); + str[size]=':'; + + hgl::utos(str+size+1,max_size-1-size,ip_addr->sin6_port); + } + + void IPv6Address::ToString(char *str,const int max_size)const + { + IPv6Address::ToString(str,max_size,&addr); + } + + /** + * 取得指定域名的IPv6地址列表 + * @param addr_list 存放结果的地址列表 + * @param domain 域名或地址字符串 + * @param _socktype Socket类型(可以为SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_RDM、SOCK_SEQPACKET等值),默认为所有类型。 + * @param _protocol 协议类型(可以为IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP),默认为所有类型。 + * @return 地址个数,-1表示出错 + */ + int IPv6Address::GetDomainIPList(List &addr_list,const char *domain,int socktype,int protocol) + { + return GetIPList(addr_list,domain,socktype,protocol); + } + + /** + * 取得本机的IPv6地址列表 + * @param addr_list 存放结果的地址列表 + * @param _socktype Socket类型(可以为SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_RDM、SOCK_SEQPACKET等值),默认为所有类型。 + * @param _protocol 协议类型(可以为IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP),默认为所有类型。 + * @return 地址个数,-1表示出错 + */ + int IPv6Address::GetLocalIPList(List &addr_list,int _socktype,int _protocol) + { + char hostname[NI_MAXHOST]; + + if(gethostname(hostname, NI_MAXHOST)) + return(-1); + + return GetDomainIPList(addr_list,hostname,_socktype,_protocol); + } + + bool IPv6Address::Comp(const IPAddress *ipa)const + { + if(this==ipa)return(true); + if(!ipa)return(false); + + if(ipa->GetFamily()!=AF_INET6)return(false); + if(ipa->GetProtocol()!=protocol)return(false); + + return (memcmp(&addr,&(((IPv6Address *)ipa)->addr),sizeof(sockaddr_in6))==0); + } + }//namespace network +}//namespace hgl diff --git a/src/MultiThreadAccept.cpp b/src/MultiThreadAccept.cpp new file mode 100644 index 0000000..aa8a618 --- /dev/null +++ b/src/MultiThreadAccept.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +namespace hgl +{ + namespace network + { + namespace + { + void PreMalloc(IPAddressStack &ip_stack,ServerSocket *server) + { + int count=ip_stack.GetCount(); + + ip_stack.SetMax(count+1024); + + server->CreateIPAddress(ip_stack.GetData()+count,1024); + + ip_stack.SetCount(count+1024); + } + }//namespace + + AcceptThread::AcceptThread(AcceptServer *as,Semaphore *sem) + { + server=as; + active_semaphore=sem; + + if(!server) + return; + + PreMalloc(ip_stack,server); + } + + bool AcceptThread::Execute() + { + if(!server)return(false); + + IPAddress *client_ip; + int client_sock; + + if(ip_stack.GetCount()<=0) + PreMalloc(ip_stack,server); + + ip_stack.Pop(client_ip); + + client_sock=server->Accept(client_ip); + + if(client_sock<0) + { + ip_stack.Push(client_ip); + return(false); + } + + active_semaphore->Post(); //发一个信号证明自己活着 + + if(client_sock>0) + { + if(!OnAccept(client_sock,client_ip)) + CloseSocket(client_sock); + } + + return(true); + }//bool AcceptThread::Execute() + }//namespace network +}//namespace hgl diff --git a/src/SCTPO2MServer.cpp b/src/SCTPO2MServer.cpp new file mode 100644 index 0000000..3652569 --- /dev/null +++ b/src/SCTPO2MServer.cpp @@ -0,0 +1,72 @@ +#include +#include +#include + +namespace hgl +{ + namespace network + { + /** + * 创建服务器 + * @param addr 服务器地址 + * @param max_listen 最大监听数量(指同一时间在未处理的情况下,最多有多少个连接可以被处理。注:并非越大越好) + * @return 创建服务器是否成功 + */ + bool SCTPO2MServer::CreateServer(const sockaddr_in &addr,const uint max_listen) + { + if(!SCTPO2MSocket::Create()) + return(false); + + if(!BindAddr(ThisSocket,addr)) + { + CloseSocket(); + return(false); + } + + listen(ThisSocket,max_listen); + + return(true); + } + + /** + * 创建服务器 + * @param hostname 指要监听的服务器地址 + * @param port 指要监听的服务器端口 + * @param max_listen 最大监听数量(指同一时间在未处理的情况下,最多有多少个连接可以被处理。注:并非越大越好) + * @return 创建服务器是否成功 + */ + bool SCTPO2MServer::CreateServer(const char *hostname,uint port,const uint max_listen) + { + sockaddr_in addr; + + if(!FillAddr(&addr,hostname,port)) + return(false); + + if(!CreateServer(addr,max_listen)) + { + LOG_HINT(OS_TEXT("ServerSocket::CreateServer failed,Listen Port: ")+OSString(port)); + + return(false); + } + + LOG_HINT(u8"ServerSocket Listen Address: "+UTF8String(hostname)); + LOG_HINT(OS_TEXT("ServerSocket Listen Port: ")+OSString(port)); + + return(true); + } + + void SCTPO2MServer::CloseServer() + { + CloseSocket(); + } + + /** + * 设置自动关闭无通信的客户端 + * @param to 超时时间 + */ + void SCTPO2MServer::SetAutoClose(const int to) + { + setsockopt(ThisSocket,IPPROTO_SCTP,SCTP_AUTOCLOSE,&to,sizeof(int)); + } + }//namespace network +}//namespace hgl diff --git a/src/SCTPO2OClient.cpp b/src/SCTPO2OClient.cpp new file mode 100644 index 0000000..1e8c598 --- /dev/null +++ b/src/SCTPO2OClient.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +namespace hgl +{ + namespace network + { + SCTPO2OClient::SCTPO2OClient() + { + } + + SCTPO2OClient::SCTPO2OClient(int sock) + { + UseSocket(sock); + } + + SCTPO2OClient::~SCTPO2OClient() + { + } + + /** + * 连接到服务器 + * @param addr 服务器地址 + * @return 是否连接成功 + */ + bool SCTPO2OClient::Connect(const sockaddr_in &addr) + { + CloseSocket(); + + if(!CreateSocket(AF_INET,SOCK_STREAM,IPPROTO_SCTP)) + return(false); + + if(connect(ThisSocket,(sockaddr *)&addr,sizeof(addr))) + { + os_char ipstr[32]; + + SockToStr(addr,ipstr,true); + + LOG_HINT(OS_TEXT("Don't Connect to SCTPServer ")+OSString(ipstr)); + CloseSocket(); + return(false); + } + + // LOG_INFO(u"connect "+u8_to_u16(host)+u':'+UTF16String(port)+u" ok,socket:"+UTF16String(ThisSocket)); + + InitDataIOEvent(); + GetMsg(); + + //SetBlock(true,TimeOut); //阻塞模式 + + return(true); + } + + /** + * 连接到服务器 + * @param host 服务器地址 + * @param port 端口 + * @return 是否连接成功 + */ + bool SCTPO2OClient::Connect(const char *host,int port) + { + sockaddr_in addr; + + if(FillAddr(&addr,host,port)==false) + { + LOG_HINT(OS_TEXT("SCTP connect error in ")+OSString(host)+OS_TEXT(":")+OSString(port)); + CloseSocket(); + return(false); + } + + return Connect(addr); + } + }//namespace network +}//namespace hgl diff --git a/src/SCTPO2OServer.cpp b/src/SCTPO2OServer.cpp new file mode 100644 index 0000000..b96e0c5 --- /dev/null +++ b/src/SCTPO2OServer.cpp @@ -0,0 +1,12 @@ +#include + +namespace hgl +{ + namespace network + { + int SCTPO2OServer::CreateServerSocket() + { + return socket(AF_INET,SOCK_STREAM,IPPROTO_SCTP); + } + }//namespace network +}//namespace hgl diff --git a/src/SCTPSocket.cpp b/src/SCTPSocket.cpp new file mode 100644 index 0000000..41a1162 --- /dev/null +++ b/src/SCTPSocket.cpp @@ -0,0 +1,227 @@ +#include + +namespace hgl +{ + namespace network + { + SCTPSocket::SCTPSocket() + { + memset(&address,0,sizeof(sockaddr_in)); + } + + bool SCTPSocket::InitDataIOEvent() + { + struct sctp_event_subscribe events; + + hgl_zero(events); + + events.sctp_data_io_event=1; + + return(setsockopt(ThisSocket,SOL_SCTP,SCTP_EVENTS,(const void *)&events,sizeof(events))!=-1); + } + + bool SCTPSocket::GetMsg() + { + sctp_initmsg msg; + socklen_t len; + + len=sizeof(sctp_initmsg); + + if(getsockopt(ThisSocket,IPPROTO_SCTP,SCTP_INITMSG,(void *)&msg,&len)==-1) + return(false); + + out_max_stream =msg.sinit_num_ostreams; + in_max_stream =msg.sinit_max_instreams; + init_max_attempts =msg.sinit_max_attempts; + init_max_init_timeo =msg.sinit_max_init_timeo; + + return(true); + } + + /** + * 初始化sctp socket + * @param this_socket socket号 + * @param out_stream 允许发送的最大sctp流数(传入0表示不设置) + * @param in_stream 允许输入的最大sctp流数(-1表示同out_stream,0表示不设置) + * @param attempts 初始化重传多少次才认为不可达(0表示不设置) + * @param init_time_out init定时器超时时间单位为毫秒(0表示不设置) + * @return 是否成功 + */ + bool SCTPSocket::InitMsg(int out_stream,int in_stream,int attempts,int init_time_out) + { + sctp_initmsg msg; + + if(in_stream==-1) + in_stream=out_stream; + + msg.sinit_num_ostreams=out_stream; //允许发送的最大sctp流数 + msg.sinit_max_instreams=in_stream; //允许输入的最大sctp流数 + msg.sinit_max_attempts=attempts; //重传多少次才认为不可达(0表示不设置) + msg.sinit_max_init_timeo=init_time_out; //重传超时时间,单位毫秒(0表示不设置) + + return(setsockopt(ThisSocket,IPPROTO_SCTP,SCTP_INITMSG,(void *)&msg,sizeof(sctp_initmsg))!=-1); + } + + /** + * 设置是否使用无延迟模式(注:无延迟模式也无缓冲,有利于小包高响应应用,大包或无高响应需要求应用不要设置) + * @param no_delay 是否无延迟 + */ + bool SCTPSocket::SetNodelay(bool no_delay) + { + int flag=(no_delay?1:0); + + return(setsockopt(ThisSocket,IPPROTO_SCTP,SCTP_NODELAY,(void *)&flag,sizeof(flag))!=-1); + } + + bool SCTPSocket::SetMaxStream(int ms) + { + out_max_stream=ms; + in_max_stream=ms; + + return InitMsg(ThisSocket,ms,ms); + } + + void SCTPSocket::UseSocket(int s,const sockaddr_in *sa) + { + ThisSocket=s; + + InitDataIOEvent(); + GetMsg(); + + if(sa) + memcpy(&address,sa,sizeof(sockaddr_in)); + else + memset(&address,0,sizeof(sockaddr_in)); + } + + /** + * 发送一个数据包 + * @param buf 数据包内存指针 + * @param len 数据包字节数 + * @param stream 数据流编号 + * @return 是否发送成功 + */ + bool SCTPO2OSocket::SendMsg(const void *buf,int len,uint16 stream) + { + if(!buf||len<=0) + return(false); + + if(stream>=out_max_stream) + return(false); + + size_t result_size=sctp_sendmsg(ThisSocket,buf,len, + nullptr, //目标地址,一对一模式不需要设置 + 0, //上一个参数的长度 + 0, //ppid 净荷协议标识符 + 0, //flags + stream, // + 0, //time to live 生命周期 + 0); //context 上下文 + //两段代码效果一样 +// hgl_zero(sri_send); +// sri_send.sinfo_stream=stream; +// +// size_t result_size=sctp_send(ThisSocket,buf,len,&sri_send,0); + + return(result_size==len); + } + + /** + * 接收一个数据包 + * @param stream 数据流编号 + * @return 是否接收成功 + */ + bool SCTPO2OSocket::RecvMsg(MemBlock *mb,uint16 &stream) + { + if(!mb)return(false); + + int flags; + int len; + int off=0; + + hgl_zero(sri_recv); + + while(true) + { + len=sctp_recvmsg(ThisSocket,mb->GetData()+off,mb->GetMaxBytes()-off, + nullptr, //来源地址,一对一模式不需要 + 0, //上一个参数的长度 + &sri_recv, + &flags); + + if(len<0) + return(false); + + std::cout<<"sctp_recvmsg return "<SetLength(len); + stream=sri_recv.sinfo_stream; + return(true); + } + + off+=len; + + mb->SetLength(mb->GetMaxLength()+1024); + } + } + + bool SCTPO2MSocket::Create() + { + int s=socket(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP); + + if(s<0) + return(false); + + UseSocket(s); + return(true); + } + + bool SCTPO2MSocket::SendMsg(const sockaddr_in *sa,void *buf,int len,uint16 stream) + { + if(!sa||!buf||len<=0) + return(false); + + const socklen_t sal=sizeof(sockaddr_in); + + size_t result_size=sctp_sendmsg(ThisSocket,buf,len, + (sockaddr *)sa, //目标地址 + sal, //上一个参数的长度 + 0, //ppid + 0, //flags + stream, // + 0, //time to live + 0); //context + + return(result_size==len); + } + + bool SCTPO2MSocket::RecvMsg(sockaddr_in &sa,void *buf,int max_len,int &len,uint16 &stream) + { + struct sctp_sndrcvinfo sndrcvinfo; + int flags; + socklen_t sal=sizeof(sockaddr_in); + + hgl_zero(sndrcvinfo); + + len=sctp_recvmsg(ThisSocket,buf,max_len, + (sockaddr *)&sa, //来源地址 + &sal, //上一个参数的长度 + &sndrcvinfo, + &flags); + + if(len<0) + return(false); + + stream=sndrcvinfo.sinfo_stream; + return(true); + } + }//namespace network +}//namespace hgl diff --git a/src/ServerSocket.cpp b/src/ServerSocket.cpp new file mode 100644 index 0000000..6195aa8 --- /dev/null +++ b/src/ServerSocket.cpp @@ -0,0 +1,88 @@ +#include +#include + +namespace hgl +{ + namespace network + { + ServerSocket::ServerSocket() + { + ThisSocket=-1; + server_address=nullptr; + } + + ServerSocket::~ServerSocket() + { + CloseServer(); + } + + /** + * 创建多个空的IP地址空间 + * @param ip_buffer 存放IP地址空间的缓冲区 + * @param count 需要产生的IP地址空间数量 + * @return 是否创建成功 + */ + bool ServerSocket::CreateIPAddress(IPAddress **ip_buffer,int count)const + { + if(!server_address)return(false); + if(!ip_buffer)return(false); + if(count<=0)return(false); + + for(int i=0;iCreate(); + ip_buffer++; + } + + return(true); + } + + /** + * 创建服务器 + * @param addr 服务器地址 + * @param max_listen 最大监听数量(指同一时间在未处理的情况下,最多有多少个连接可以被处理。注:并非越大越好) + * @param reuse 是否可以复用这个IP地址,默认为假 + * @return 创建服务器是否成功 + */ + bool ServerSocket::CreateServer(const IPAddress *addr,const uint max_listen,bool reuse) + { + ThisSocket=CreateServerSocket(); + + if(ThisSocket<0) + { + LOG_HINT(OS_TEXT("Create TCP Server Socket Failed!")); + return(false); + } + + if(!addr->Bind(ThisSocket,reuse)) + { + CloseSocket(ThisSocket); + return(false); + } + + server_address=addr->CreateCopy(); + + listen(ThisSocket,max_listen); + + return(true); + } + + void ServerSocket::CloseServer() + { + CloseSocket(ThisSocket); + ThisSocket=-1; + SAFE_CLEAR(server_address); + } + + bool ServerSocket::SetIPv6Only(bool only) + { + if (ThisSocket == -1)return(false); + + if (server_address->GetFamily() != AF_INET6)return(false); + + int on = only?1:0; + + return(setsockopt(ThisSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, sizeof(on)) == 0); + } + }//namespace network +}//namespace hgl diff --git a/src/Socket.cpp b/src/Socket.cpp new file mode 100644 index 0000000..19868d1 --- /dev/null +++ b/src/Socket.cpp @@ -0,0 +1,497 @@ +#include +#include +#include +#include + +#if HGL_OS != HGL_OS_Windows +#include + +#if HGL_OS == HGL_OS_Solaris + #include +#endif//HGL_OS == HGL_OS_Solaris +#endif//HGL_OS != HGL_OS_Windows + +//setsockopt函数的Windows下使用参数格式请参见http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx + +namespace hgl +{ + void SetTimeVal(timeval &tv,const double t_sec); + + namespace network + { +#if HGL_OS == HGL_OS_Windows + namespace + { + static bool winsocket_init=false; + }//namespace + + bool InitWinSocket() + { + if(winsocket_init)return(true); + + WSADATA wsa; + + winsocket_init=(WSAStartup(MAKEWORD(2,2),&wsa)==NO_ERROR); + + return(winsocket_init); + } +#endif//HGL_OS == HGL_OS_Windows + + int CreateSocket(const IPAddress *addr) + { + if(!addr) + RETURN_ERROR(-1); + + #if HGL_OS == HGL_OS_Windows + if(!InitWinSocket()) + RETURN_ERROR(-2); + #endif//HGL_OS == HGL_OS_Windows + + const int protocol=addr->GetProtocol(); + + int s=socket(addr->GetFamily(), + addr->GetSocketType(), + addr->GetProtocol()); + + if(s<0) + { + const int sock_error=GetLastSocketError(); //在这里定义一个,是为了调试方便可以查看 + + LOG_ERROR(OS_TEXT("CreateSocket(domain=")+OSString(addr->GetFamily())+ + OS_TEXT(",type=")+OSString(addr->GetSocketType())+ + OS_TEXT(",protocol=")+OSString(protocol)+ + OS_TEXT(") return ")+OSString(s)+ + OS_TEXT("; errno ")+OSString(sock_error)); + + RETURN_ERROR(-3); + } + + LOG_INFO(U8_TEXT("Create ")+UTF8String(addr->GetProtocolName())+U8_TEXT(" Socket OK: ")+UTF8String(s)); + + return s; + } + + /** + * 连接当前socket到指定地址 + */ + bool Connect(int sock,IPAddress *addr) + { + if(sock<0||!addr) + RETURN_FALSE; + + if(connect(sock,addr->GetSockAddr(),addr->GetSockAddrInSize())) + RETURN_FALSE; + + return(true); + } + +// static atom_int socket_count=0; + + Socket::Socket() + { + ThisAddress=nullptr; + ThisSocket=-1; + +// LOG_INFO(u8"Socket Count ++: "+UTF8String(++socket_count)); + } + + Socket::Socket(int sock,const IPAddress *addr) + { + ThisSocket=sock; + + if(addr) + ThisAddress=addr->CreateCopy(); + else + ThisAddress=nullptr; + } + + Socket::~Socket() + { + CloseSocket(); + +// LOG_INFO(u8"Socket Count --: "+UTF8String(--socket_count)); + } + + bool Socket::InitSocket(const IPAddress *addr) + { + if(!addr) + RETURN_FALSE; + + SAFE_CLEAR(ThisAddress); + + #if HGL_OS == HGL_OS_Windows + if(!InitWinSocket()) + RETURN_FALSE; + #endif//HGL_OS == HGL_OS_Windows + + ThisSocket=CreateSocket(addr); + + if(ThisSocket<0) + RETURN_FALSE; + + ThisAddress=addr->CreateCopy(); + return(true); + } + + /** + * 使用一个现有的Socket + */ + bool Socket::UseSocket(int sock,const IPAddress *addr) + { + if(sock<0||!addr) + RETURN_FALSE; + + SAFE_CLEAR(ThisAddress); + + ThisSocket=sock; + ThisAddress=addr->CreateCopy(); + + return(true); + } + + /** + * 使用现有的地址重新创建Socket + */ + bool Socket::ReCreateSocket() + { + if(!ThisAddress) + RETURN_FALSE; + + if(ThisSocket!=-1) + hgl::CloseSocket(ThisSocket); + + ThisSocket=CreateSocket(ThisAddress); + + if(ThisSocket!=-1) + return(true); + + RETURN_FALSE; + } + + /** + * 关闭连接 + */ + void Socket::CloseSocket() + { + if(ThisSocket==-1) + return; + + hgl::CloseSocket(ThisSocket); + ThisSocket=-1; + } + + struct SocketErrorMessage //socket错误信息 + { + int number; //编号 + os_char recountal[256]; //详细叙述 + }; + + const SocketErrorMessage tab[]= + { + {0, OS_TEXT("没有错误")}, + +#if HGL_OS != HGL_OS_Windows +// //errno-base.h + + {EPERM ,OS_TEXT("Operation not permitted")}, + {ENOENT ,OS_TEXT("No such file or directory")}, + {ESRCH ,OS_TEXT("No such process")}, + {EINTR ,OS_TEXT("Interrupted system call")}, + {EIO ,OS_TEXT("I/O error")}, + {ENXIO ,OS_TEXT("No such device or address")}, + {E2BIG ,OS_TEXT("Argument list too long")}, + {ENOEXEC ,OS_TEXT("Exec format error")}, + {EBADF ,OS_TEXT("Bad file number")}, + {ECHILD ,OS_TEXT("No child processes")}, + {EAGAIN ,OS_TEXT("Try again")}, + {ENOMEM ,OS_TEXT("Out of memory")}, + {EACCES ,OS_TEXT("Permission denied")}, + {EFAULT ,OS_TEXT("Bad address")}, + {ENOTBLK ,OS_TEXT("Block device required")}, + {EBUSY ,OS_TEXT("Device or resource busy")}, + {EEXIST ,OS_TEXT("File exists")}, + {EXDEV ,OS_TEXT("Cross-device link")}, + {ENODEV ,OS_TEXT("No such device")}, + {ENOTDIR ,OS_TEXT("Not a directory")}, + {EISDIR ,OS_TEXT("Is a directory")}, + {EINVAL ,OS_TEXT("Invalid argument")}, + {ENFILE ,OS_TEXT("File table overflow")}, + {EMFILE ,OS_TEXT("Too many open files")}, + {ENOTTY ,OS_TEXT("Not a typewriter")}, + {ETXTBSY ,OS_TEXT("Text file busy")}, + {EFBIG ,OS_TEXT("File too large")}, + {ENOSPC ,OS_TEXT("No space left on device")}, + {ESPIPE ,OS_TEXT("Illegal seek")}, + {EROFS ,OS_TEXT("Read-only file system")}, + {EMLINK ,OS_TEXT("Too many links")}, + {EPIPE ,OS_TEXT("Broken pipe")}, + {EDOM ,OS_TEXT("Math argument out of domain of func")}, + {ERANGE ,OS_TEXT("Math result not representable")}, + +// //errno.h +// + {EDEADLK ,OS_TEXT("Resource deadlock would occur")}, + {ENAMETOOLONG ,OS_TEXT("File name too long")}, + {ENOLCK ,OS_TEXT("No record locks available")}, + {ENOSYS ,OS_TEXT("Function not implemented")}, + {ENOTEMPTY ,OS_TEXT("Directory not empty")}, + {ELOOP ,OS_TEXT("Too many symbolic links encountered")}, + + {ENOMSG ,OS_TEXT("No message of desired type")}, + {EIDRM ,OS_TEXT("Identifier removed")}, + + {ENOSTR ,OS_TEXT("Device not a stream")}, + {ENODATA ,OS_TEXT("No data available")}, + {ETIME ,OS_TEXT("Timer expired")}, + {ENOSR ,OS_TEXT("Out of streams resources")}, + + {EREMOTE ,OS_TEXT("Object is remote")}, + {ENOLINK ,OS_TEXT("Link has been severed")}, + + {EPROTO ,OS_TEXT("Protocol error")}, + {EMULTIHOP ,OS_TEXT("Multihop attempted")}, + + {EBADMSG ,OS_TEXT("Not a data message")}, + {EOVERFLOW ,OS_TEXT("Value too large for defined data type")}, + {EILSEQ ,OS_TEXT("Illegal byte sequence")}, + {EUSERS ,OS_TEXT("Too many users")}, + {ENOTSOCK ,OS_TEXT("Socket operation on non-socket")}, + {EDESTADDRREQ ,OS_TEXT("Destination address required")}, + {EMSGSIZE ,OS_TEXT("Message too long")}, + {EPROTOTYPE ,OS_TEXT("Protocol wrong type for socket")}, + {ENOPROTOOPT ,OS_TEXT("Protocol not available")}, + {EPROTONOSUPPORT,OS_TEXT("Protocol not supported")}, + {ESOCKTNOSUPPORT,OS_TEXT("Socket type not supported")}, + {EOPNOTSUPP ,OS_TEXT("Operation not supported on transport endpoint")}, + {EPFNOSUPPORT ,OS_TEXT("Protocol family not supported")}, + {EAFNOSUPPORT ,OS_TEXT("Address family not supported by protocol")}, + {EADDRINUSE ,OS_TEXT("Address already in use")}, + {EADDRNOTAVAIL ,OS_TEXT("Cannot assign requested address")}, + {ENETDOWN ,OS_TEXT("Network is down")}, + {ENETUNREACH ,OS_TEXT("Network is unreachable")}, + {ENETRESET ,OS_TEXT("Network dropped connection because of reset")}, + {ECONNABORTED ,OS_TEXT("Software caused connection abort")}, + {ECONNRESET ,OS_TEXT("Connection reset by peer")}, + {ENOBUFS ,OS_TEXT("No buffer space available")}, + {EISCONN ,OS_TEXT("Transport endpoint is already connected")}, + {ENOTCONN ,OS_TEXT("Transport endpoint is not connected")}, + {ESHUTDOWN ,OS_TEXT("Cannot send after transport endpoint shutdown")}, + {ETOOMANYREFS ,OS_TEXT("Too many references: cannot splice")}, + {ETIMEDOUT ,OS_TEXT("Connection timed out")}, + {ECONNREFUSED ,OS_TEXT("Connection refused")}, + {EHOSTDOWN ,OS_TEXT("Host is down")}, + {EHOSTUNREACH ,OS_TEXT("No route to host")}, + {EALREADY ,OS_TEXT("Operation already in progress")}, + {EINPROGRESS ,OS_TEXT("Operation now in progress")}, + {ESTALE ,OS_TEXT("Stale NFS file handle")}, + {EDQUOT ,OS_TEXT("Quota exceeded")}, + + {ECANCELED ,OS_TEXT("Operation Canceled")}, + + {EOWNERDEAD ,OS_TEXT("Owner died")}, + {ENOTRECOVERABLE,OS_TEXT("State not recoverable")}, + + #ifndef HGL_OS_BSD + {ECHRNG ,OS_TEXT("Channel number out of range")}, + {EL2NSYNC ,OS_TEXT("Level 2 not synchronized")}, + {EL3HLT ,OS_TEXT("Level 3 halted")}, + {EL3RST ,OS_TEXT("Level 3 reset")}, + {ELNRNG ,OS_TEXT("Link number out of range")}, + {EUNATCH ,OS_TEXT("Protocol driver not attached")}, + {ENOCSI ,OS_TEXT("No CSI structure available")}, + {EL2HLT ,OS_TEXT("Level 2 halted")}, + {EBADE ,OS_TEXT("Invalid exchange")}, + {EBADR ,OS_TEXT("Invalid request descriptor")}, + {EXFULL ,OS_TEXT("Exchange full")}, + {ENOANO ,OS_TEXT("No anode")}, + {EBADRQC ,OS_TEXT("Invalid request code")}, + {EBADSLT ,OS_TEXT("Invalid slot")}, + + {EBFONT ,OS_TEXT("Bad font file format")}, + + {ENONET ,OS_TEXT("Machine is not on the network")}, + {ENOPKG ,OS_TEXT("Package not installed")}, + {EADV ,OS_TEXT("Advertise error")}, + {ESRMNT ,OS_TEXT("Srmount error")}, + + {ECOMM ,OS_TEXT("Communication error on send")}, + {EDOTDOT ,OS_TEXT("RFS specific error")}, + {ENOTUNIQ ,OS_TEXT("Name not unique on network")}, + {EBADFD ,OS_TEXT("File descriptor in bad state")}, + {EREMCHG ,OS_TEXT("Remote address changed")}, + {ELIBACC ,OS_TEXT("Can not access a needed shared library")}, + {ELIBBAD ,OS_TEXT("Accessing a corrupted shared library")}, + {ELIBSCN ,OS_TEXT(".lib section in a.out corrupted")}, + {ELIBMAX ,OS_TEXT("Attempting to link in too many shared libraries")}, + {ELIBEXEC ,OS_TEXT("Cannot exec a shared library directly")}, + {ERESTART ,OS_TEXT("Interrupted system call should be restarted")}, + {ESTRPIPE ,OS_TEXT("Streams pipe error")}, + {EUCLEAN ,OS_TEXT("Structure needs cleaning")}, + {ENOTNAM ,OS_TEXT("Not a XENIX named type file")}, + {ENAVAIL ,OS_TEXT("No XENIX semaphores available")}, + {EISNAM ,OS_TEXT("Is a named type file")}, + {EREMOTEIO ,OS_TEXT("Remote I/O error")}, + {ENOMEDIUM ,OS_TEXT("No medium found")}, + {EMEDIUMTYPE ,OS_TEXT("Wrong medium type")}, + + {ENOKEY ,OS_TEXT("Required key not available")}, + {EKEYEXPIRED ,OS_TEXT("Key has expired")}, + {EKEYREVOKED ,OS_TEXT("Key has been revoked")}, + {EKEYREJECTED ,OS_TEXT("Key was rejected by service")}, + {ERFKILL ,OS_TEXT("Operation not possible due to RF-kill")}, + + {EHWPOISON ,OS_TEXT("Memory page has hardware error")}, + + #endif// + +#else + {10004, OS_TEXT("阻塞操作被函数WSACancelBlockingCall ()调用所中断")}, + {10013, OS_TEXT("试图使用被禁止的访问权限去访问套接字")}, + {10014, OS_TEXT("系统检测到调用试图使用的一个指针参数指向的是一个非法指针地址")}, + {10022, OS_TEXT("提供了非法参数")}, + {10024, OS_TEXT("打开了太多的套接字")}, + {10035, OS_TEXT("当前操作在非阻塞套接字上不能立即完成")}, + {10036, OS_TEXT("一个阻塞操作正在执行")}, + {10037, OS_TEXT("被调用的套接字当前有操作正在进行")}, + {10038, OS_TEXT("操作试图不是在套接字上进行")}, + {10039, OS_TEXT("在套接字上一个操作所必须的地址被遗漏")}, + {10040, OS_TEXT("在数据报套接字上发送的一个消息大于内部消息缓冲区或一些其它网络限制,或者是用来接受数据报的缓冲区小于数据报本身")}, + {10041, OS_TEXT("在socket()函数调用中指定的协议不支持请求的套接字类型的语义")}, + {10042, OS_TEXT("在getsockopt()或setsockopt()调用中,指定了一个未知的、非法的或不支持的选项或层(level)")}, + {10043, OS_TEXT("请求的协议没有在系统中配置或没有支持它的实现存在")}, + {10044, OS_TEXT("不支持在此地址族中指定的套接字类型")}, + {10045, OS_TEXT("对于引用的对象的类型来说,试图进行的操作不支持")}, + {10046, OS_TEXT("协议簇没有在系统中配置或没有支持它的实现存在")}, + {10047, OS_TEXT("使用的地址与被请求的协议不兼容")}, + {10048, OS_TEXT("重复使用一个套接字地址(协议/IP地址/端口号)")}, + {10049, OS_TEXT("被请求的地址在它的环境中是不合法的")}, + {10050, OS_TEXT("套接字操作遇到一个不活动的网络")}, + {10051, OS_TEXT("试图和一个无法到达的网络进行套接字操作")}, + {10052, OS_TEXT("在操作正在进行时连接因“keep-alive”检测到失败而中断")}, + {10053, OS_TEXT("一个已建立的连接被你的主机上的软件终止")}, + {10054, OS_TEXT("存在的连接被远程主机强制关闭")}, + {10055, OS_TEXT("由于系统缺乏足够的缓冲区空间,或因为队列已满")}, + {10056, OS_TEXT("连接请求发生在已经连接的套接字上")}, + {10057, OS_TEXT("因为套接字没有连接,发送或接收数据的请求不被允许,或者是使用sendto()函数在数据报套接字上发送时没有提供地址")}, + {10058, OS_TEXT("因为套接字在相应方向上已经被先前的shutdown()调用关闭,因此该方向上的发送或接收请求不被允许")}, + {10060, OS_TEXT("连接请求因被连接方在一个时间周期内不能正确响应而失败,或已经建立的连接因被连接的主机不能响应而失败")}, + {10061, OS_TEXT("因为目标主机主动拒绝,连接不能建立")}, + {10064, OS_TEXT("套接字操作因为目的主机关闭而失败返回")}, + {10065, OS_TEXT("试图和一个不可达主机进行套接字操作")}, + {10067, OS_TEXT("太多的socket应用程序")}, + {10091, OS_TEXT("此时Windows Sockets实现因底层用来提供网络服务的系统不可用")}, + {10093, OS_TEXT("应用程序没有调用WSAStartup()函数,或函数WSAStartup()调用失败了")}, + {10101, OS_TEXT("远端已经初始化了一个“雅致”的关闭序列")}, + {10109, OS_TEXT("指定的类没有找到")}, + {11001, OS_TEXT("主机未知")}, + {11002, OS_TEXT("在主机名解析时的临时错误,它意味着本地服务器没有从授权服务器接收到一个响应")}, + {11003, OS_TEXT("在数据库查找时发生了某种不可恢复错误")}, + {11004, OS_TEXT("请求的名字合法并且在数据库中找到了,但它没有正确的关联数据用于解析")}, + {11092, OS_TEXT("当前的Windows Sockets实现不支持应用程序指定的Windows Sockets规范版本")}, +#endif//HGL_OS != HGL_OS_Windows + + {-1, OS_TEXT("未知错误")}, + }; + + /** + * 取得SOCKET错误详细信息字符串 + * @param n 错误编号 + * @return 详细信息字符串 + */ + const os_char *GetSocketString(int n) + { + int i=0; + + while(tab[i].number!=-1) + { + if(tab[i].number==n) + return tab[i].recountal; + + i++; + } + + return(tab[i].recountal); //此时tab[i].number==-1 + } + + /** + * @param ThisSocket 要关闭的socket + */ + void CloseSocket(int ThisSocket) + { + if(ThisSocket<=-1)return; + + #ifdef _WIN32 + closesocket(ThisSocket); + + LOG_INFO(OS_TEXT("CloseSocket: ") + OSString(ThisSocket)); + #else + int result; + + do + { + shutdown(ThisSocket,SHUT_RDWR); //终止读写操作 + + result=close(ThisSocket); + + LOG_INFO(OS_TEXT("CloseSocket: ")+OSString(ThisSocket)+OS_TEXT(",result:")+OSString(result)+OS_TEXT(",errno: ")+OSString(errno)); + + if(errno==EBADF)break; + if(errno==EINPROGRESS)continue; + }while(result); + + LOG_INFO(OS_TEXT("CloseSocket: ") + OSString(ThisSocket) + OS_TEXT(",result:") + OSString(result)); + #endif//_WIN32 + } + + /** + * 设置是否使用堵塞方式传输 + * @param ThisSocket 要设置的socket + * @param block 是否使用堵塞方式(true/false) + * @param send_time_out 发送数据超时时间(单位: 秒) + * @param recv_time_out 接收数据超时时间(单位: 秒) + */ + void SetSocketBlock(int ThisSocket,bool block,double send_time_out,double recv_time_out) + { + #if HGL_OS == HGL_OS_Windows + u_long par = (block ? 0 : 1); + + DWORD stv=send_time_out*1000; + DWORD rtv=recv_time_out*1000; + + ioctlsocket(ThisSocket, FIONBIO, &par); + + setsockopt(ThisSocket,SOL_SOCKET,SO_RCVTIMEO,(char *)&rtv,sizeof(DWORD)); + setsockopt(ThisSocket,SOL_SOCKET,SO_SNDTIMEO,(char *)&stv,sizeof(DWORD)); + #else + int par=(block?0:1); + + timeval stv,rtv; + + SetTimeVal(stv,send_time_out); + SetTimeVal(rtv,recv_time_out); + + ioctl(ThisSocket,FIONBIO,&par); + + setsockopt(ThisSocket,SOL_SOCKET,SO_RCVTIMEO,(char *)&rtv,sizeof(timeval)); + setsockopt(ThisSocket,SOL_SOCKET,SO_SNDTIMEO,(char *)&stv,sizeof(timeval)); + #endif//HGL_OS == HGL_OS_Windows + } + + void SetSocketLinger(int ThisSocket,int time_out) + { + struct linger so_linger; + + if(time_out>0) + { + so_linger.l_onoff=1; + so_linger.l_linger=time_out; + } + else + { + so_linger.l_onoff=0; + } + + setsockopt(ThisSocket,SOL_SOCKET,SO_LINGER,(const char *)&so_linger,sizeof(linger)); + } + }//namespace network +}//namespace hgl diff --git a/src/SocketInputStream.cpp b/src/SocketInputStream.cpp new file mode 100644 index 0000000..1100219 --- /dev/null +++ b/src/SocketInputStream.cpp @@ -0,0 +1,249 @@ +#include +#include +#include +#include +#include +namespace hgl +{ + namespace network + { + bool Read(io::DataInputStream *dis,sockaddr_in &addr) + { + if(dis->ReadUint8((uint8 *)&(addr.sin_addr.s_addr),4)!=4) + return(false); + + return dis->ReadUint16(addr.sin_port); + } + }//namespace network + + namespace network + { + SocketInputStream::SocketInputStream(int s) + { +// LOG_INFO(OS_TEXT("SocketInputStream::SocketInputStream(")+OSString(s)+OS_TEXT(")")); + + SetSocket(s); + mb=new MemBlock(); + } + + SocketInputStream::~SocketInputStream() + { +// LOG_INFO(OS_TEXT("SocketInputStream::~SocketInputStream(")+OSString(sock)+OS_TEXT(")")); + + SAFE_CLEAR(mb); + } + + /** + * 从socket中读取指定的字节数 + * @param buf 数据保存缓冲区 + * @param size 预想读取的字节数 + * @return 成功读取的字节数 + * @return -1 失败 + */ + int64 SocketInputStream::Read(void *buf,int64 size) + { + if(sock==-1) + { + LOG_ERROR(OS_TEXT("SocketInputStream::Read() fatal error,sock=-1")); + return(-1); + } + + if(size==0)return(0); + if(size<0) + { + LOG_ERROR(OS_TEXT("SocketInputStream::Read() fatal error,size<0,sock=")+OSString(sock)); + return(-3); + } + + if(!buf) + { + LOG_ERROR(OS_TEXT("SocketInputStream::Read() fatal error,buf=nullptr,sock=")+OSString(sock)); + return(-2); + } + + const int64 result=recv(sock,(char *)buf,size,0); + + if(result>0) + { + total+=result; + +// LOG_INFO(OS_TEXT("Socket ")+OSString(sock)+OS_TEXT(" recv ")+OSString(size)+OS_TEXT(" bytes ok,result ")+OSString(result)+OS_TEXT(" total recv ")+OSString(total)+OS_TEXT(" bytes.")); + } + else if(result<0) + { + int err=GetLastSocketError(); + + if(err==nseWouldBlock) + return 0; + + LOG_INFO(OS_TEXT("Socket ")+OSString(sock)+OS_TEXT(" recv ")+OSString(size)+OS_TEXT(" bytes failed,error: ")+OSString(err)+OS_TEXT(",")+GetSocketString(err)); + } + + return(result); + } + + /** + * 从socket中读取指定的字节数,但不从缓存队列中删除 + * @param buf 数据保存缓冲区 + * @param size 预想读取的字节数 + * @return 成功读取的字节数 + * @return -1 失败 + */ + int64 SocketInputStream::Peek(void *buf,int64 size) + { + if(sock==-1) + { + LOG_ERROR(OS_TEXT("SocketInputStream::Peek() fatal error,sock=-1")); + return(-1); + } + + if(!buf) + { + LOG_ERROR(OS_TEXT("SocketInputStream::Peek() fatal error,buf=nullptr,sock=")+OSString(sock)); + return(-2); + } + + if(size<=0) + { + LOG_ERROR(OS_TEXT("SocketInputStream::Peek() fatal error,size<=0,sock=")+OSString(sock)); + return(-3); + } + + return recv(sock,(char *)buf,size,MSG_PEEK); + } + + /** + * 从socket中充分读取指定的字节数,无视超时,直接读满指定字节数为止 + * @param buf 数据保存缓冲区 + * @param size 预想读取的字节数 + * @return 成功读取的字节数 + * @return -1 失败 + */ + int64 SocketInputStream::ReadFully(void *buf,int64 size) + { + if(sock==-1) + { + LOG_ERROR(OS_TEXT("SocketInputStream::ReadFully() fatal error,sock=-1")); + return(-1); + } + + if(size==0)return(0); + if(size<0) + { + LOG_ERROR(OS_TEXT("SocketInputStream::ReadFully() fatal error,size<0,sock=")+OSString(sock)); + return(-3); + } + + if(!buf) + { + LOG_ERROR(OS_TEXT("SocketInputStream::ReadFully() fatal error,buf=nullptr,sock=")+OSString(sock)); + return(-2); + } + + bool to_first=true; + int err; + const os_char *err_str; + char *p=(char *)buf; + +#if HGL_OS == HGL_OS_Windows + int result = 0; + int left_bytes = size; +#else + ssize_t result = 0; + size_t left_bytes = size; +#endif//HGL_OS == HGL_OS_Windows + +// const double start_time=GetDoubleTime(); + + while(left_bytes>0) + { + //如果最后一个参数使用MSG_WAITALL,则无论是否阻塞模式,都会永远阻塞 + //使用0则会得出一个11号nseTryAgain错误 + result=recv(sock,p,left_bytes,0); //似乎windows 2003才开始支持MSG_WAITALL + + if(result==0) //linux下返回0即为对方断开连接,win/bsd下未验证 + { + sock=-1; + break; + } + + if(result<0) + { + err=GetLastSocketError(); + +// std::cerr<<"SocketInputStream::ReadFully error,Socket:"<SetLength(n); + + return ReadFully(mb->data(),n); + } + + int64 SocketInputStream::Available()const + { + int recv_buf_size=0; + socklen_t len=sizeof(int); + + getsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char *)&recv_buf_size,&len); //这个函数会出错,但我们不理会 + + return recv_buf_size; + } + }//namespace network +}//namespace hgl diff --git a/src/SocketManage.cpp b/src/SocketManage.cpp new file mode 100644 index 0000000..0c9ac50 --- /dev/null +++ b/src/SocketManage.cpp @@ -0,0 +1,223 @@ +#include +#include +#include"SocketManageBase.h" + +namespace hgl +{ + namespace network + { + SocketManage::SocketManage(int max_user) + { + manage=CreateSocketManageBase(max_user); + } + + SocketManage::~SocketManage() + { + delete manage; + } + + void SocketManage::ProcSocketRecvList() + { + const int count=sock_recv_list.GetCount(); + + if(count<=0)return; + + SocketEvent *se=sock_recv_list.GetData(); + TCPAccept *sock; + + for(int i=0;isock,sock)) + { + if(sock->OnSocketRecv(se->error)<0) + { + LOG_INFO(OS_TEXT("OnSocketRecv return Error,sock:")+OSString(se->sock)); + error_set.Add(sock); + } + } + else + { + LOG_ERROR(OS_TEXT("Fatal error,can't find socket in SocketList,sock is ")+OSString(se->sock)); + } + + ++se; + } + + sock_recv_list.ClearData(); + } + + void SocketManage::ProcSocketSendList() + { + const int count=sock_send_list.GetCount(); + + if(count<=0)return; + + SocketEvent *se=sock_send_list.GetData(); + TCPAccept *sock; + + for(int i=0;isock,sock)) + { + if(sock->OnSocketSend(se->size)<0) + { + LOG_INFO(OS_TEXT("OnSocketSend return Error,sock:")+OSString(se->sock)); + error_set.Add(sock); + } + } + else + { + LOG_ERROR(OS_TEXT("Fatal error,can't find socket in SocketList,sock is ")+OSString(se->sock)); + } + + ++se; + } + + sock_send_list.ClearData(); + } + + void SocketManage::ProcSocketErrorList() + { + const int count=sock_error_list.GetCount(); + + if(count<=0)return; + + SocketEvent *se=sock_error_list.GetData(); + TCPAccept *sock; + + for(int i=0;isock,sock)) + { + LOG_INFO(OS_TEXT("SocketError,sock:")+OSString(se->sock)+OS_TEXT(",errno:")+OSString(se->error)); + sock->OnSocketError(se->error); + error_set.Add(sock); + } + else + { + LOG_ERROR(OS_TEXT("Fatal error,can't find socket in SocketList,sock is ")+OSString(se->sock)); + } + + ++se; + } + + sock_error_list.ClearData(); + } + + void SocketManage::ProcErrorList() + { + const int count=error_set.GetCount(); + + if(count<=0)return; + + TCPAccept **sp=error_set.GetData(); + + for(int i=0;iThisSocket,s)) + { + LOG_ERROR(OS_TEXT("repeat append socket to manage,sock:")+OSString(s->ThisSocket)); + return(false); + } + + if(!manage->Join(s->ThisSocket)) + { + socket_list.DeleteByKey(s->ThisSocket); + return(false); + } + + return(true); + } + + int SocketManage::Join(TCPAccept **s_list,int count) + { + if(!s_list||count<=0) + return(-1); + + int total=0; + + for(int i=0;iJoin(s_list[i])) + ++total; + } + + return total; + } + + bool SocketManage::Unjoin(TCPAccept *s) + { + if(!s)return(false); + + if(!socket_list.DeleteByKey(s->ThisSocket)) + { + LOG_ERROR(OS_TEXT("socket don't in SocketManage,sock:")+OSString(s->ThisSocket)); + return(false); + } + + manage->Unjoin(s->ThisSocket); //unjoin理论上不存在失败 + + return(true); + } + + int SocketManage::Unjoin(TCPAccept **s_list,int count) + { + if(!s_list||count<=0) + return(-1); + + int total=0; + + for(int i=0;iUnjoin(s_list[i])) + ++total; + } + + return total; + } + + int SocketManage::Update(const double &time_out) + { + //将error_set放在这里,是为了保留它给外面的调用者使用 + error_set.ClearData(); + + const int count=manage->Update(time_out,sock_recv_list,sock_send_list,sock_error_list); + + if(count<=0) + return(count); + + ProcSocketSendList(); //这是上一帧的,所以先发。未来可能考虑改成另建一批线程发送 + ProcSocketRecvList(); + ProcSocketErrorList(); + ProcErrorList(); //这里仅仅是将Socket从列表中移除,并没有删掉。 + + return count; + } + + void SocketManage::Clear() + { + const int count=socket_list.GetCount(); + auto **us=socket_list.GetDataList(); + + for(int i=0;iright); + + ++us; + } + + socket_list.ClearData(); + } + }//namespace network +}//namespace hgl diff --git a/src/SocketManageBase.h b/src/SocketManageBase.h new file mode 100644 index 0000000..b72fbd3 --- /dev/null +++ b/src/SocketManageBase.h @@ -0,0 +1,35 @@ +#ifndef HGL_NETWORK_SOCKET_MANAGE_BASE_INCLUDE +#define HGL_NETWORK_SOCKET_MANAGE_BASE_INCLUDE + +#include +namespace hgl +{ + namespace network + { + /** + * Socket基础管理 + */ + class SocketManageBase + { + public: + + virtual ~SocketManageBase()=default; + + virtual bool Join(int)=0; ///<加入一个Socket +// virtual bool Join(const int *,int)=0; ///<加入一批Socket + virtual bool Unjoin(int)=0; ///<分离一个Socket +// virtual bool Unjoin(const int *,int)=0; ///<分离一批Socket + + virtual int GetCount()const=0; ///<取得Socket数量 + virtual void Clear()=0; ///<清除所有Socket + + virtual int Update(const double &, + SocketEventList &, + SocketEventList &, + SocketEventList &)=0; ///<轮循刷新所有socket + };//class SocketManageBase + + SocketManageBase *CreateSocketManageBase(int max_user); ///<创建一个Socket基础管理器 + }//namespace network +}//namespace hgl +#endif//HGL_NETWORK_SERVER_ACCEPT_MANAGE_BASE_INCLUDE diff --git a/src/SocketManageEpoll.cpp b/src/SocketManageEpoll.cpp new file mode 100644 index 0000000..1f9091b --- /dev/null +++ b/src/SocketManageEpoll.cpp @@ -0,0 +1,255 @@ +#include"SocketManageBase.h" +#include +#include + +#include +#include + +namespace hgl +{ + namespace network + { + class SocketManageEpoll:public SocketManageBase + { + protected: + + int epoll_fd; + uint user_event; + + int max_connect; + int cur_count; + + protected: + + epoll_event *event_list; + + private: + + bool epoll_add(int sock) + { + epoll_event ev; + + hgl_zero(ev); + + ev.data.fd= sock; + ev.events= user_event //要处理的事件 + |EPOLLET //边缘模式(即读/写时,需要一直读/写直到出错为止;相对LT模式是只要有数据就会一直通知) + |EPOLLERR //出错 + |EPOLLRDHUP //对方挂断 + |EPOLLHUP; //挂断 + + return(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&ev)==0); + } + + bool epoll_del(int sock) + { + //在内核版本 2.6.9 之前,EPOLL_CTL_DEL 要求一个 event 是非空的指针,尽管这个参数会被忽略。 + //从 Linux 2.6.9 开始,在执行 EPOLL_CTL_DEL 时 event 可以指定为 NULL。 + //我们不支持Linux 2.6.9之前的版本,所以直接传NULL + return(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,sock,nullptr)==0); + } + + public: + + SocketManageEpoll(int epfd,uint ue,int mc) + { + epoll_fd=epfd; + user_event=ue; + + max_connect=mc; + cur_count=0; + + event_list=new epoll_event[max_connect]; + } + + ~SocketManageEpoll() + { + delete[] event_list; + + close(epoll_fd); + } + + bool Join(int sock) override + { + epoll_add(sock); + + SetSocketBlock(sock,false); + + ++cur_count; + + LOG_INFO(OS_TEXT("SocketManageEpoll::Join() Socket:")+OSString(sock)); + + return(true); + } + +// bool Join(const int *sock_list,int count) override +// { +// if(count<=0)return(false); +// +// for(int i=0;ievent_list; + + SocketEvent *rp=recv_list.GetData(); + SocketEvent *sp=send_list.GetData(); + SocketEvent *ep=error_list.GetData(); + + int recv_num=0; + int send_num=0; + int error_num=0; + + for(int i=0;ievents&( EPOLLERR| //出错了 + EPOLLRDHUP| //对方关了 + EPOLLHUP)) //我方强制关了 + { + LOG_ERROR("SocketManageEpoll Error,socket:"+OSString(ee->data.fd)+",epoll event:"+OSString(ee->events)); + + ep->sock=ee->data.fd; + ep->error=ee->events; + ++ep; + ++error_num; + } + else + if(ee->events&EPOLLIN) //可以读数据 + { + rp->sock=ee->data.fd; + rp->size=0; + ++rp; + ++recv_num; + } + else + if(ee->events&EPOLLOUT) //可以发数据 + { + sp->sock=ee->data.fd; + sp->size=0; + ++sp; + ++send_num; + } + + ++ee; + } + + recv_list.SetCount(recv_num); + send_list.SetCount(send_num); + error_list.SetCount(error_num); + + return(event_count); + } + };//class SocketManageEpoll:public SocketManageBase + + SocketManageBase *CreateSocketManageBase(int max_user) + { + if(max_user<=0)return(nullptr); + + int epoll_fd=epoll_create(max_user); + + if(epoll_fd<0) + { + LOG_ERROR(OS_TEXT("epoll_create return error,errno is")+OSString(errno)); + return(nullptr); + } + + return(new SocketManageEpoll(epoll_fd,EPOLLIN,max_user)); //暂时只处理recv。。。至于send则直接强制发完,不走epoll + //return(new SocketManageEpoll(epoll_fd,EPOLLIN|EPOLLOUT,max_user)); + } + }//namespace network +}//namespace hgl diff --git a/src/SocketManageSelect.cpp b/src/SocketManageSelect.cpp new file mode 100644 index 0000000..1e918ce --- /dev/null +++ b/src/SocketManageSelect.cpp @@ -0,0 +1,150 @@ +#include"SocketManageBase.h" +#include +#include +#include +#include + +namespace hgl +{ + void SetTimeVal(timeval &tv,const double t_sec); + + namespace network + { + class SocketManageSelect:public SocketManageBase + { + int max_connect; + int cur_count; + + int max_fd; + + Set sock_id_list; + + fd_set fd_sock_list; //完整的sock列表 + + fd_set fd_recv_list; + fd_set fd_send_list; + fd_set fd_error_list; + + timeval time_out,*time_par; + + public: + + SocketManageSelect(int mc) + { + cur_count=0; + max_connect=mc; + max_fd=0; + + Clear(); + } + + bool Join(int sock) override + { + FD_SET(sock,&fd_sock_list); + + sock_id_list.Add(sock); + + if(sock>max_fd) + max_fd=sock; + + SetSocketBlock(sock,false); + + cur_count++; + + LOG_INFO(OS_TEXT("Join ")+OSString(sock)+OS_TEXT(" to SocketManageSelect")); + + return(true); + } + + bool Unjoin(int sock) override + { + cur_count--; + + FD_CLR(sock,&fd_sock_list); + + sock_id_list.Delete(sock); + + LOG_INFO(OS_TEXT("Unjoin ")+OSString(sock)+OS_TEXT(" from SocketManageSelect")); + + return(true); + } + + int GetCount()const override + { + return cur_count; + } + + void Clear() override + { + cur_count=0; + + max_fd=0; + + FD_ZERO(&fd_sock_list); + FD_ZERO(&fd_recv_list); + FD_ZERO(&fd_send_list); + FD_ZERO(&fd_error_list); + } + + int ConvertList(SocketEventList &sel,const fd_set &fs) + { + sel.SetCount(fs.fd_count); + + SocketEvent *p=sel.GetData(); + + for(uint i=0;isock=fs.fd_array[i]; + p->size=-1; + ++p; + } + + return fs.fd_count; + } + + int Update(const double &to,SocketEventList &recv_list,SocketEventList &send_list,SocketEventList &error_list) override + { + if(cur_count<=0) + return(0); + + if(to<=0) + time_par=nullptr; //传入nullptr表示阻塞模式 + else + { //时间为0表示非阻塞 + SetTimeVal(time_out,to); + + time_par=&time_out; + } + + memcpy(&fd_recv_list, &fd_sock_list,sizeof(fd_sock_list)); + memcpy(&fd_send_list, &fd_sock_list,sizeof(fd_sock_list)); + memcpy(&fd_error_list, &fd_sock_list,sizeof(fd_sock_list)); + + if(select(max_fd+1,&fd_recv_list,&fd_send_list,&fd_error_list,time_par)<0) + { + LOG_INFO(OS_TEXT("select return -1,errno: ")+OSString(errno)); + + if(errno==EBADF + ||errno==EFAULT + ||errno==EINVAL) + return(-1); + + return(0); + } + + ConvertList(recv_list,fd_recv_list); + ConvertList(send_list,fd_send_list); + ConvertList(error_list,fd_error_list); + + return(fd_recv_list.fd_count + +fd_send_list.fd_count + +fd_error_list.fd_count); + } + };//class SocketManageSelect:public SocketManageBase + + SocketManageBase *CreateSocketManageBase(int max_user) + { + return(new SocketManageSelect(max_user)); + } + }//namespace network +}//namespace hgl diff --git a/src/SocketOutputStream.cpp b/src/SocketOutputStream.cpp new file mode 100644 index 0000000..b28b6a6 --- /dev/null +++ b/src/SocketOutputStream.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +namespace hgl +{ + namespace network + { +// bool Write(io::DataOutputStream *dos,const sockaddr_in &addr) +// { +// #if (HGL_COMPILER == HGL_COMPILER_GNU)||(HGL_COMPILER == HGL_COMPILER_LLVM) +// if(dos->WriteUint8((uint8 *)&(addr.sin_addr.s_addr),4)!=4)return(false); +// #else +// #if (HGL_OS == HGL_OS_Windows) +// if(dos->WriteUint8((uint8 *)&(addr.sin_addr.S_addr),4)!=4)return(false); +// #endif// +// #endif// +// +// return dos->WriteUint16(addr.sin_port); +// } + }//namespace network + + namespace network + { + /** + * 向socket中写入指定的字节数 + * @param buf 数据缓冲区 + * @param size 预想写入的字节数 + * @return 成功写入的字节数 + * @return -1 失败 + */ + int64 SocketOutputStream::Write(const void *buf,int64 size) + { + if(sock==-1) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::Write() fatal error,sock=-1")); + return(-1); + } + + if(size==0)return(0); + if(size<0) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::Write() fatal error,size<0,sock=")+OSString(sock)); + return(-3); + } + + if(!buf) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::Write() fatal error,buf=nullptr,sock=")+OSString(sock)); + return(-2); + } + + const int64 result=send(sock,(char *)buf,size,0); + + if(result>0) + { + total+=result; + +// LOG_INFO(OS_TEXT("Socket ")+OSString(sock)+OS_TEXT(" send ")+OSString(size)+OS_TEXT(" bytes ok,result ")+OSString(result)+OS_TEXT(" total send ")+OSString(total)+OS_TEXT(" bytes.")); + } + else + { + int err=GetLastSocketError(); + + LOG_INFO(OS_TEXT("Socket ")+OSString(sock)+OS_TEXT(" send ")+OSString(size)+OS_TEXT(" bytes failed,,error: ")+OSString(err)+OS_TEXT(",")+GetSocketString(err)); + } + + return(result); + } + + /** + * 向socket中充分写入指定的字节数,无视超时,直接写完指定字节数为止 + * @param buf 数据缓冲区 + * @param size 预想写入的字节数 + * @return 成功写入的字节数 + * @return -1 失败 + */ + int64 SocketOutputStream::WriteFully(const void *buf,int64 size) + { + if(sock==-1) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::WriteFully() fatal error,sock=-1")); + return(-1); + } + + if(size==0)return(0); + + if(size<0) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::WriteFully() fatal error,size<0,sock=")+OSString(sock)); + return(-3); + } + + if(!buf) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::WriteFully() fatal error,buf=nullptr,sock=")+OSString(sock)); + return(-2); + } + + int err; + const os_char *err_str; + char *p=(char *)buf; + +// const double start_time=GetDoubleTime(); + +#if HGL_OS == HGL_OS_Windows + int result = 0; + int left_bytes = size; +#else + ssize_t result = 0; + size_t left_bytes = size; +#endif//HGL_OS == HGL_OS_Windows + + while(left_bytes>0) + { + result=send(sock,p,left_bytes,0); + +// LOG_ERROR(OS_TEXT("socket send result, left_bytes: ")+OSString(sock)+OSString(" , ")+OSString((int)result)+OSString(" , ")+OSString((int)left_bytes)); + if(result==0) + { + sock=-1; + break; + } + + if(result<0) + { + err=GetLastSocketError(); + + if(err==0 + ||err==nseTimeOut + ||err==nseWouldBlock + ||err==nseInt + ) + continue; + + if(err==nseBrokenPipe) + { + sock=-1; + break; + } + + err_str=GetSocketString(err); + if(err_str) + { + LOG_ERROR(OS_TEXT("SocketOutputStream::WriteFully error,Socket:")+OSString(sock)+OS_TEXT(",error code=")+OSString(err)+OS_TEXT(":")+OSString(err_str)); + } + else + { + LOG_ERROR(OS_TEXT("SocketOutputStream::WriteFully error,Socket:")+OSString(sock)+OS_TEXT(",error code=")+OSString(err)); + } + sock=-1; + break; + } + + p+=result; + left_bytes-=result; + } + +// const double end_time=GetDoubleTime(); + +// LOG_INFO(OS_TEXT("SocketOutputStream::WriteFully() time: ")+OSString(end_time-start_time)); + + total+=(p-(char *)buf); + + return(p-(char *)buf); + } + + int64 SocketOutputStream::Available()const + { + int send_buf_size=0; + socklen_t len=sizeof(int); + + getsockopt(sock,SOL_SOCKET,SO_SNDBUF,(char *)&send_buf_size,&len); //这个函数会出错,但我们不理会 + + return send_buf_size; + } + }//namespace network +}//namespace hgl diff --git a/src/TCPAccept.cpp b/src/TCPAccept.cpp new file mode 100644 index 0000000..db4116c --- /dev/null +++ b/src/TCPAccept.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + TCPAccept::~TCPAccept() + { + SAFE_CLEAR(sos); + SAFE_CLEAR(sis); + } + + bool TCPAccept::Send(void *data,const uint size) + { + if(!data)return(false); + if(size<=0)return(false); + + if(!sos) + sos=new SocketOutputStream(ThisSocket); + + int result=sos->WriteFully(data,size); + + if(result!=size) + return(false); + + return(true); + } + }//namespace network +}//namespace hgl diff --git a/src/TCPAcceptPacket.cpp b/src/TCPAcceptPacket.cpp new file mode 100644 index 0000000..3a1d425 --- /dev/null +++ b/src/TCPAcceptPacket.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + TCPAcceptPacket::TCPAcceptPacket():TCPAccept(),recv_buffer(HGL_TCP_BUFFER_SIZE) + { + } + + TCPAcceptPacket::TCPAcceptPacket(int s,IPAddress *ip):TCPAccept(s,ip),recv_buffer(HGL_TCP_BUFFER_SIZE) + { + } + + /** + * 从socket接收数据回调函数
此写法有待改良 + */ + int TCPAcceptPacket::OnSocketRecv(int /*size*/) + { + if(!sis) + { + sis=new SocketInputStream(ThisSocket); + recv_total=0; + recv_length=0; + } + + int total=0; + + while(true) + { + if(recv_lengthRead(recv_buffer.data()+recv_length,PACKET_SIZE_TYPE_BYTES-recv_length); + + if(result<0) + return(result); + + if(result==0) + return total; + + recv_length+=result; + recv_total+=result; + total+=result; + } + + if(recv_lengthRead(recv_buffer.data()+recv_length,pack_size-(recv_length-PACKET_SIZE_TYPE_BYTES)); + + if(result<0) + return(result); + + if(result==0) + return total; + + recv_length+=result; + recv_total+=result; + total+=result; + + if(recv_lengthWriteFully(&size,sizeof(PACKET_SIZE_TYPE)); + + if(result!=sizeof(PACKET_SIZE_TYPE)) + return(false); + + result=sos->WriteFully(data,size); + + if(result!=size) + return(false); + + return(true); + } + }//namespace network +}//namespace hgl diff --git a/src/TCPClient.cpp b/src/TCPClient.cpp new file mode 100644 index 0000000..fa99c2c --- /dev/null +++ b/src/TCPClient.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +/* +发送数据 + +模式一: + 理论:主线程每循环结束时 主动进行 接收缓冲区锁定、发送缓冲区锁定,如锁定成功,下一循环开始时将其提交到发送线程。 + 缺点:虽是多线程,但利用率不高,缓冲区仍是独占,而且长期独占 + +模式二: + 理论:每次发包时锁定缓冲区,发完解锁。 + 缺点:过于频繁加锁解锁,影响整体程序效能。 + +理想模式: + 独立的双缓冲区:主线程每次写入的为后备缓冲区,当每循环刷新或是缓冲区满时,锁定缓冲区,与发送线程交换或是复制到发送线程缓冲区。 +*/ + +/* +接收 + +接收线程模式(现有): + 理论:接收线程独立一个暂存缓冲区,先从socket接收到暂存缓冲区,然后锁定主缓冲区,复制完成后解锁。 + +主线程模式(旧方案): + 理论:每次读包时,锁定缓冲区,读完解锁。 + 缺点:过于频繁加锁解锁,影响整体程序效能。 + +主线程模式(备选方案): + 理论:每次访问开始锁定缓冲区,完全读走所有包后,再解锁。 + 缺点:长期锁定主缓冲区,会造成接收线程收到数据后无法写入,长时间会造成socket接收缓冲区暴满。 +*/ + +namespace hgl +{ + namespace network + { + void TCPClient::InitPrivate(int sock) + { + TimeOut=HGL_NETWORK_TIME_OUT; + + sis=new SocketInputStream(sock); + sos=new SocketOutputStream(sock); + + ipstr = nullptr; + } + + /** + * TCP客户端连接类构造函数 + */ + TCPClient::TCPClient():TCPSocket() + { + InitPrivate(-1); + } + + TCPClient::TCPClient(int sock,const IPAddress *addr):TCPSocket(sock,addr) + { + InitPrivate(sock); + } + + TCPClient::~TCPClient() + { + Disconnect(); + SAFE_CLEAR(sis); + SAFE_CLEAR(sos); + SAFE_CLEAR_ARRAY(ipstr); + } + + /** + * 连接到服务器 + * @return 是否连接成功 + */ + bool TCPClient::Connect() + { + if(ThisSocket<0||!ThisAddress)RETURN_FALSE; + + if(connect(ThisSocket,ThisAddress->GetSockAddr(),ThisAddress->GetSockAddrInSize())) + { + SAFE_CLEAR(ipstr); + ipstr=ThisAddress->CreateString(); + + LOG_HINT(U8_TEXT("Don't Connect to TCPServer ")+UTF8String(ipstr)); + this->CloseSocket(); + return(false); + } + +// LOG_INFO(u"connect "+u8_to_u16(host)+u':'+UTF16String(port)+u" ok,socket:"+UTF16String(ThisSocket)); + + SetBlock(true,TimeOut); //阻塞模式 + + ResetConnect(); + + return(true); + } + + /** + * 创建一个连接 + * @param addr 服务器地址 + */ + bool TCPClient::CreateConnect(const IPAddress *addr) + { + if(!addr) + RETURN_FALSE; + + if(!addr->IsTCP()) + RETURN_FALSE; + + if(!InitSocket(addr)) + RETURN_FALSE; + + return this->Connect(); + } + + /** + * 与服务器断开 + */ + void TCPClient::Disconnect() + { + SAFE_CLEAR_ARRAY(ipstr); + + if(ThisSocket==-1) + return; + + this->CloseSocket(); //两个线程会自动因为socket关闭而退出 + //注:线程退出会调用ThreadExit,再次调用Disconnect + } + + /** + * 使用指定socket + * @param sock 指定socket编号 + * @param addr socket地址 + */ + bool TCPClient::UseSocket(int sock,const IPAddress *addr) + { + if(!TCPSocket::UseSocket(sock,addr)) + RETURN_FALSE; + + ((SocketInputStream *)sis)->SetSocket(sock); + ((SocketOutputStream *)sos)->SetSocket(sock); + + return(true); + } + + TCPClient *CreateTCPClient(IPAddress *addr) + { + if(!addr)RETURN_ERROR_NULL; + + if(!addr->IsTCP()) + RETURN_ERROR_NULL; + + int sock=CreateSocket(addr); + + if(sock<0) + RETURN_ERROR_NULL; + + if(!Connect(sock,addr)) + { + CloseSocket(sock); + RETURN_ERROR_NULL; + } + + return(new TCPClient(sock,addr)); + } + }//namespace network +}//namespace hgl diff --git a/src/TCPServer.cpp b/src/TCPServer.cpp new file mode 100644 index 0000000..f8d3153 --- /dev/null +++ b/src/TCPServer.cpp @@ -0,0 +1,34 @@ +#include + +#if HGL_OS != HGL_OS_Windows +#include +#endif// + +namespace hgl +{ + namespace network + { + int TCPServer::CreateServerSocket() + { + //独立一个函数,是因为Windows下创建IOCP Server时,这里需要特殊实现 + return socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + } + +#if (HGL_OS != HGL_OS_Windows)&&(HGL_OS != HGL_OS_macOS)&&(HGL_OS != HGL_OS_iOS) + /** + * 设置推迟Accept(在接收到第一个数据后,才会Accept成功) + * @param time_out 收到数据的时间限制(单位秒) + */ + void TCPServer::SetDeferAccept(const int time_out) + { + #ifdef HGL_OS_BSD + constexpr struct accept_filter_arg af={"dataready",""}; + + setsockopt(this->ThisSocket,SOL_SOCKET,SO_ACCEPTFILTER,&af,sizeof(af)); + #else + setsockopt(this->ThisSocket,SOL_TCP,TCP_DEFER_ACCEPT,&time_out,sizeof(time_out)); + #endif//bsd&mac + } +#endif//no windows&mac + }//namespace network +}//namespace hgl diff --git a/src/TCPSocket.cpp b/src/TCPSocket.cpp new file mode 100644 index 0000000..ceb337a --- /dev/null +++ b/src/TCPSocket.cpp @@ -0,0 +1,123 @@ +#include +#include + +#if HGL_OS != HGL_OS_Windows +#include +#endif//HGL_OS != HGL_OS_Windows + +namespace hgl +{ + void SetTimeVal(timeval &tv,const double t_sec); + + namespace network + { + /** + * 设置是否使用无延迟模式(注:无延迟模式也无缓冲,有利于小包高响应应用,大包或无高响应需要求应用不要设置) + * @param no_delay 是否无延迟 + */ + bool TCPSocket::SetNodelay(bool no_delay) + { + int flag=(no_delay?1:0); + + return(setsockopt(ThisSocket,IPPROTO_TCP,TCP_NODELAY,(char *)&flag,sizeof(flag))!=-1); + } + + /** + * 设置是否使唤用KEEPALIVE自动保持连接机制 + * @param keep_alive 是否自动保持连接 + * @param idle 没有数据交互,开始探测的是时间(默认为7200秒) + * @param interval 探测之间的时间间隔(默认为75秒) + * @param count 探测的最大次数(默认为9次) + */ + void TCPSocket::SetKeepAlive(bool keep_alive,const int idle,const int interval,const int count) + { + int flag=(keep_alive?1:0); + +#if HGL_OS != HGL_OS_Windows + setsockopt(ThisSocket,SOL_SOCKET,SO_KEEPALIVE,&flag,sizeof(flag)); +#else + setsockopt(ThisSocket, SOL_SOCKET, SO_KEEPALIVE, (const char *)&flag, sizeof(flag)); +#endif//windows + + if(!flag) + return; + +#if HGL_OS != HGL_OS_Windows //windows不支持 + #if HGL_OS != HGL_OS_macOS + setsockopt(ThisSocket,IPPROTO_TCP,TCP_KEEPIDLE, &idle, sizeof(int)); + #endif//HGL_OS_macOS + setsockopt(ThisSocket,IPPROTO_TCP,TCP_KEEPINTVL, &interval, sizeof(int)); + setsockopt(ThisSocket,IPPROTO_TCP,TCP_KEEPCNT, &count, sizeof(int)); +#endif//HGL_OS_Windows + } + + void TCPSocket::ResetConnect() + { + FD_ZERO(&local_set); + FD_SET(ThisSocket,&local_set); + } + + /** + * 使用指定socket + * @param sock 指定socket编号 + * @param addr socket地址 + */ + bool TCPSocket::UseSocket(int sock,const IPAddress *addr) + { + if(sock<0) + RETURN_FALSE; + + if(!addr->IsTCP()) + RETURN_FALSE; + + if(!Socket::UseSocket(sock,addr)) + RETURN_FALSE; + + ResetConnect(); + return(true); + } + + /** + * 取得当前socket是否还在联接状态 + */ + bool TCPSocket::IsConnect() + { + return(ThisSocket!=-1); + } + + /** + * 等待数据接收 + * @param wait_time 等待的时间 + * @return >0 有数据 + * @return 0 无数据,但不是错误(如超时) + * @return <0 出错 + */ + int TCPSocket::WaitRecv(double wait_time) + { + if(ThisSocket==-1) + RETURN_ERROR(-1); + + SetTimeVal(time_out,wait_time); + + memcpy(&recv_set,&local_set,sizeof(fd_set)); + memcpy(&err_set,&local_set,sizeof(fd_set)); + + const int result=select(ThisSocket+1,&recv_set,nullptr,&err_set,&time_out); + + if(result<0) + { + int err=GetLastSocketError(); + + RETURN_ERROR(-err); + } + + if(FD_ISSET(ThisSocket,&recv_set)) + return(1); + + if(FD_ISSET(ThisSocket,&err_set)) + RETURN_ERROR(-1); + + return(0); + } + }//namespace network +}//namespace hgl diff --git a/src/UdpLiteSocket.cpp b/src/UdpLiteSocket.cpp new file mode 100644 index 0000000..894b866 --- /dev/null +++ b/src/UdpLiteSocket.cpp @@ -0,0 +1,89 @@ +#include +#include +#include + +#if HGL_OS != HGL_OS_Windows + #if (HGL_OS == HGL_OS_FreeBSD)||(HGL_OS == HGL_OS_NetBSD)||(HGL_OS == HGL_OS_OpenBSD)||(HGL_OS==HGL_OS_MacOSX) + #include + #else + #include + #endif//BSD or macOS +#endif//HGL_OS != HGL_OS_Windows + +#ifndef SOL_UDPLITE +#define SOL_UDPLITE 136 +#endif//SOL_UDPLITE + +#ifndef UDPLITE_SEND_CSCOV +#define UDPLITE_SEND_CSCOV 10 +#endif + +#ifndef UDPLITE_RECV_CSCOV +#define UDPLITE_RECV_CSCOV 11 +#endif +//-------------------------------------------------------------------------------------------------- +namespace hgl +{ + namespace network + { + /** + * 创建一个UDPLite连接,使用指定的IP地址和端口 + * @param addr 地址 + * @return true 创建服务器成功 + * @return false 创建服务器失败 + */ + bool UDPLiteSocket::Create(const IPAddress *addr) + { + if(!addr)RETURN_FALSE; + + if(addr->GetSocketType()!=SOCK_DGRAM)RETURN_FALSE; + if(addr->GetProtocol()!=IPPROTO_UDPLITE)RETURN_FALSE; + + if(!CreateSocket(addr->GetFamily(),SOCK_DGRAM,IPPROTO_UDPLITE)) + RETURN_FALSE; + + socket_protocols=IPPROTO_UDPLITE; + + if(!addr->Bind(ThisSocket)) + { + hgl::CloseSocket(ThisSocket); + return(false); + } + + SetBlock(false); + return(true); + } + + /** + * 创建一个UDPLite连接 + */ + bool UDPLiteSocket::Create(int family) + { + if(family!=AF_INET&&family!=AF_INET6)RETURN_FALSE; + + if((ThisSocket=socket(family,SOCK_DGRAM,IPPROTO_UDPLITE))<0) + { + LOG_ERROR(U16_TEXT("创建UDPLiteSocket失败!errno:")+UTF16String(GetLastSocketError())); + return(false); + } + + socket_protocols=IPPROTO_UDPLITE; + + SetBlock(false); + return(true); + } + + void UDPLiteSocket::SetChecksumCoverage(int send_val,int recv_val) + { + if(ThisSocket==-1)return; + +#if HGL_OS == HGL_OS_Windows + setsockopt(ThisSocket, SOL_UDPLITE, UDPLITE_SEND_CSCOV, (const char *)&send_val, sizeof(int)); + setsockopt(ThisSocket, SOL_UDPLITE, UDPLITE_RECV_CSCOV, (const char *)&recv_val, sizeof(int)); +#else + setsockopt(ThisSocket, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &send_val, sizeof(int)); + setsockopt(ThisSocket, SOL_UDPLITE, UDPLITE_RECV_CSCOV, &recv_val, sizeof(int)); +#endif// + } + }//namespace network +}//namespace hgl diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp new file mode 100644 index 0000000..5501a7f --- /dev/null +++ b/src/UdpSocket.cpp @@ -0,0 +1,167 @@ +#include +#include +#include + +#if HGL_OS != HGL_OS_Windows +#include +#endif//HGL_OS != HGL_OS_Windows +//-------------------------------------------------------------------------------------------------- +namespace hgl +{ + namespace network + { + /** + * 本类构造函数 + */ + UDPSocket::UDPSocket() + { + ThisSocket=-1; + bind_addr=nullptr; + tar_addr=nullptr; + } + + /** + * 本类析构函数 + */ + UDPSocket::~UDPSocket() + { + CloseSocket(); + + SAFE_CLEAR(tar_addr); + SAFE_CLEAR(bind_addr); + } + + /** + * 创建一个UDP连接,使用指定的IP地址和端口 + * @param addr 地址 + * @return true 创建服务器成功 + * @return false 创建服务器失败 + */ + bool UDPSocket::Create(const IPAddress *addr) + { + if(!addr)RETURN_FALSE; + + if(!addr->IsUDP())RETURN_FALSE; + + if(!Socket::InitSocket(addr)) + RETURN_FALSE; + + bind_addr=addr->CreateCopy(); + + if(!bind_addr->Bind(ThisSocket)) + { + hgl::CloseSocket(ThisSocket); + RETURN_FALSE; + } + + SetBlock(false); + return(true); + } + +// /** +// * 创建一个UDP连接 +// */ +// bool UDPSocket::Create(int family) +// { +// if(family!=AF_INET&&family!=AF_INET6)RETURN_FALSE; +// +// if((ThisSocket=socket(family,SOCK_DGRAM,IPPROTO_UDP))<0) +// { +// LOG_ERROR(U16_TEXT("创建Socket失败!errno:")+UTF16String(GetLastSocketError())); +// RETURN_FALSE; +// } +// +// SetBlock(false); +// return(true); +// } + + /** + * 设定发送数据时,接收数据方的地址 + * @param addr 接收方的地址 + * @return 是否成功 + */ + bool UDPSocket::SetSendAddr(const IPAddress *addr) + { + if(ThisSocket==-1)RETURN_FALSE; + + SAFE_CLEAR(tar_addr); + tar_addr=addr->CreateCopy(); + + { + const int opt = tar_addr->IsBoradcast()?1:0; + + if(setsockopt(ThisSocket, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt))==-1) + RETURN_FALSE; + } + + return(true); + } + + /** + * 向已指定的地址和端口发送数据包 + * @param data 数据指针 + * @param size 数据长度 + * @return 已发送的数据字节数 + */ + int UDPSocket::SendPacket(const void *data,int size) + { + #ifdef _DEBUG + if(ThisSocket==-1) + { + LOG_HINT(U16_TEXT("UDPSocket没有调用Create")); + return(-1); + } + #endif// + return(sendto(ThisSocket,(char *)data,size,0,tar_addr->GetSockAddr(),tar_addr->GetSockAddrInSize())); + } + + /** + * 向指定地址发送数据包 + * @param addr 接收数据方的地址 + * @param data 数据指针 + * @param size 数据长度 + * @return 已发送的数据字节数 + */ + int UDPSocket::SendPacket(IPAddress *addr,const void *data,int size) + { + #ifdef _DEBUG + if(ThisSocket==-1) + { + LOG_HINT(U16_TEXT("UDPSocket没有调用Create")); + return(-1); + } + #endif// + return(sendto(ThisSocket,(char *)data,size,0,addr->GetSockAddr(),addr->GetSockAddrInSize())); + } + + /** + * 接收由外部发来的数据包 + * @param buf 接收用的数据缓冲区指针 + * @param size 缓冲区长度 + * @param remote_addr 发送方的地址 + * @return 接收到的数据长度 + */ + int UDPSocket::RecvPacket(void *buf,int size,IPAddress *remote_addr) + { + #ifdef _DEBUG + if(ThisSocket==-1) + { + LOG_HINT(U16_TEXT("UDPSocket没有调用Create")); + return(-1); + } + #endif// + + static + + #if HGL_OS == HGL_OS_Windows + int + #else + socklen_t + #endif// + sas=remote_addr->GetSockAddrInSize(); + + return(recvfrom(ThisSocket,(char *)buf,size,0,remote_addr->GetSockAddr(),&sas)); + } + }//namespace network +}//namespace hgl + diff --git a/src/WebApi_Currency.cpp b/src/WebApi_Currency.cpp new file mode 100644 index 0000000..3e2a04e --- /dev/null +++ b/src/WebApi_Currency.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +namespace hgl +{ + namespace finance + { + float GetCurrencyExchangeRate(const char target[4],const char source[4]) + { + if(strlen(target)!=3 + ||strlen(source)!=3)return(0); + + char url[]="http://download.finance.yahoo.com/d/quotes.csv?s=******=X&f=l1&e=.csv"; + + memcpy(url+49,source,3); + memcpy(url+52,target,3); + + hgl::io::MemoryOutputStream mos; + + if(hgl::network::http::get(&mos,url)<=0) + return 0; + + float result; + + if(stof((char *)(mos.GetData()),result)) + return result; + + return 0; + } + }//namespace finance +}//namespace hgl diff --git a/src/WebSocket.cpp b/src/WebSocket.cpp new file mode 100644 index 0000000..b133836 --- /dev/null +++ b/src/WebSocket.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + using namespace hgl::algorithm; + + /** + * 获取WebSocket信息 + * @param data 输入的信息头 + * @param size 信息头长度 + * @return 是否解晰成功 + */ + bool GetWebSocketInfo(UTF8String &sec_websocket_key,UTF8String &sec_websocket_protocol,uint &sec_websocket_version,const char *data,const uint size) + { + constexpr char SEC_WEBSOCKET_KEY[]="Sec-WebSocket-Key: "; + constexpr uint SEC_WEBSOCKET_KEY_SIZE=sizeof(SEC_WEBSOCKET_KEY)-1; //sizeof的带\0所以要-1 + + constexpr char SEC_WEBSOCKET_PROTOCOL[]="Sec-WebSocket-Protocol: "; + constexpr uint SEC_WEBSOCKET_PROTOCOL_SIZE=sizeof(SEC_WEBSOCKET_PROTOCOL)-1; + + constexpr char SEC_WEBSOCKET_VERSION[]="Sec-WebSocket-Version: "; + constexpr uint SEC_WEBSOCKET_VERSION_SIZE=sizeof(SEC_WEBSOCKET_VERSION)-1; + + if(!data||size<40)return(false); + + const char *end; + + { + const char *key=hgl::strstr(data,size,SEC_WEBSOCKET_KEY,SEC_WEBSOCKET_KEY_SIZE); + + if(!key)return(false); + + key+=SEC_WEBSOCKET_KEY_SIZE; + + end=key; + while(*end!='\r')++end; + + sec_websocket_key.Set(key,end-key); + } + + { + const char *protocol=hgl::strstr(data,size,SEC_WEBSOCKET_PROTOCOL,SEC_WEBSOCKET_PROTOCOL_SIZE); + + if(protocol) //也有可能是不存在的 + { + protocol+=SEC_WEBSOCKET_PROTOCOL_SIZE; + end=protocol; + while(*end!='\r')++end; + + sec_websocket_protocol.Set(protocol,end-protocol); + } + } + + { + const char *version=hgl::strstr(data,size,SEC_WEBSOCKET_VERSION,SEC_WEBSOCKET_VERSION_SIZE); + + if(version) + { + version+=SEC_WEBSOCKET_VERSION_SIZE; + end=version; + while(*end!='\r')++end; + + hgl::stou(version,sec_websocket_version); + } + } + + return(true); + } + + /** + * 生成WebSocket回复头 + * @param result 回复头存放字符串 + */ + void MakeWebSocketAccept(UTF8String &result,const UTF8String &sec_websocket_key,const UTF8String &sec_websocket_protocol) + { + const UTF8String key_mask=sec_websocket_key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + HashCodeSHA1 hc; + + CountSHA1(key_mask.c_str(),key_mask.Length(),hc); + + io::MemoryOutputStream mos; + + base64_encode(&mos,hc.code,hc.size()); + + const UTF8String sec_websocket_accept((char *)mos.GetData(),mos.GetSize()); + + result="HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: "+sec_websocket_accept; + + if(!sec_websocket_protocol.IsEmpty()) + result+="\r\nSec-WebSocket-Protocol: "+sec_websocket_protocol; + + result+="\r\n\r\n"; + } + }//namespace network +}//namespace hgl diff --git a/src/WebSocketAccept.cpp b/src/WebSocketAccept.cpp new file mode 100644 index 0000000..80add66 --- /dev/null +++ b/src/WebSocketAccept.cpp @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include + +namespace hgl +{ + namespace network + { + //参考工程: https://github.com/katzarsky/WebSocket + + WebSocketAccept::WebSocketAccept():TCPAccept(),recv_buffer(HGL_TCP_BUFFER_SIZE) + { + } + + WebSocketAccept::WebSocketAccept(int s,IPAddress *ip):TCPAccept(s,ip),recv_buffer(HGL_TCP_BUFFER_SIZE) + { + } + + void WebSocketAccept::WebSocketHandshake() + { + constexpr char HTTP_HEADER_END_STR[4]={'\r','\n','\r','\n'}; //别用"\r\n\r\n",不然sizeof会得出来5 + constexpr int HTTP_HEADER_END_SIZE=sizeof(HTTP_HEADER_END_STR); + + MemBlock ws_header(1024); + + int pos=0; + int total=0; + int size=0; + + const char *end; + + while(true) + { + size=sis->Read(ws_header.data()+pos,1024); + + if(size==0)continue; + if(size<0) + { + LOG_ERROR(OS_TEXT("WebSocketAccept::WebSocketHandshake() read data error")); + break; + } + + recv_total+=size; + pos+=size; + + end=hgl::strrstr(ws_header.data(),pos,HTTP_HEADER_END_STR,HTTP_HEADER_END_SIZE); + + if(!end) + continue; + + end+=HTTP_HEADER_END_SIZE; + total=end-ws_header.data(); + + UTF8String key; + UTF8String ws_protocol; + uint ws_version; + + UTF8String ws_accept_protocol; + + if(GetWebSocketInfo(key,ws_protocol,ws_version,ws_header.data(),total)) + if(OnHandshake(ws_accept_protocol,ws_protocol,ws_version)) + { + UTF8String ws_return; + + MakeWebSocketAccept(ws_return,key,ws_accept_protocol); + + if(Send(ws_return.c_str(),ws_return.Length())) + { + size=pos-total; + + if(size>0) + memcpy(recv_buffer.data(),end,size); + + recv_length=size; + return; + } + } + + break; + } + + CloseSocket(); + } + + /** + * 从socket接收数据回调函数
此写法有待改良 + */ + int WebSocketAccept::OnSocketRecv(int /*size*/) + { + int total=0; + + if(!sis) + { + sis=new SocketInputStream(ThisSocket); + recv_total=0; + recv_length=0; + WebSocketHandshake(); + + total+=recv_total; + } + + while(true) + { + if(recv_length==0) + { + msg_header_size=2; //最小头2字节 + msg_full_length=0; + msg_length=0; + msg_opcode=0; + msg_fin=0; + msg_masked=0; + } + + if(recv_lengthRead(recv_buffer.data()+recv_length,msg_header_size-recv_length); + + if(result<0) + RETURN_ERROR(result); + + if(result==0) + return(total); + + recv_length+=result; + recv_total+=result; + total+=result; + } + + if(recv_lengthCloseSocket(); + return(-1); + } + + msg_fin =(p[0]>>7)&0x1; + msg_masked =(p[1]>>7)&0x1; + + uint length_field=p[1]&0x7F; + + msg_header_size=2; + + if(length_field<=125) + { + msg_length=length_field; + } + else + if(length_field==126) //16 bit msg_length + { + msg_header_size+=2; + + if(recv_lengthRead(recv_buffer.data()+recv_length,msg_full_length-(recv_length-msg_header_size)); + + if(result<0) + RETURN_ERROR(result); + + if(result==0) + return total; + + recv_length+=result; + recv_total+=result; + total+=result; + + if(recv_length=3 + &&msg_opcode<=7) + { + } + else + if(msg_opcode==2) + { + if(msg_length>0) + { + #ifdef _DEBUG + data_out_str.SetLength(msg_length*3); + + DataToLowerHexStr(data_out_str.data(),(uint8 *)pack,msg_length,','); + + LOG_INFO(U8_TEXT("WebSocket[")+UTF8String(ThisSocket)+U8_TEXT("] Recv binary [")+UTF8String(msg_length)+U8_TEXT("]: ")+UTF8String(data_out_str.data())); + #endif//_DEBUG + + OnBinary(pack,msg_length,msg_fin); + } + + if(!msg_fin) + last_opcode=2; + else + last_opcode=0; + } + else + if(msg_opcode==1){if(msg_length>0)OnText(pack,msg_length,msg_fin);if(!msg_fin)last_opcode=2;else last_opcode=0;}else + if(msg_opcode==0) + { + if(msg_length>0) + { + if(last_opcode==1) + OnText(pack,msg_length,msg_fin); + else + if(last_opcode==2) + OnBinary(pack,msg_length,msg_fin); + } + } + else + { + LOG_PROBLEM(OS_TEXT("WebSocketAccept,opcode error,opcode:")+OSString(msg_opcode)+OS_TEXT(",length:")+OSString(msg_length)); + OnError(); + } + + recv_buffer.ClearData(); + recv_length=0; + }//while + } + + int WebSocketAccept::SendFrame(uint8 opcode,void *msg,uint32 size,bool fin) + { + uint8 header[14]; + uint header_size; + + header[0]=opcode&0xF; + if(fin) + header[0]|=1<<7; + + if(size<=125) + { + header[1]=size; + header_size=2; + } + else + if(size<=65535) + { + header[1]=126; + header[2]=(size&0xFF00)>>8; + header[3]= size&0xFF; + + header_size=4; + } + else + { + header[1]=127; + header[2]=0; + header[3]=0; + header[4]=0; + header[5]=0; + header[6]=(size&0xFF000000)>>24; + header[7]=(size&0xFF0000)>>16; + header[8]=(size&0xFF00)>>8; + header[9]= size&0xFF; + + header_size=10; + } + + if(!sos) + sos=new SocketOutputStream(ThisSocket); + + if(sos->WriteFully(header,header_size)!=header_size) + return(-1); + + if(size>0) + if(sos->WriteFully(msg,size)!=size) + return(-1); + + return header_size+size; + } + + bool WebSocketAccept::SendPing() + { + return SendFrame(0x9,nullptr,0,true)>0; + } + + bool WebSocketAccept::SendPong() + { + return SendFrame(0xA,nullptr,0,true)>0; + } + + bool WebSocketAccept::SendBinary(void *data,uint32 size,bool fin) + { + #ifdef _DEBUG + data_out_str.SetLength(size*3); + + DataToLowerHexStr(data_out_str.data(),(uint8 *)data,size,','); + + LOG_INFO(U8_TEXT("WebSocket[")+UTF8String(ThisSocket)+U8_TEXT("] Send binary [")+UTF8String(size)+U8_TEXT("]: ")+UTF8String(data_out_str.data())); + #endif//_DEBUG + + return SendFrame(0x2,data,size,fin)>0; + } + + bool WebSocketAccept::SendText(void *text,uint32 size,bool fin) + { + return SendFrame(0x1,text,size,fin)>0; + } + }//namespace network +}//namespace hgl diff --git a/src/WebapiUserAgent.cpp b/src/WebapiUserAgent.cpp new file mode 100644 index 0000000..281ec7d --- /dev/null +++ b/src/WebapiUserAgent.cpp @@ -0,0 +1,68 @@ +#include + +namespace hgl +{ + namespace webapi + { + UTF8String FirefoxUserAgent(FirefoxUserAgentConfig &cfg) + { + UTF8String agent="Mozilla/5.0 "; + + if(cfg.os>=OS_WindowsX86 + &&cfg.os<=OS_LinuxX86_64) + { + if(cfg.os>=OS_WindowsX86 + &&cfg.os<=OS_WindowsWOW64) + { + agent+="(Windows NT "+UTF8String(cfg.os_ver.major)+"."+UTF8String(cfg.os_ver.minor)+"; "; + + if(cfg.os==OS_WindowsAMD64)agent+="Win64; x64; ";else + if(cfg.os==OS_WindowsWOW64)agent+="WOW64; "; + } + else + if(cfg.os==OS_macOS) + agent+="(Macintosh; Intel Mac OS X "+UTF8String(cfg.os_ver.major)+"."+UTF8String(cfg.os_ver.minor)+"; "; + else + if(cfg.os==OS_Linuxi686) + agent+="(X11; Linux i686; "; + else + if(cfg.os==OS_LinuxX86_64) + agent+="(X11; Linux x86_64; "; + + agent+="rv:"+UTF8String(cfg.ff_ver.major)+"."+UTF8String(cfg.ff_ver.minor)+") Gecko/"+UTF8String(cfg.gecko_version)+" Firefox/" + +UTF8String(cfg.ff_ver.major)+"."+UTF8String(cfg.ff_ver.minor); + } + else + if(cfg.os>=OS_iPod + &&cfg.os<=OS_iPad) + { + if(cfg.os==OS_iPod) agent+="(iPod touch; ";else + if(cfg.os==OS_iPad) agent+="(iPad; ";else + agent+="(iPhone; "; + + agent+= "CPU iPhone OS "+UTF8String(cfg.os_ver.major)+"_"+UTF8String(cfg.os_ver.minor)+" like Mac OS X) "+ + "AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4"; + } + else + if(cfg.os>=OS_AndroidPhone + &&cfg.os<=OS_AndroidTV) + { + agent+="(Android "+UTF8String(cfg.os_ver.major)+"."+UTF8String(cfg.os_ver.minor)+"; "; + + if(cfg.os==OS_AndroidPhone )agent+="Mobile; "; + if(cfg.os==OS_AndroidTablet )agent+="Tablet; "; + if(cfg.os==OS_AndroidTV )agent+="TV; "; + + agent+="rv:"+UTF8String(cfg.ff_ver.major)+"."+UTF8String(cfg.ff_ver.minor)+") Gecko/" + +UTF8String(cfg.ff_ver.major)+"."+UTF8String(cfg.ff_ver.minor)+" Firefox/" + +UTF8String(cfg.ff_ver.major)+"."+UTF8String(cfg.ff_ver.minor); + } + else + { + agent+="Firefox/"+UTF8String(cfg.ff_ver.major)+"."+UTF8String(cfg.ff_ver.minor); + } + + return agent; + } + }//namespace webapi +}//namespace hgl diff --git a/src/WinFireWall.cpp b/src/WinFireWall.cpp new file mode 100644 index 0000000..1a19512 --- /dev/null +++ b/src/WinFireWall.cpp @@ -0,0 +1,721 @@ +#include +#include +#include + +namespace hgl +{ + namespace network + { + WinFireWall::WinFireWall(void) + { + CoInitialize( NULL ); + + m_pFireWallProfile = NULL; + } + + WinFireWall::~WinFireWall(void) + { + Close(); + CoUninitialize(); + } + + /** + * 初始化Windows防火墙访问 + * @return FW_NOERROR 成功 + */ + FW_ERROR_CODE WinFireWall::Init() + { + HRESULT hr = S_FALSE; + INetFwMgr* fwMgr = NULL; + INetFwPolicy* fwPolicy = NULL; + + FW_ERROR_CODE ret = FW_NOERROR; + try + { + if( m_pFireWallProfile ) + throw FW_ERR_INITIALIZED; + + // Create an instance of the firewall settings manager. + hr = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof( INetFwMgr), (void**)&fwMgr ); + + if( FAILED( hr )) + throw FW_ERR_CREATE_SETTING_MANAGER; + + // Retrieve the local firewall policy. + hr = fwMgr->get_LocalPolicy( &fwPolicy ); + if( FAILED( hr )) + throw FW_ERR_LOCAL_POLICY; + + // Retrieve the firewall profile currently in effect + hr = fwPolicy->get_CurrentProfile( &m_pFireWallProfile ); + if( FAILED( hr )) + throw FW_ERR_PROFILE; + + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + if( fwPolicy ) + fwPolicy->Release(); + if( fwMgr ) + fwMgr->Release(); + + return ret; + } + + /** + * 关闭Windows防火墙访问 + */ + FW_ERROR_CODE WinFireWall::Close() + { + // Release the firewall profile + if( m_pFireWallProfile ) + { + m_pFireWallProfile->Release(); + m_pFireWallProfile = NULL; + } + + return FW_NOERROR; + } + + /** + * 检测防火墙是否打开 + * @param bOn 是否打开标识保存变量 + * @return FW_NOERROR 检测正常 + */ + FW_ERROR_CODE WinFireWall::CheckFirewall( bool& bOn ) + { + HRESULT hr; + VARIANT_BOOL bFWEnabled; + bOn = FALSE; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + hr = m_pFireWallProfile->get_FirewallEnabled( &bFWEnabled ); + if( FAILED(hr)) + throw FW_ERR_FIREWALL_IS_ENABLED; + + if( bFWEnabled != VARIANT_FALSE ) + bOn = TRUE; + } + catch( FW_ERROR_CODE nError ) + { + return nError; + } + + return FW_NOERROR; + } + + /** + * 打开Windows防火墙 + * @return FW_NOERROR 成功 + */ + FW_ERROR_CODE WinFireWall::OpenFirewall() + { + HRESULT hr; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + // Check whether the firewall is off + bool bFWOn; + FW_ERROR_CODE ret = CheckFirewall( bFWOn ); + + if( ret != FW_NOERROR ) + throw ret; + + // If it is off now, turn it on + if( !bFWOn ) + { + hr = m_pFireWallProfile->put_FirewallEnabled( VARIANT_TRUE ); + if( FAILED( hr )) + throw FW_ERR_FIREWALL_ENABLED; + } + } + catch( FW_ERROR_CODE nError ) + { + return nError; + } + + return FW_NOERROR; + } + + /** + * 关闭Windows防火墙 + * @return FW_NOERROR 成功 + */ + FW_ERROR_CODE WinFireWall::CloseFirewall() + { + HRESULT hr; + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + // Check whether the firewall is off + bool bFWOn; + FW_ERROR_CODE ret = CheckFirewall( bFWOn ); + + if( ret != FW_NOERROR ) + throw ret; + + // If it is on now, turn it off + if( bFWOn ) + { + hr = m_pFireWallProfile->put_FirewallEnabled( VARIANT_FALSE ); + if( FAILED( hr )) + throw FW_ERR_FIREWALL_ENABLED; + } + } + catch( FW_ERROR_CODE nError ) + { + return nError; + } + return FW_NOERROR; + } + + /** + * 检测指定程序在Windows防火墙中的设置 + * @param lpszProcessImageFileName 程序可执行文件名称(绝对路径) + * @param bEnable “是否被允许通信”信息保存用变量 + * @return FW_NOERROR 检测成功 + */ + FW_ERROR_CODE WinFireWall::CheckApplication( const wchar_t* lpszProcessImageFileName, bool& bEnable ) + { + FW_ERROR_CODE ret = FW_NOERROR; + HRESULT hr; + // BSTR bstrFWProcessImageFileName = NULL; + UTF16String bstrFWProcessImageFileName; + VARIANT_BOOL bFWEnabled; + INetFwAuthorizedApplication* pFWApp = NULL; + INetFwAuthorizedApplications* pFWApps = NULL; + + bEnable = FALSE; + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + if( lpszProcessImageFileName == NULL ) + throw FW_ERR_INVALID_ARG; + + hr = m_pFireWallProfile->get_AuthorizedApplications( &pFWApps ); + if( FAILED( hr )) + throw FW_ERR_AUTH_APPLICATIONS; + + // Allocate a BSTR for the process image file name + //bstrFWProcessImageFileName = SysAllocString( lpszProcessImageFileName ); + bstrFWProcessImageFileName = lpszProcessImageFileName; + //if( SysStringLen( bstrFWProcessImageFileName ) == 0) + if(bstrFWProcessImageFileName.Length()==0) + throw FW_ERR_SYS_ALLOC_STRING; + + hr = pFWApps->Item( bstrFWProcessImageFileName.c_str(), &pFWApp); + // If FAILED, the appliacation is not in the collection list + if( SUCCEEDED( hr )) + { + // Find out if the authorized application is enabled + hr = pFWApp->get_Enabled( &bFWEnabled ); + + if( FAILED( hr )) + throw FW_ERR_APP_ENABLED; + + if( bFWEnabled == VARIANT_TRUE ) + bEnable = TRUE; + } + } + catch( FW_ERROR_CODE nError ) + { + ret = nError; + } + + // Free the BSTR + //SysFreeString( bstrFWProcessImageFileName ); + + // Release memories to retrieve the information of the application + if( pFWApp ) + pFWApp->Release(); + if( pFWApps ) + pFWApps->Release(); + + return ret; + } + + /** + * 添加一个程序到Windows防火墙可通信名单 + * @param lpszProcessImageFileName 程序可执行文件名称(绝对路径) + * @param lpszRegisterName 在Windows防火墙名单中的程序名称 + * @return FW_NOERROR 添加成功 + */ + FW_ERROR_CODE WinFireWall::AddApplication( const wchar_t* lpszProcessImageFileName, const wchar_t* lpszRegisterName ) + { + FW_ERROR_CODE ret = FW_NOERROR; + HRESULT hr; + bool bAppEnable; + //BSTR bstrProcessImageFileName = NULL; + UTF16String bstrProcessImageFileName; + //BSTR bstrRegisterName = NULL; + UTF16String bstrRegisterName; + INetFwAuthorizedApplication* pFWApp = NULL; + INetFwAuthorizedApplications* pFWApps = NULL; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + if( lpszProcessImageFileName == NULL || lpszRegisterName == NULL ) + throw FW_ERR_INVALID_ARG; + + // First of all, check the application is already authorized; + FW_ERROR_CODE nError = this->CheckApplication( lpszProcessImageFileName, bAppEnable ); + if( nError != FW_NOERROR ) + throw nError; + + // Only add the application if it isn't authorized + if( bAppEnable == FALSE ) + { + // Retrieve the authorized application collection + hr = m_pFireWallProfile->get_AuthorizedApplications( &pFWApps ); + if( FAILED( hr )) + throw FW_ERR_AUTH_APPLICATIONS; + + // Create an instance of an authorized application + hr = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&pFWApp); + if( FAILED( hr )) + throw FW_ERR_CREATE_APP_INSTANCE; + + // Allocate a BSTR for the Process Image FileName + //bstrProcessImageFileName = SysAllocString( lpszProcessImageFileName ); + bstrProcessImageFileName = lpszProcessImageFileName; + //if( SysStringLen( bstrProcessImageFileName ) == 0) + if (bstrProcessImageFileName.Length() == 0) + throw FW_ERR_SYS_ALLOC_STRING; + + // Set the process image file name + hr = pFWApp->put_ProcessImageFileName( bstrProcessImageFileName.c_str() ); + if( FAILED( hr ) ) + throw FW_ERR_PUT_PROCESS_IMAGE_NAME; + + // Allocate a BSTR for register name + //bstrRegisterName = SysAllocString( lpszRegisterName ); + bstrRegisterName = lpszRegisterName; + //if( SysStringLen( bstrRegisterName ) == 0) + if (bstrRegisterName.Length() == 0) + throw FW_ERR_SYS_ALLOC_STRING; + // Set a registered name of the process + hr = pFWApp->put_Name( bstrRegisterName.c_str() ); + if( FAILED( hr )) + throw FW_ERR_PUT_REGISTER_NAME; + + // Add the application to the collection + hr = pFWApps->Add( pFWApp ); + if( FAILED( hr )) + throw FW_ERR_ADD_TO_COLLECTION; + } + } + catch( FW_ERROR_CODE nError ) + { + ret = nError; + } + + //SysFreeString( bstrProcessImageFileName ); + //SysFreeString( bstrRegisterName ); + + if( pFWApp ) + pFWApp->Release(); + if( pFWApps ) + pFWApps->Release(); + + return ret; + } + + /** + * 将一个程序从Windows防火墙可通信名单中移除 + * @param lpszProcessImageFileName 程序可执行文件名称(绝对路径) + * @return FW_NOERROR 移除成功 + */ + FW_ERROR_CODE WinFireWall::RemoveApplication( const wchar_t* lpszProcessImageFileName ) + { + FW_ERROR_CODE ret = FW_NOERROR; + HRESULT hr; + bool bAppEnable; + //BSTR bstrProcessImageFileName = NULL; + UTF16String bstrProcessImageFileName; + INetFwAuthorizedApplications* pFWApps = NULL; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + if( lpszProcessImageFileName == NULL ) + throw FW_ERR_INVALID_ARG; + + FW_ERROR_CODE nError = this->CheckApplication( lpszProcessImageFileName, bAppEnable ); + if( nError != FW_NOERROR ) + throw nError; + + // Only remove the application if it is authorized + if( bAppEnable == TRUE ) + { + // Retrieve the authorized application collection + hr = m_pFireWallProfile->get_AuthorizedApplications( &pFWApps ); + if( FAILED( hr )) + throw FW_ERR_AUTH_APPLICATIONS; + + // Allocate a BSTR for the Process Image FileName + //bstrProcessImageFileName = SysAllocString( lpszProcessImageFileName ); + bstrProcessImageFileName = lpszProcessImageFileName; + //if( SysStringLen( bstrProcessImageFileName ) == 0) + if (bstrProcessImageFileName.Length() == 0) + throw FW_ERR_SYS_ALLOC_STRING; + hr = pFWApps->Remove( bstrProcessImageFileName.c_str() ); + if( FAILED( hr )) + throw FW_ERR_REMOVE_FROM_COLLECTION; + } + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + //SysFreeString( bstrProcessImageFileName); + if( pFWApps ) + pFWApps->Release(); + + return ret; + } + + /** + * 检测指定端口状态 + * @param lPortNumber 端口号 + * @param ipProtocol 协议(ANY:任意、TCP、UDP 三种可选) + * @param bEnable “是否被允许通信”信息保存变量 + * @return FW_NOERROR 检测成功 + */ + FW_ERROR_CODE WinFireWall::CheckPort( unsigned int lPortNumber, PROTOCOL ipProtocol, bool& bEnable ) + { + FW_ERROR_CODE ret = FW_NOERROR; + VARIANT_BOOL bFWEnabled; + INetFwOpenPort* pFWOpenPort = NULL; + INetFwOpenPorts* pFWOpenPorts = NULL; + HRESULT hr; + + bEnable = FALSE; + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + // Retrieve the open ports collection + hr = m_pFireWallProfile->get_GloballyOpenPorts( &pFWOpenPorts ); + if( FAILED( hr )) + throw FW_ERR_GLOBAL_OPEN_PORTS; + + // Get the open port + hr = pFWOpenPorts->Item( lPortNumber, (NET_FW_IP_PROTOCOL)ipProtocol, &pFWOpenPort ); + if( SUCCEEDED( hr )) + { + hr = pFWOpenPort->get_Enabled( &bFWEnabled ); + if( FAILED( hr )) + throw FW_ERR_PORT_IS_ENABLED; + + if( bFWEnabled == VARIANT_TRUE ) + bEnable = TRUE; + } + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + if( pFWOpenPort ) + pFWOpenPort->Release(); + if( pFWOpenPorts ) + pFWOpenPorts->Release(); + + return ret; + } + + /** + * 将指定端口添加到Windows防火墙允许通信名单中 + * @param lPortNumber 端口号 + * @param ipProtocol 协议(ANY:任意、TCP、UDP 三种可选) + * @param lpszRegisterName 是Windows防火墙名单中所显示的名称 + * @return FW_NOERROR 添加成功 + */ + FW_ERROR_CODE WinFireWall::OpenPort( unsigned int lPortNumber, PROTOCOL ipProtocol, const wchar_t* lpszRegisterName ) + { + FW_ERROR_CODE ret = FW_NOERROR; + INetFwOpenPort* pFWOpenPort = NULL; + INetFwOpenPorts* pFWOpenPorts = NULL; + //BSTR bstrRegisterName = NULL; + UTF16String bstrRegisterName; + HRESULT hr; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + bool bEnablePort; + FW_ERROR_CODE nError = CheckPort( lPortNumber, ipProtocol, bEnablePort); + if( nError != FW_NOERROR) + throw nError; + + // Only add the port, if it isn't added to the collection + if( bEnablePort == FALSE ) + { + // Retrieve the collection of globally open ports + hr = m_pFireWallProfile->get_GloballyOpenPorts( &pFWOpenPorts ); + if( FAILED( hr )) + throw FW_ERR_GLOBAL_OPEN_PORTS; + + // Create an instance of an open port + hr = CoCreateInstance( __uuidof(NetFwOpenPort), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwOpenPort), (void**)&pFWOpenPort); + if( FAILED( hr )) + throw FW_ERR_CREATE_PORT_INSTANCE; + + // Set the port number + hr = pFWOpenPort->put_Port( lPortNumber ); + if( FAILED( hr )) + throw FW_ERR_SET_PORT_NUMBER; + + // Set the IP Protocol + hr = pFWOpenPort->put_Protocol( (NET_FW_IP_PROTOCOL)ipProtocol ); + if( FAILED( hr )) + throw FW_ERR_SET_IP_PROTOCOL; + + //bstrRegisterName = SysAllocString( lpszRegisterName ); + bstrRegisterName = lpszRegisterName; + //if( SysStringLen( bstrRegisterName ) == 0) + if (bstrRegisterName.Length() == 0) + throw FW_ERR_SYS_ALLOC_STRING; + + // Set the registered name + hr = pFWOpenPort->put_Name( bstrRegisterName.c_str() ); + if( FAILED( hr )) + throw FW_ERR_PUT_REGISTER_NAME; + + hr = pFWOpenPorts->Add( pFWOpenPort ); + if( FAILED( hr )) + throw FW_ERR_ADD_TO_COLLECTION; + } + + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + //SysFreeString( bstrRegisterName ); + if( pFWOpenPort ) + pFWOpenPort->Release(); + if( pFWOpenPorts ) + pFWOpenPorts->Release(); + + return ret; + } + + /** + * 将指定端口从Windows防火墙允许通信名单中移除 + * @param lPortNumber 端口号 + * @param ipProtocol 协议(ANY:任意、TCP、UDP 三种可选) + * @return FW_NOERROR 移除成功 + */ + FW_ERROR_CODE WinFireWall::ClosePort( unsigned int lPortNumber, PROTOCOL ipProtocol ) + { + FW_ERROR_CODE ret = FW_NOERROR; + INetFwOpenPorts* pFWOpenPorts = NULL; + HRESULT hr; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + bool bEnablePort; + FW_ERROR_CODE nError = CheckPort( lPortNumber, ipProtocol, bEnablePort); + if( nError != FW_NOERROR) + throw nError; + + // Only remove the port, if it is on the collection + if( bEnablePort == TRUE ) + { + // Retrieve the collection of globally open ports + hr = m_pFireWallProfile->get_GloballyOpenPorts( &pFWOpenPorts ); + if( FAILED( hr )) + throw FW_ERR_GLOBAL_OPEN_PORTS; + + hr = pFWOpenPorts->Remove( lPortNumber, (NET_FW_IP_PROTOCOL)ipProtocol ); + if (FAILED( hr )) + throw FW_ERR_REMOVE_FROM_COLLECTION; + } + + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + if( pFWOpenPorts ) + pFWOpenPorts->Release(); + + return ret; + } + + FW_ERROR_CODE WinFireWall::IsExceptionNotAllowed( bool& bNotAllowed ) + { + FW_ERROR_CODE ret = FW_NOERROR; + + bNotAllowed = TRUE; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + VARIANT_BOOL bExNotAllowed; + + HRESULT hr = m_pFireWallProfile->get_ExceptionsNotAllowed( &bExNotAllowed ); + + if( FAILED( hr )) + throw FW_ERR_EXCEPTION_NOT_ALLOWED; + + if( bExNotAllowed == VARIANT_TRUE ) + bNotAllowed = TRUE; + else + bNotAllowed = FALSE; + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + return ret; + } + + FW_ERROR_CODE WinFireWall::SetExceptionNotAllowed( bool bNotAllowed ) + { + FW_ERROR_CODE ret = FW_NOERROR; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + HRESULT hr = m_pFireWallProfile->put_ExceptionsNotAllowed( bNotAllowed ? VARIANT_TRUE : VARIANT_FALSE ); + + if( FAILED( hr )) + throw FW_ERR_EXCEPTION_NOT_ALLOWED; + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + return ret; + } + + FW_ERROR_CODE WinFireWall::IsNotificationDiabled( bool& bDisabled ) + { + FW_ERROR_CODE ret = FW_NOERROR; + + bDisabled = FALSE; + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + VARIANT_BOOL bNotifyDisable; + HRESULT hr = m_pFireWallProfile->get_NotificationsDisabled( &bNotifyDisable ); + if( FAILED( hr )) + throw FW_ERR_NOTIFICATION_DISABLED; + + if( bNotifyDisable == VARIANT_TRUE ) + bDisabled = TRUE; + else + bDisabled = FALSE; + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + return ret; + } + + FW_ERROR_CODE WinFireWall::SetNotificationDiabled( bool bDisabled ) + { + FW_ERROR_CODE ret = FW_NOERROR; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + HRESULT hr = m_pFireWallProfile->put_NotificationsDisabled( bDisabled ? VARIANT_TRUE : VARIANT_FALSE ); + if( FAILED( hr )) + throw FW_ERR_NOTIFICATION_DISABLED; + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + return ret; + } + + FW_ERROR_CODE WinFireWall::IsUnicastResponsesToMulticastBroadcastDisabled( bool& bDisabled ) + { + FW_ERROR_CODE ret = FW_NOERROR; + + bDisabled = FALSE; + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + VARIANT_BOOL bUniMultiDisabled; + HRESULT hr = m_pFireWallProfile->get_UnicastResponsesToMulticastBroadcastDisabled( &bUniMultiDisabled ); + if( FAILED( hr )) + throw FW_ERR_UNICAST_MULTICAST; + + if( bUniMultiDisabled == VARIANT_TRUE ) + bDisabled = TRUE; + else + bDisabled = FALSE; + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + return ret; + } + + FW_ERROR_CODE WinFireWall::SetUnicastResponsesToMulticastBroadcastDisabled( bool bDisabled ) + { + FW_ERROR_CODE ret = FW_NOERROR; + + try + { + if( m_pFireWallProfile == NULL ) + throw FW_ERR_INITIALIZED; + + HRESULT hr = m_pFireWallProfile->put_UnicastResponsesToMulticastBroadcastDisabled( bDisabled ? VARIANT_TRUE : VARIANT_FALSE ); + if( FAILED( hr )) + throw FW_ERR_UNICAST_MULTICAST; + } + catch( FW_ERROR_CODE nError) + { + ret = nError; + } + + return ret; + } + }//namespace network +}//namespace hgl