Files
BlitzNext/bbruntime/bbsockets.cpp
T
blitz-research 08a613ed0e Initial commit.
2014-01-31 08:23:00 +13:00

506 lines
11 KiB
C++

#include "std.h"
#include "bbsockets.h"
static bool socks_ok;
static WSADATA wsadata;
static int recv_timeout;
static int read_timeout;
static int accept_timeout;
static void close( SOCKET sock,int e ){
if( e<0 ){
int opt=1;
setsockopt( sock,SOL_SOCKET,SO_DONTLINGER,(char*)&opt,sizeof(opt) );
}
closesocket( sock );
}
class UDPStream;
class TCPStream;
class TCPServer;
static set<UDPStream*> udp_set;
static set<TCPStream*> tcp_set;
static set<TCPServer*> server_set;
class UDPStream : public bbStream{
public:
UDPStream( SOCKET s );
~UDPStream();
int read( char *buff,int size );
int write( const char *buff,int size );
int avail();
int eof();
int recv();
int send( int ip,int port );
int getIP();
int getPort();
int getMsgIP();
int getMsgPort();
private:
SOCKET sock;
vector<char> in_buf,out_buf;
sockaddr_in addr,in_addr,out_addr;
int in_get,e;
};
UDPStream::UDPStream( SOCKET s ):sock(s),in_get(0),e(0){
int len=sizeof(addr);
getsockname( s,(sockaddr*)&addr,&len );
in_addr=out_addr=addr;
}
UDPStream::~UDPStream(){
close( sock,e );
}
int UDPStream::read( char *buff,int size ){
if( e ) return 0;
int n=in_buf.size()-in_get;
if( n<size ) size=n;
memcpy( buff,&in_buf[in_get],size );
in_get+=size;
return size;
}
int UDPStream::write( const char *buff,int size ){
if( e ) return 0;
out_buf.insert( out_buf.end(),buff,buff+size );
return size;
}
int UDPStream::avail(){
if( e ) return 0;
return in_buf.size()-in_get;
}
int UDPStream::eof(){
return e ? e : in_get==in_buf.size();
}
//fill buffer, return sender
int UDPStream::recv(){
if( e ) return 0;
int tout;
if( recv_timeout ) tout=gx_runtime->getMilliSecs()+recv_timeout;
for(;;){
int dt=0;
if( recv_timeout ){
dt=tout-gx_runtime->getMilliSecs();
if( dt<0 ) dt=0;
}
fd_set fd={ 1,sock };
timeval tv={ dt/1000,(dt%1000)*1000 };
int n=::select( 0,&fd,0,0,&tv );
if( !n ) return 0;
if( n!=1 ){ e=-1;return 0; }
unsigned long sz=-1;
if( ioctlsocket( sock,FIONREAD,&sz ) ){ e=-1;return 0; }
in_buf.resize( sz );in_get=0;
int len=sizeof(in_addr);
n=::recvfrom( sock,in_buf.begin(),sz,0,(sockaddr*)&in_addr,&len );
if( n==SOCKET_ERROR ) continue; //{ e=-1;return 0; }
in_buf.resize( n );
return getMsgIP();
}
return 0;
}
//send, empty buffer
int UDPStream::send( int ip,int port ){
if( e ) return 0;
int sz=out_buf.size();
out_addr.sin_addr.S_un.S_addr=htonl( ip );
out_addr.sin_port=htons( port ? port : addr.sin_port );
int n=::sendto( sock,out_buf.begin(),sz,0,(sockaddr*)&out_addr,sizeof(out_addr) );
if( n!=sz ) return e=-1;
out_buf.clear();
return sz;
}
int UDPStream::getIP(){
return ntohl( addr.sin_addr.S_un.S_addr );
}
int UDPStream::getPort(){
return ntohs( addr.sin_port );
}
int UDPStream::getMsgIP(){
return ntohl( in_addr.sin_addr.S_un.S_addr );
}
int UDPStream::getMsgPort(){
return ntohs( in_addr.sin_port );
}
class TCPStream : public bbStream{
public:
TCPStream( SOCKET s,TCPServer *t );
~TCPStream();
int read( char *buff,int size );
int write( const char *buff,int size );
int avail();
int eof();
int getIP();
int getPort();
private:
SOCKET sock;
TCPServer *server;
int e,ip,port;
};
class TCPServer{
public:
TCPServer( SOCKET S );
~TCPServer();
TCPStream *accept();
void remove( TCPStream *s );
private:
int e;
SOCKET sock;
set<TCPStream*> accepted_set;
};
TCPStream::TCPStream( SOCKET s,TCPServer *t ):sock(s),server(t),e(0){
sockaddr_in addr;
int len=sizeof(addr);
if( getpeername( s,(sockaddr*)&addr,&len ) ){
ip=port=0;
return;
}
ip=ntohl(addr.sin_addr.S_un.S_addr);
port=ntohs(addr.sin_port);
}
TCPStream::~TCPStream(){
if( server ) server->remove( this );
close( sock,e );
}
int TCPStream::read( char *buff,int size ){
if( e ) return 0;
char *b=buff,*l=buff+size;
int tout;
if( read_timeout ) tout=gx_runtime->getMilliSecs()+read_timeout;
while( b<l ){
int dt=0;
if( read_timeout ){
dt=tout-gx_runtime->getMilliSecs();
if( dt<0 ) dt=0;
}
fd_set fd={ 1,sock };
timeval tv={ dt/1000,(dt%1000)*1000 };
int n=::select( 0,&fd,0,0,&tv );
if( n!=1 ){ e=-1;break; }
n=::recv( sock,b,l-b,0 );
if( n==0 ){ e=1;break; }
if( n==SOCKET_ERROR ){ e=-1;break; }
b+=n;
}
return b-buff;
}
int TCPStream::write( const char *buff,int size ){
if( e ) return 0;
int n=::send( sock,buff,size,0 );
if( n==SOCKET_ERROR ){ e=-1;return 0; }
return n;
}
int TCPStream::avail(){
unsigned long t;
int n=::ioctlsocket( sock,FIONREAD,&t );
if( n==SOCKET_ERROR ){ e=-1;return 0; }
return t;
}
int TCPStream::eof(){
if( e ) return e;
fd_set fd={ 1,sock };
timeval tv={ 0,0 };
switch( ::select( 0,&fd,0,0,&tv ) ){
case 0:break;
case 1:if( !avail() ) e=1;break;
default:e=-1;
}
return e;
}
int TCPStream::getIP(){
return ip;
}
int TCPStream::getPort(){
return port;
}
TCPServer::TCPServer( SOCKET s ):sock(s),e(0){
}
TCPServer::~TCPServer(){
while( accepted_set.size() ) delete *accepted_set.begin();
close( sock,e );
}
TCPStream *TCPServer::accept(){
if( e ) return 0;
fd_set fd={ 1,sock };
timeval tv={ accept_timeout/1000,(accept_timeout%1000)*1000 };
int n=::select( 0,&fd,0,0,&tv );
if( n==0 ) return 0;
if( n!=1 ){ e=-1;return 0; }
SOCKET t=::accept( sock,0,0 );
if( t==INVALID_SOCKET ){ e=-1;return 0; }
TCPStream *s=d_new TCPStream( t,this );
accepted_set.insert( s );
return s;
}
void TCPServer::remove( TCPStream *s ){
accepted_set.erase( s );
}
static inline void debugUDPStream( UDPStream *p ){
if( debug && !udp_set.count(p) ){
RTEX( "UDP Stream does not exist" );
}
}
static inline void debugTCPStream( TCPStream *p ){
if( debug && !tcp_set.count(p) ){
RTEX( "TCP Stream does not exist" );
}
}
static inline void debugTCPServer( TCPServer *p ){
if( debug && !server_set.count(p) ){
RTEX( "TCP Server does not exist" );
}
}
static vector<int> host_ips;
int bbCountHostIPs( BBStr *host ){
host_ips.clear();
HOSTENT *h=gethostbyname( host->c_str() );
delete host;if( !h ) return 0;
char **p=h->h_addr_list;
while( char *t=*p++ ) host_ips.push_back( ntohl(*(int*)t) );
return host_ips.size();
}
int bbHostIP( int index ){
if( debug ){
if( index<1 || index>host_ips.size() ){
RTEX( "Host index out of range" );
}
}
return host_ips[index-1];
}
UDPStream *bbCreateUDPStream( int port ){
if( !socks_ok ) return 0;
SOCKET s=::socket( AF_INET,SOCK_DGRAM,0 );
if( s!=INVALID_SOCKET ){
sockaddr_in addr={AF_INET,htons(port)};
if( !::bind( s,(sockaddr*)&addr,sizeof(addr) ) ){
UDPStream *p=d_new UDPStream( s );
udp_set.insert( p );
return p;
}
::closesocket( s );
}
return 0;
}
void bbCloseUDPStream( UDPStream *p ){
debugUDPStream( p );
udp_set.erase( p );
delete p;
}
int bbRecvUDPMsg( UDPStream *p ){
debugUDPStream( p );
return p->recv();
}
void bbSendUDPMsg( UDPStream *p,int ip,int port ){
debugUDPStream( p );
p->send( ip,port );
}
int bbUDPStreamIP( UDPStream *p ){
debugUDPStream( p );
return p->getIP();
}
int bbUDPStreamPort( UDPStream *p ){
debugUDPStream( p );
return p->getPort();
}
int bbUDPMsgIP( UDPStream *p ){
debugUDPStream( p );
return p->getMsgIP();
}
int bbUDPMsgPort( UDPStream *p ){
debugUDPStream( p );
return p->getMsgPort();
}
void bbUDPTimeouts( int rt ){
recv_timeout=rt;
}
BBStr *bbDottedIP( int ip ){
return d_new BBStr(
itoa((ip>>24)&255)+"."+itoa((ip>>16)&255)+"."+
itoa((ip>>8)&255)+"."+itoa(ip&255) );
}
static int findHostIP( const string &t ){
int ip=inet_addr( t.c_str() );
if( ip!=INADDR_NONE ) return ip;
HOSTENT *h=gethostbyname( t.c_str() );
if( !h ) return -1;
char *p;
for( char **list=h->h_addr_list;p=*list;++list ){
return *(int*)p;
}
return 0;
}
TCPStream *bbOpenTCPStream( BBStr *server,int port,int local_port ){
if( !socks_ok ){
delete server;
return 0;
}
int ip=findHostIP( *server );delete server;
if( ip==-1 ) return 0;
SOCKET s=::socket( AF_INET,SOCK_STREAM,0 );
if( s!=INVALID_SOCKET ){
if( local_port ){
sockaddr_in addr={AF_INET,htons(local_port)};
if( ::bind( s,(sockaddr*)&addr,sizeof(addr) ) ){
::closesocket( s );
return 0;
}
}
sockaddr_in addr={AF_INET,htons(port)};
addr.sin_addr.S_un.S_addr=ip;
if( !::connect( s,(sockaddr*)&addr,sizeof(addr) ) ){
TCPStream *p=d_new TCPStream( s,0 );
tcp_set.insert( p );
return p;
}
::closesocket( s );
}
return 0;
}
void bbCloseTCPStream( TCPStream *p ){
debugTCPStream( p );
tcp_set.erase( p );
delete p;
}
TCPServer * bbCreateTCPServer( int port ){
SOCKET s=::socket( AF_INET,SOCK_STREAM,0 );
if( s!=INVALID_SOCKET ){
sockaddr_in addr={AF_INET,htons(port)};
if( !::bind( s,(sockaddr*)&addr,sizeof(addr) ) ){
if( !::listen( s,SOMAXCONN ) ){
TCPServer *p=d_new TCPServer( s );
server_set.insert( p );
return p;
}
}
::closesocket(s);
}
return 0;
}
void bbCloseTCPServer( TCPServer *p ){
debugTCPServer( p );
server_set.erase( p );
delete p;
}
TCPStream * bbAcceptTCPStream( TCPServer *server ){
debugTCPServer( server );
if( !gx_runtime->idle() ) RTEX( 0 );
if( TCPStream *tcp=server->accept() ){
tcp_set.insert( tcp );
return tcp;
}
return 0;
}
int bbTCPStreamIP( TCPStream *p ){
debugTCPStream( p );
return p->getIP();
}
int bbTCPStreamPort( TCPStream *p ){
debugTCPStream( p );
return p->getPort();
}
void bbTCPTimeouts( int rt,int at ){
read_timeout=rt;
accept_timeout=at;
}
bool sockets_create(){
socks_ok=WSAStartup( 0x0101,&wsadata )==0;
recv_timeout=0;
read_timeout=10000;
accept_timeout=0;
return true;
}
bool sockets_destroy(){
while( udp_set.size() ) bbCloseUDPStream( *udp_set.begin() );
while( tcp_set.size() ) bbCloseTCPStream( *tcp_set.begin() );
while( server_set.size() ) bbCloseTCPServer( *server_set.begin() );
if( socks_ok ) WSACleanup();
return true;
}
void sockets_link( void(*rtSym)(const char*,void*) ){
rtSym( "$DottedIP%IP",bbDottedIP );
rtSym( "%CountHostIPs$host_name",bbCountHostIPs );
rtSym( "%HostIP%host_index",bbHostIP );
rtSym( "%CreateUDPStream%port=0",bbCreateUDPStream );
rtSym( "CloseUDPStream%udp_stream",bbCloseUDPStream );
rtSym( "SendUDPMsg%udp_stream%dest_ip%dest_port=0",bbSendUDPMsg );
rtSym( "%RecvUDPMsg%udp_stream",bbRecvUDPMsg );
rtSym( "%UDPStreamIP%udp_stream",bbUDPStreamIP );
rtSym( "%UDPStreamPort%udp_stream",bbUDPStreamPort );
rtSym( "%UDPMsgIP%udp_stream",bbUDPMsgIP );
rtSym( "%UDPMsgPort%udp_stream",bbUDPMsgPort );
rtSym( "UDPTimeouts%recv_timeout",bbUDPTimeouts );
rtSym( "%OpenTCPStream$server%server_port%local_port=0",bbOpenTCPStream );
rtSym( "CloseTCPStream%tcp_stream",bbCloseTCPStream );
rtSym( "%CreateTCPServer%port",bbCreateTCPServer );
rtSym( "CloseTCPServer%tcp_server",bbCloseTCPServer );
rtSym( "%AcceptTCPStream%tcp_server",bbAcceptTCPStream );
rtSym( "%TCPStreamIP%tcp_stream",bbTCPStreamIP );
rtSym( "%TCPStreamPort%tcp_stream",bbTCPStreamPort );
rtSym( "TCPTimeouts%read_millis%accept_millis",bbTCPTimeouts );
}