18.03.2013 - Bug fixes, add in/out speed diagnostic to PIProtocol, fixed PIConsole tab switch segfault, PIObject EVENT / EVENT_HANDLER mechanism update - new EVENT macros that use EVENT_HANDLER with raiseEvent implementation.

This allow compile check event for CONNECT and use EVENT as CONNECT target, also raise event now is simple execute EVENT function.
This commit is contained in:
peri4
2013-03-18 12:07:44 +04:00
parent cfc5eed75e
commit 66c53a27fc
72 changed files with 4407 additions and 960 deletions

351
piethernet.cpp Executable file → Normal file
View File

@@ -1,7 +1,7 @@
/*
PIP - Platform Independent Primitives
Ethernet, UDP
Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,16 +27,13 @@ PIEthernet::PIEthernet(void * data, ReadRetFunc slot): PIIODevice("", ReadWrite)
ret_data_ = data;
ip_ = ip_s = "";
port_ = port_s = 0;
sock = -1;
sock = sock_s = -1;
ret_func_ = slot;
connected_ = false;
params = PIEthernet::ReuseAddress;
server_thread_.setData(this);
setThreadedReadBufferSize(65536);
#ifdef WINDOWS
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
init();
if (type_ != UDP) init();
}
@@ -47,16 +44,13 @@ PIEthernet::PIEthernet(PIEthernet::Type type, void * data, ReadRetFunc slot): PI
ret_data_ = data;
ip_ = ip_s = "";
port_ = port_s = 0;
sock = -1;
sock = sock_s = -1;
ret_func_ = slot;
connected_ = false;
params = PIEthernet::ReuseAddress;
server_thread_.setData(this);
setThreadedReadBufferSize(65536);
#ifdef WINDOWS
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
init();
if (type_ != UDP) init();
}
@@ -66,13 +60,11 @@ PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) {
type_ = TCP_Client;
parseAddress(ip_port, &ip_s, &port_s);
sock = sock_;
sock_s = -1;
server_thread_.setData(this);
params = PIEthernet::ReuseAddress;
init_ = opened_ = connected_ = true;
setThreadedReadBufferSize(65536);
#ifdef WINDOWS
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
}
@@ -80,9 +72,6 @@ PIEthernet::~PIEthernet() {
piMonitor.ethernets--;
if (server_thread_.isRunning()) server_thread_.terminate();
closeSocket(sock);
#ifdef WINDOWS
WSACleanup();
#endif
//if (buffer_ != 0) delete buffer_;
//buffer_ = 0;
}
@@ -91,15 +80,29 @@ PIEthernet::~PIEthernet() {
bool PIEthernet::init() {
//cout << "init " << type_ << endl;
closeSocket(sock);
int st = 0, so = 1;
int st = 0;
#ifdef WINDOWS
int flags = WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF;
#else
int so = 1;
#endif
if (type_ == UDP) st = SOCK_DGRAM;
if (type_ == TCP_Client || type_ == TCP_Server) st = SOCK_STREAM;
sock = socket(PF_INET, st, 0);
else st = SOCK_STREAM;
#ifdef WINDOWS
if (params[ReuseAddress]) flags |= WSA_FLAG_OVERLAPPED;
sock = WSASocket(AF_INET, st, type_ == UDP ? IPPROTO_UDP : IPPROTO_TCP, NULL, 0, flags);
#else
sock = socket(AF_INET, st, type_ == UDP ? IPPROTO_UDP : IPPROTO_TCP);
#endif
if (sock == -1) {
piCout << "[PIEthernet] Cant`t create socket, " << errorString() << endl;
piCout << "[PIEthernet] Cant`t create socket, " << EthErrorString() << endl;
return false;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &so, sizeof(so));
#ifndef WINDOWS
if (params[PIEthernet::ReuseAddress]) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &so, sizeof(so));
if (params[PIEthernet::Broadcast]) setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so, sizeof(so));
#endif
//cout << "inited " << sock << ": bc = " << params << endl;
//fcntl(sock, F_SETFL, 0/*O_NONBLOCK*/);
return true;
}
@@ -114,15 +117,24 @@ void PIEthernet::parseAddress(const PIString & ipp, PIString * ip, int * port) {
bool PIEthernet::openDevice() {
if (connected_) return true;
if (sock == -1) init();
if (sock == -1) return false;
//cout << " bind to " << path_ << " ..." <<endl;
if (sock == -1 || path_.isEmpty()) return false;
parseAddress(path_, &ip_, &port_);
//cout << " bind to " << sock << ": " << (params[PIEthernet::Broadcast] ? "0.0.0.0" : path_) << ": " << port_ << " ..." <<endl;
memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(ip_.data());
addr_.sin_family = PF_INET;
//cout << "openDevice\n";
if (bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) {
piCout << "[PIEthernet] Cant`t bind to " << ip_ << ":" << port_ << ", " << errorString() << endl;
if (params[PIEthernet::Broadcast]) addr_.sin_addr.s_addr = INADDR_ANY;
else addr_.sin_addr.s_addr = inet_addr(ip_.data());
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
int tries = 0;
while ((bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) && (tries < 10)) {
init();
tries++;
}
if (tries == 10) {
piCout << "[PIEthernet] Cant`t bind to " << ip_ << ":" << port_ << ", " << EthErrorString() << endl;
return false;
}
//cout << "!" << endl;
@@ -143,15 +155,89 @@ bool PIEthernet::closeDevice() {
}
bool PIEthernet::connect(PIString ip, int port) {
bool PIEthernet::joinMulticastGroup(const PIString & group) {
if (sock == -1) init();
if (sock == -1) return false;
if (type_ != UDP) {
piCout << "[PIEthernet] Only UDP sockets can join multicast groups" << endl;
return false;
}
#ifdef WINDOWS
parseAddress(path_, &ip_, &port_);
memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(group.data());
SOCKET ret = WSAJoinLeaf(sock, (sockaddr *)&addr_, sizeof(addr_), NULL, NULL, NULL, NULL, JL_BOTH);
if (ret == INVALID_SOCKET) {
piCout << "[PIEthernet] Cant`t join multicast group " << group << ", " << EthErrorString() << endl;
return false;
}
leafs.insert(group, ret);
#else
# ifndef QNX
parseAddress(path_, &ip_, &port_);
struct ip_mreqn mreq;
memset(&mreq, 0, sizeof(mreq));
if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY;
else mreq.imr_address.s_addr = inet_addr(ip_.data());
mreq.imr_multiaddr.s_addr = inet_addr(group.data());
mreq.imr_ifindex = 0;
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
piCout << "[PIEthernet] Cant`t join multicast group " << group << ", " << EthErrorString() << endl;
return false;
}
# endif
#endif
return true;
}
bool PIEthernet::leaveMulticastGroup(const PIString & group) {
if (sock == -1) init();
if (sock == -1) return false;
if (type_ != UDP) {
piCout << "[PIEthernet] Only UDP sockets can leave multicast groups" << endl;
return false;
}
#ifdef WINDOWS
SOCKET s = leafs[group];
if (s != SOCKET()) {
leafs.erase(group);
closesocket(s);
}
#else
# ifndef QNX
parseAddress(path_, &ip_, &port_);
struct ip_mreqn mreq;
memset(&mreq, 0, sizeof(mreq));
if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY;
else mreq.imr_address.s_addr = inet_addr(ip_.data());
mreq.imr_multiaddr.s_addr = inet_addr(group.data());
mreq.imr_ifindex = 0;
if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
piCout << "[PIEthernet] Cant`t leave multicast group " << group << ", " << EthErrorString() << endl;
return false;
}
#endif
#endif
return true;
}
bool PIEthernet::connect(const PIString & ip, int port) {
if (sock == -1) return false;
memset(&addr_, 0, sizeof(addr_));
addr_.sin_port = htons(port);
addr_.sin_addr.s_addr = inet_addr(ip.data());
addr_.sin_family = PF_INET;
addr_.sin_family = AF_INET;
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
//piCout << "[PIEthernet] connect to " << ip << ":" << port << endl;
connected_ = (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) == 0);
if (!connected_)
piCout << "[PIEthernet] Cant`t connect to " << ip << ":" << port << ", " << errorString() << endl;
piCout << "[PIEthernet] Cant`t connect to " << ip << ":" << port << ", " << EthErrorString() << endl;
opened_ = connected_;
return connected_;
}
@@ -161,15 +247,24 @@ bool PIEthernet::listen() {
if (sock == -1) init();
if (sock == -1) return false;
parseAddress(path_, &ip_, &port_);
memset(&addr_, 0, sizeof(addr_));
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(ip_.data());
addr_.sin_family = PF_INET;
if (bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) {
piCout << "[PIEthernet] Cant`t bind to " << ip_ << ":" << port_ << ", " << errorString() << endl;
addr_.sin_family = AF_INET;
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
int tries = 0;
while ((bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) && (tries < 10)) {
init();
tries++;
}
if (tries == 10) {
piCout << "[PIEthernet] Cant`t bind to " << ip_ << ":" << port_ << ", " << EthErrorString() << endl;
return false;
}
if (::listen(sock, 64) == -1) {
piCout << "[PIEthernet] Can`t listen on "<< ip_ << ":" << port_ << ", " << errorString() << endl;
piCout << "[PIEthernet] Can`t listen on "<< ip_ << ":" << port_ << ", " << EthErrorString() << endl;
return false;
}
//piCout << "[PIEthernet] listen on " << ip_ << ":" << port_ << endl;
@@ -181,11 +276,30 @@ bool PIEthernet::listen() {
int PIEthernet::read(void * read_to, int max_size) {
//cout << "read " << sock << endl;
if (sock == -1) init();
if (sock == -1) return -1;
if (sock == -1 || read_to == 0) return -1;
int rs = 0, s = 0;
sockaddr_in client_addr;
socklen_t slen = sizeof(client_addr);
//piCout << "[PIEthernet] read from " << ip_ << ":" << port_ << endl;
switch (type_) {
case TCP_SingleTCP:
::listen(sock, 64);
s = accept(sock, (sockaddr * )&client_addr, &slen);
if (s == -1) {
piCout << "[PIEthernet] Cant`t accept new connection, " << EthErrorString() << endl;
return -1;
}
rs = recv(s, (char * )read_to, max_size, 0);
closeSocket(s);
return rs;
case UDP: case TCP_Client:
return recv(sock, read_to, max_size, 0);
#ifdef WINDOWS
rs = recv(sock, (char * )read_to, max_size, 0);
#else
rs = recv(sock, read_to, max_size, 0);
#endif
if (rs >= 0) received(read_to, rs);
return rs;
//return ::read(sock, read_to, max_size);
default: break;
//return ::read(sock, (char * )read_to, max_size);
@@ -201,12 +315,34 @@ int PIEthernet::write(const void * data, int max_size) {
return -1;
}
//piCout << "[PIEthernet] sending to " << ip_s << ":" << port_s << " " << max_size << " bytes" << endl;
int ret = 0;
switch (type_) {
case TCP_SingleTCP:
memset(&addr_, 0, sizeof(addr_));
addr_.sin_port = htons(port_s);
addr_.sin_addr.s_addr = inet_addr(ip_s.data());
addr_.sin_family = AF_INET;
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
if (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) != 0) {
piCout << "[PIEthernet] Cant`t connect to " << ip_s << ":" << port_s << ", " << EthErrorString() << endl;
return -1;
}
ret = ::write(sock, data, max_size);
closeSocket(sock);
init();
return ret;
case UDP:
saddr_.sin_port = htons(port_s);
saddr_.sin_addr.s_addr = inet_addr(ip_s.data());
saddr_.sin_family = PF_INET;
if (params[PIEthernet::Broadcast]) saddr_.sin_addr.s_addr = INADDR_BROADCAST;
else saddr_.sin_addr.s_addr = inet_addr(ip_s.data());
saddr_.sin_family = AF_INET;
#ifdef WINDOWS
return sendto(sock, (const char * )data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_));
#else
return sendto(sock, data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_));
#endif
case TCP_Client:
return ::write(sock, data, max_size);
default: break;
@@ -219,18 +355,139 @@ void PIEthernet::server_func(void * eth) {
PIEthernet * ce = (PIEthernet * )eth;
sockaddr_in client_addr;
socklen_t slen = sizeof(client_addr);
int s;
s = accept(ce->sock, (sockaddr * )&client_addr, &slen);
int s = accept(ce->sock, (sockaddr * )&client_addr, &slen);
if (s == -1) {
piCout << "[PIEthernet] Cant`t accept new connection, " << errorString() << endl;
piCout << "[PIEthernet] Cant`t accept new connection, " << EthErrorString() << endl;
return;
}
PIString ip(inet_ntoa(client_addr.sin_addr));
ip += ":" + PIString::fromNumber(htons(client_addr.sin_port));
ce->clients_ << new PIEthernet(s, ip);
cout << "connected " << ip << endl;
//cout << "connected " << ip << endl;
//char d[256];
//cout << " recv " << recv(s, d, 256, 0) << endl;
//cout << recv(ce->clients_.back()->sock, d, 256, 0) << endl;
}
PIStringList PIEthernet::interfaces() {
#ifdef WINDOWS
piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" instead" << endl;
return PIStringList();
#else
# ifdef QNX
PIStringList il, sl;
/*struct if_nameindex * ni = if_nameindex();
for (int i = 0; ; ++i) {
if (ni[i].if_name == 0 || ni[i].if_index == 0) break;
sl << PIString(ni[i].if_name);
}
if_freenameindex(ni);*/
PIProcess proc;
proc.setGrabOutput(true);
proc.exec(ifconfigPath.c_str(), "-l");
if (!proc.waitForFinish(1000)) return sl;
PIString out(proc.readOutput());
il = out.split(" ");
il.removeAll("");
piForeachC (PIString & i, il) {
proc.exec(ifconfigPath.c_str(), i);
if (!proc.waitForFinish(1000)) return il;
sl << i.trimmed();
out = proc.readOutput();
int al = out.length();
al = (al - out.replaceAll("alias", "").length()) / 5;
for (int j = 0; j < al; ++j)
sl << i.trimmed() + ":" + PIString::fromNumber(j);
}
//cout << out;
//cout << sl << endl;
return sl;
# else
PIStringList sl;
/*struct if_nameindex * ni = if_nameindex();
for (int i = 0; ; ++i) {
if (ni[i].if_name == 0 || ni[i].if_index == 0) break;
sl << PIString(ni[i].if_name);
}
if_freenameindex(ni);*/
PIProcess proc;
proc.setGrabOutput(true);
proc.exec(ifconfigPath.c_str(), "-s");
if (!proc.waitForFinish(1000)) return sl;
PIString out(proc.readOutput());
//cout << out << endl;
out.cutLeft(out.find('\n') + 1);
while (!out.isEmpty()) {
sl << out.left(out.find(' '));
out.cutLeft(out.find('\n') + 1);
}
//cout << sl << endl;
return sl;
# endif
#endif
}
PIString PIEthernet::interfaceAddress(const PIString & interface_) {
#ifdef WINDOWS
piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" instead" << endl;
return PIString();
#else
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, interface_.data());
int s = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(s, SIOCGIFADDR, &ifr);
::close(s);
struct sockaddr_in * sa = (struct sockaddr_in * )&ifr.ifr_addr;
return PIString(inet_ntoa(sa->sin_addr));
#endif
}
PIStringList PIEthernet::allAddresses() {
#ifdef WINDOWS
PIStringList al;
PIString ca;
PIP_ADAPTER_INFO pAdapterInfo, pAdapter = 0;
int ret = 0;
ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO);
pAdapterInfo = (IP_ADAPTER_INFO * ) HeapAlloc(GetProcessHeap(), 0, (sizeof (IP_ADAPTER_INFO)));
if (pAdapterInfo == 0) {
piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo" << endl;
return PIStringList();
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
HeapFree(GetProcessHeap(), 0, (pAdapterInfo));
pAdapterInfo = (IP_ADAPTER_INFO *) HeapAlloc(GetProcessHeap(), 0, (ulOutBufLen));
if (pAdapterInfo == 0) {
piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo" << endl;
return PIStringList();
}
}
if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
pAdapter = pAdapterInfo;
while (pAdapter) {
/*if (pAdapter->Type != MIB_IF_TYPE_ETHERNET && pAdapter->Type != MIB_IF_TYPE_LOOPBACK) {
pAdapter = pAdapter->Next;
continue;
}*/
ca = PIString(pAdapter->IpAddressList.IpAddress.String);
if (ca != "0.0.0.0") al << ca;
pAdapter = pAdapter->Next;
}
} else
piCout << "[PIEthernet] GetAdaptersInfo failed with error: " << ret << endl;
if (pAdapterInfo)
HeapFree(GetProcessHeap(), 0, (pAdapterInfo));
return al;
#else
PIStringList il = interfaces(), al;
piForeachC (PIString & i, il)
al << interfaceAddress(i);
return al;
#endif
}