// Common, group Communication
// Copyright Alexander Liss
#ifndef __SOCKET_H__
#define __SOCKET_H__
// IP only, with window sockets 2, if available;
// when _WIN32_WINNT >= 0x0400,
// window sockets 2 is selected automatically;
// use winsock32.dll for sockets 1 and
// Ws_32.dll for sockets 2;
#include "win0.h"
#include "strbuf.h"
#include "event.h"
// DATA CONVERSION
// network byte order is big-endian, Intel's byte order is little-endian
inline unsigned long host2net(unsigned long host_value){return ::htonl(host_value);}
inline unsigned short host2net(unsigned short host_value){return ::htons(host_value);}
inline unsigned long net2host(unsigned long net_value){return ::ntohl(net_value);}
inline unsigned short net2host(unsigned short net_value){return ::ntohs(net_value);}
// LIBRARY INITIALIZATION AND CLEANUP
struct FunctionSocketLib
{
int status;
FunctionSocketLib(WORD wVersionRequested)
{WSADATA data; status=::WSAStartup(wVersionRequested,&data);}
~FunctionSocketLib(){if(!status) ::WSACleanup(); }
};
// INTERNET PROTOCOL ADDRESS
// BYTES IN NETWORK ORDER
struct IPAddress
{
unsigned long address;
IPAddress(unsigned long z=INADDR_NONE):address(host2net(z)){}
int set_address(unsigned long z)
{address=host2net(z); return 0;}
int get_dotted(StrBuffer& z)const
{in_addr a; a.S_un.S_addr=address; z.set(::inet_ntoa(a)); return address==INADDR_NONE;}
int put_dotted(const StrBuffer& z)
{address=::inet_addr(z.data);return address==INADDR_NONE;}
};
// address and port
// bytes in network order
struct IPSocketAddress: public IPAddress
{
unsigned short port;
IPSocketAddress(unsigned short p=0,unsigned long address=INADDR_NONE):
IPAddress(address),port(host2net(p)){}
int set_port(unsigned short z){port=host2net(z); return 0;}
int get(sockaddr&)const;
int put(const sockaddr&);
};
// Socket
class Socket
{
public:
// takes control of previously created handle
// should be used by a listener only
Socket(SOCKET h):handle(h){}
// creates a socket
Socket(int type=SOCK_STREAM
#if( _WIN32_WINNT >= 0x0400 )
,DWORD flags=WSA_FLAG_OVERLAPPED,
LPWSAPROTOCOL_INFO pi=0
#endif
);
~Socket(){::closesocket(handle);}
int get_status()const{return handle==INVALID_SOCKET;}
SOCKET get_handle()const{return handle;}
HANDLE get_file_handle()const{return (HANDLE)handle;}
int bind(const IPSocketAddress& own);
// see WSAConnect in the case of socket2
int connect(const IPSocketAddress& peer
#if (_WIN32_WINNT >= 0x0400)
,LPWSABUF lpCallerData=0,
LPWSABUF lpCalleeData=0,
LPQOS lpSQOS=0
#endif
);
#if (_WIN32_WINNT >= 0x0400)
int connect_multipoint(const IPSocketAddress& peer_or_root
,LPWSABUF lpCallerData,
LPWSABUF lpCalleeData,
LPQOS lpSQOS,
DWORD dwFlags
);
#endif
int reconnect(
#if (_WIN32_WINNT >= 0x0400)
LPWSABUF lpCallerData=0,
LPWSABUF lpCalleeData=0,
LPQOS lpSQOS=0
#endif
);
int get_address(IPSocketAddress& );
int get_peer_address(IPSocketAddress& );
int send(DWORD& sent, const VOID *buf,DWORD len,DWORD flags=0);
int receive(DWORD& received, VOID *buf,DWORD limit,DWORD flags=0);
int send_to(DWORD& sent,const VOID *buf,DWORD len,const IPSocketAddress& remote,DWORD flags=0);
int receive_from(DWORD& received,VOID *buf,DWORD limit, IPSocketAddress& remote,DWORD flags=0);
int send_loop(DWORD& sent,const VOID *buf,DWORD len,DWORD flags=0);
int receive_loop(DWORD& received, VOID *buf,DWORD limit,DWORD flags=0);
int send_to_loop(DWORD& sent,const VOID *buf,DWORD len,const IPSocketAddress& remote,DWORD flags=0);
int receive_from_loop(DWORD& received,VOID *buf,DWORD limit, IPSocketAddress& remote,DWORD flags=0);
#if (_WIN32_WINNT >= 0x0400)
int send(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
DWORD dwFlags=0)
{return ::WSASend(handle,lpBuffers,dwBufferCount,&NumberOfBytesSent,dwFlags,0,0);}
int receive(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpdwFlags=0)
{return ::WSARecv(handle,lpBuffers,dwBufferCount,&NumberOfBytesReceived,lpdwFlags,0,0);}
int send_to(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
const IPSocketAddress& peer,
DWORD dwFlags=0);
int receive_from(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
IPSocketAddress& peer,
LPDWORD lpdwFlags=0);
int send_loop(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
DWORD dwFlags=0);
int receive_loop(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpdwFlags=0);
int send_to_loop(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
const IPSocketAddress& peer,
DWORD dwFlags=0);
int receive_from_loop(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
IPSocketAddress& peer,
LPDWORD lpdwFlags=0);
int receive_ex(char *buf,int limit,int *flags)
{return ::WSARecvEx(handle,buf,limit,flags);}
int shutdown(int how=SD_BOTH){return ::shutdown(handle,how);}
int io_contrl(
DWORD dwIoControlCode,
LPVOID lpvInBuffer,
DWORD cbInBuffer,
LPVOID lpvOutBuffer,
DWORD cbOutBuffer,
LPDWORD lpcbBytesReturned
)
{return ::WSAIoctl(handle,dwIoControlCode,lpvInBuffer,cbInBuffer,lpvOutBuffer,cbOutBuffer,lpcbBytesReturned,0,0);}
// i/o control
int receive_ready(unsigned long& bytes)
{DWORD cbBytesReturned; return io_contrl(FIONREAD,0,0,&bytes,sizeof(unsigned long),&cbBytesReturned);}
#endif
// old version
int io_control(long command,unsigned long *arguments)
{return ::ioctlsocket(handle,command,arguments);}
int set_option(int level,int name,const char *value,int len)
{return ::setsockopt(handle,level,name,value,len);}
int get_option(int level,int name,char *value,int *len)
{return ::getsockopt(handle,level,name,value,len);}
// socket options
int set_heartbeat();
int linger(int seconds);
int get_send_buffer_size(int & size);
int set_send_buffer_size(int size);
int get_receive_buffer_size(int & size);
int set_receive_buffer_size(int size);
void abrupt_shutdown();
// uses select
bool receive_data_ready();
bool send_data_ready();
private:
SOCKET handle;
};
// SocketPtr - A BASE CLASS
class SocketPtr
{
public:
SocketPtr():socket(0){}
SocketPtr(const SocketPtr& z):socket(z.socket){}
SocketPtr(Socket& z):socket(&z){}
SocketPtr& operator==(const SocketPtr& z)
{if(this!=&z) socket=z.socket; return *this;}
Socket& get_socket(){return *socket;}
int set_socket(Socket& z){socket=&z;return 0;}
bool is_set(){return socket!=0;}
protected:
Socket* socket;
};
// Listener
class Listener: public SocketPtr
{
Listener(const Listener&);
Listener& operator==(const Listener&);
public:
Listener(){}
Listener(Socket& s):SocketPtr(s){}
int listen(int backlog=SOMAXCONN)
{return ::listen(socket->get_handle(),backlog);}
Socket * new_connection(IPSocketAddress& peer
#if (_WIN32_WINNT >= 0x0400)
,LPCONDITIONPROC lpfnCondition=0,
DWORD dwCallbackData=0
#endif
);
};
// asynchronous socket operations through file
class SocketFile
{
SocketFile(const SocketFile&);
SocketFile& operator=(const SocketFile&);
public:
SocketFile(){clear();}
SocketFile(Socket& s){clear();set_socket(s);}
virtual ~SocketFile(){}
int set_socket(Socket& z){handle=z.get_file_handle();return 0;}
BOOL get_result(DWORD& dwTransfer,BOOL bWait=TRUE);
// cancels operations created by calling thread
BOOL cancel();
protected:
HANDLE handle;
OVERLAPPED overlapped;
void clear();
};
class SocketFileOperation:public SocketFile
{
SocketFileOperation(const SocketFileOperation&);
SocketFileOperation& operator=(const SocketFileOperation&);
public:
SocketFileOperation(){}
SocketFileOperation(Socket& s):SocketFile(s){}
SocketFileOperation(Socket& s, Event& e):SocketFile(s){set_event(e);}
~SocketFileOperation(){}
int set_event(Event& z){overlapped.hEvent=z.get_handle();return 0;}
// RETURNS 0 IF DONE AND -1 IF PENDING, RETURNS >0 IF ERROR
int send(DWORD& actual,const VOID *buffer,DWORD bytes);
int receive(DWORD& actual, VOID *buffer, DWORD bytes_limit);
// blocking, returns -1 if timeout and >0 if error
int send_loop(DWORD& actual,const VOID *buffer,DWORD bytes,DWORD timeout, DWORD wait_slice);
int receive_loop(DWORD& actual,const VOID *buffer,DWORD bytes_limit,DWORD timeout, DWORD wait_slice);
};
class SocketFileOperationEx: public SocketFile
{
SocketFileOperationEx(const SocketFileOperationEx&);
SocketFileOperationEx& operator=(const SocketFileOperationEx&);
public:
SocketFileOperationEx(){}
SocketFileOperationEx(Socket& s):SocketFile(s){}
SocketFileOperationEx(Socket& s, LPOVERLAPPED_COMPLETION_ROUTINE z):SocketFile(s){}
~SocketFileOperationEx(){}
int set_routine(LPOVERLAPPED_COMPLETION_ROUTINE z){completion_routine=z;return 0;}
// one or the other
int set_data(VOID *z){overlapped.hEvent=(HANDLE)z;return 0;}
int set_event(Event& z){overlapped.hEvent=z.get_handle();return 0;}
// returns 0 if done and -1 if pending, returns >0 if error
int send(const VOID *buffer,DWORD bytes);
int receive( VOID *buffer, DWORD bytes_limit);
private:
VOID (CALLBACK* completion_routine) (DWORD dwErrorCode,DWORD dwNumberOfBytesTransfered,LPOVERLAPPED lpOverlapped);
};
#if (_WIN32_WINNT >= 0x0400)
// SocketEvent
class SocketEvent
{
SocketEvent(const SocketEvent&);
SocketEvent& operator=(const SocketEvent&);
public:
SocketEvent(){handle=WSACreateEvent();}
~SocketEvent(){if(!status())WSACloseEvent(handle);}
WSAEVENT get_handle()const{return handle;}
int status()const{return handle==WSA_INVALID_EVENT;}
BOOL set(){return WSASetEvent(handle);}
BOOL reset(){return WSAResetEvent(handle);}
private:
WSAEVENT handle;
};
// network events monitor
class NetworkEvents: public SocketPtr
{
NetworkEvents(const NetworkEvents& z);
NetworkEvents& operator=(const NetworkEvents& z);
public:
SocketEvent event;
NetworkEvents(){}
NetworkEvents(Socket& z):SocketPtr(z){}
int start_record(long events_mask)
{return WSAEventSelect(socket->get_handle(),event.get_handle(),events_mask);}
int report_record(LPWSANETWORKEVENTS lpNetworkEvents)
{return WSAEnumNetworkEvents(socket->get_handle(),event.get_handle(),lpNetworkEvents);}
};
// asynchronous socket operations
// if completion routine is set, the event is ignored
class SocketOperation: public SocketPtr
{
SocketOperation(const SocketOperation&);
SocketOperation& operator=(const SocketOperation&);
public:
SocketOperation(){clear();}
SocketOperation(Socket& s):SocketPtr(s){clear();}
SocketOperation(Socket& s, SocketEvent& e):SocketPtr(s){clear();set_event(e);}
~SocketOperation(){}
int set_routine(LPWSAOVERLAPPED_COMPLETION_ROUTINE z){completion_routine=z;return 0;}
// one or the other
int set_event(SocketEvent& z){overlapped.hEvent=z.get_handle();return 0;}
int set_data(LPVOID z){overlapped.hEvent=(WSAEVENT)z;return 0;}
WSAEVENT get_handle()const{return overlapped.hEvent;}
int io_contrl(
DWORD dwIoControlCode,
LPVOID lpvInBuffer,
DWORD cbInBuffer,
LPVOID lpvOutBuffer,
DWORD cbOutBuffer
)
{DWORD cbBytesReturned;return ::WSAIoctl(socket->get_handle(),dwIoControlCode,lpvInBuffer,cbInBuffer,lpvOutBuffer,cbOutBuffer,&cbBytesReturned,&overlapped,completion_routine);}
int receive_ready(unsigned long& bytes)
{return io_contrl(FIONREAD,0,0,&bytes,sizeof(unsigned long));}
int send(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
DWORD dwFlags=0)
{return ::WSASend(socket->get_handle(),lpBuffers,dwBufferCount,&NumberOfBytesSent,dwFlags,&overlapped,completion_routine);}
int receive(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpdwFlags=0)
{return ::WSARecv(socket->get_handle(),lpBuffers,dwBufferCount,&NumberOfBytesReceived,lpdwFlags,&overlapped,completion_routine);}
int send_to(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
const IPSocketAddress& peer,
DWORD dwFlags=0);
int receive_from(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
IPSocketAddress& peer,
LPDWORD lpdwFlags=0);
int send_loop(
DWORD& NumberOfBytesSent,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
DWORD timeout_milliseconds,
DWORD wait_slice_milliseconds,
DWORD dwFlags=0);
int receive_loop(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
DWORD timeout_milliseconds,
DWORD wait_slice_milliseconds,
LPDWORD lpdwFlags=0);
int send_to_loop(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
const IPSocketAddress& peer,
DWORD timeout_milliseconds,
DWORD wait_slice_milliseconds,
DWORD dwFlags=0);
int receive_from_loop(
DWORD& NumberOfBytesReceived,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
IPSocketAddress& peer,
DWORD timeout_milliseconds,
DWORD wait_slice_milliseconds,
LPDWORD lpdwFlags=0);
BOOL get_result(
DWORD& dwTransfer,
BOOL nWait=TRUE,
LPDWORD lpdwFlags=0
)
{return WSAGetOverlappedResult(socket->get_handle(),&overlapped,&dwTransfer,nWait,lpdwFlags);}
protected:
WSAOVERLAPPED overlapped;
void (CALLBACK *completion_routine)(DWORD dwError, DWORD cbTransferred,LPWSAOVERLAPPED lpOverlapped,DWORD dwFlags);
void clear();
};
// SocketWaitableCollection
class SocketWaitableCollection
{
friend DWORD wait(const SocketWaitableCollection&,BOOL,DWORD,BOOL);
public:
SocketWaitableCollection():size(0){}
SocketWaitableCollection(const SocketWaitableCollection & z){copy(z);}
SocketWaitableCollection & operator= (const SocketWaitableCollection & z)
{if(&z!=this) copy(z); return *this;}
LONG get_number()const{return size;}
int add(const SocketEvent&);
int add(const SocketOperation&);
int add(const SocketWaitableCollection & );
private:
WSAEVENT handles[WSA_MAXIMUM_WAIT_EVENTS];
LONG size;
void copy(const SocketWaitableCollection & );
};
int monitor(SocketEvent& signal,const Socket& socket,long event_flags);
// wait functions
DWORD wait(const SocketEvent&,
DWORD milliseconds=WSA_INFINITE,
BOOL alertable=FALSE);
DWORD wait(const SocketWaitableCollection&,
BOOL bWaitAll=FALSE,
DWORD milliseconds=WSA_INFINITE,
BOOL alertable=FALSE);
#endif // _WIN32_WINNT >= 0x0400
// returns 2=error, 1=timeout, 0=data ready
// uses select
DWORD wait_receive_ready(Socket& socket,DWORD milliseconds);
// FUNCTIONS
inline int socket_last_error(){return ::WSAGetLastError();}
// ip address # n
int IP_host(StrBuffer& name,IPAddress& address, int n=0);
#endif