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