506 lines
11 KiB
C++
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())[0], 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())[0], 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)) {
|
|
ThrowRuntimeException("UDP Stream does not exist");
|
|
}
|
|
}
|
|
|
|
static inline void debugTCPStream(TCPStream *p) {
|
|
if (debug && !tcp_set.count(p)) {
|
|
ThrowRuntimeException("TCP Stream does not exist");
|
|
}
|
|
}
|
|
|
|
static inline void debugTCPServer(TCPServer *p) {
|
|
if (debug && !server_set.count(p)) {
|
|
ThrowRuntimeException("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()) {
|
|
ThrowRuntimeException("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()) ThrowRuntimeException(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);
|
|
}
|