Add files via upload

This commit is contained in:
Dave Nardella 2025-03-15 12:20:11 +01:00 committed by GitHub
parent ddcae72b01
commit f3da2df734
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 3062 additions and 0 deletions

View File

@ -0,0 +1,923 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#include "snap_msgsock.h"
//---------------------------------------------------------------------------
static SocketsLayer SocketsLayerInitializer;
//---------------------------------------------------------------------------
// Base class endian aware
//---------------------------------------------------------------------------
TSnapBase::TSnapBase()
{
int x = 1;
LittleEndian=*(char *)&x == 1;
}
//---------------------------------------------------------------------------
word TSnapBase::SwapWord(word Value)
{
if (LittleEndian)
return ((Value >> 8) & 0xFF) | ((Value << 8) & 0xFF00);
else
return Value;
}
//---------------------------------------------------------------------------
longword TSnapBase::SwapDWord(longword Value)
{
if (LittleEndian)
return (Value >> 24) | ((Value << 8) & 0x00FF0000) | ((Value >> 8) & 0x0000FF00) | (Value << 24);
else
return Value;
}
//---------------------------------------------------------------------------
void Msg_CloseSocket(socket_t FSocket)
{
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
}
//---------------------------------------------------------------------------
longword Msg_GetSockAddr(socket_t FSocket)
{
sockaddr_in RemoteSin;
#ifdef OS_WINDOWS
int namelen = sizeof(RemoteSin);
#else
uint32_t namelen = sizeof(RemoteSin);
#endif
namelen=sizeof(sockaddr_in);
if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
return RemoteSin.sin_addr.s_addr;
else
return 0;
}
//---------------------------------------------------------------------------
TMsgSocket::TMsgSocket()
{
Pinger = new TPinger();
// Set Defaults
strcpy(LocalAddress,"0.0.0.0");
LocalPort=0;
strcpy(RemoteAddress,"127.0.0.1");
RemotePort=0;
WorkInterval=100;
RecvTimeout=500;
SendTimeout=10;
PingTimeout=750;
Connected=false;
FSocket=INVALID_SOCKET;
LastTcpError=0;
LocalBind=0;
}
//---------------------------------------------------------------------------
TMsgSocket::~TMsgSocket()
{
DestroySocket();
delete Pinger;
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSin(sockaddr_in &sin, char *Address, u_short Port)
{
uint32_t in_addr;
in_addr=inet_addr(Address);
memset(&sin, 0, sizeof(sin));
LastTcpError=0;
if (in_addr!=INADDR_NONE)
{
sin.sin_addr.s_addr = in_addr; //INADDR_ANY;
sin.sin_family = AF_INET;
sin.sin_port = htons(Port);
}
else
LastTcpError=WSAEINVALIDADDRESS;
}
//---------------------------------------------------------------------------
void TMsgSocket::GetSin(sockaddr_in sin, char *Address, u_short &Port)
{
strcpy(Address,inet_ntoa(sin.sin_addr));
Port=htons(sin.sin_port);
}
//---------------------------------------------------------------------------
void TMsgSocket::GetLocal()
{
#ifdef OS_WINDOWS
int namelen = sizeof(LocalSin);
#else
uint32_t namelen = sizeof(LocalSin);
#endif
if (getsockname(FSocket, (struct sockaddr*)&LocalSin, &namelen)==0)
GetSin(LocalSin, LocalAddress, LocalPort);
}
//---------------------------------------------------------------------------
void TMsgSocket::GetRemote()
{
#ifdef OS_WINDOWS
int namelen = sizeof(RemoteSin);
#else
uint32_t namelen = sizeof(RemoteSin);
#endif
if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
GetSin(RemoteSin, RemoteAddress, RemotePort);
}
//---------------------------------------------------------------------------
int TMsgSocket::GetLastSocketError()
{
#ifdef OS_WINDOWS
return WSAGetLastError();
#else
return errno;
#endif
}
//---------------------------------------------------------------------------
void TMsgSocket::Purge()
{
// small buffer to empty the socket
char Trash[512];
int Read;
if (LastTcpError!=WSAECONNRESET)
{
if (CanRead(0)) {
do
{
Read=recv(FSocket, Trash, 512, MSG_NOSIGNAL );
} while(Read==512);
}
}
}
//---------------------------------------------------------------------------
void TMsgSocket::CreateSocket()
{
DestroySocket();
LastTcpError=0;
FSocket =socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (FSocket!=INVALID_SOCKET)
SetSocketOptions();
else
LastTcpError =GetLastSocketError();
}
//---------------------------------------------------------------------------
void TMsgSocket::GotSocket()
{
ClientHandle=RemoteSin.sin_addr.s_addr;
// could be inherited it if wee need further actions on the socket
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSocket(socket_t s)
{
FSocket=s;
if (FSocket!=INVALID_SOCKET)
{
SetSocketOptions();
GetLocal();
GetRemote();
GotSocket();
}
Connected=FSocket!=INVALID_SOCKET;
}
//---------------------------------------------------------------------------
void TMsgSocket::DestroySocket()
{
if(FSocket != INVALID_SOCKET)
{
if (shutdown(FSocket, SD_SEND)==0)
Purge();
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
FSocket=INVALID_SOCKET;
}
LastTcpError=0;
}
//---------------------------------------------------------------------------
int TMsgSocket::WaitingData()
{
int result = 0;
u_long x = 0;
#ifdef OS_WINDOWS
if (ioctlsocket(FSocket, FIONREAD, &x) == 0)
result = x;
#else
if (ioctl(FSocket, FIONREAD, &x) == 0)
result = x;
#endif
if (result>MaxPacketSize)
result = MaxPacketSize;
return result;
}
//---------------------------------------------------------------------------
int TMsgSocket::WaitForData(int Size, int Timeout)
{
longword Elapsed;
// Check for connection active
if (CanRead(0) && (WaitingData()==0))
LastTcpError=WSAECONNRESET;
else
LastTcpError=0;
// Enter main loop
if (LastTcpError==0)
{
Elapsed =SysGetTick();
while((WaitingData()<Size) && (LastTcpError==0))
{
// Checks timeout
if (DeltaTime(Elapsed)>=(longword)(Timeout))
LastTcpError =WSAETIMEDOUT;
else
SysSleep(1);
}
}
if(LastTcpError==WSAECONNRESET)
Connected =false;
return LastTcpError;
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSocketOptions()
{
int NoDelay = 1;
int KeepAlive = 1;
LastTcpError=0;
SockCheck(setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&NoDelay, sizeof(NoDelay)));
if (LastTcpError==0)
SockCheck(setsockopt(FSocket, SOL_SOCKET, SO_KEEPALIVE,(char*)&KeepAlive, sizeof(KeepAlive)));
}
//---------------------------------------------------------------------------
int TMsgSocket::SockCheck(int SockResult)
{
if (SockResult == (int)(SOCKET_ERROR))
LastTcpError = GetLastSocketError();
return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::CanWrite(int Timeout)
{
timeval TimeV;
int64_t x;
fd_set FDset;
if(FSocket == INVALID_SOCKET)
return false;
TimeV.tv_usec = (Timeout % 1000) * 1000;
TimeV.tv_sec = Timeout / 1000;
FD_ZERO(&FDset);
FD_SET(FSocket, &FDset);
x = select(FSocket + 1, NULL, &FDset, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
if (x==(int)SOCKET_ERROR)
{
LastTcpError = GetLastSocketError();
x=0;
}
return (x > 0);
}
//---------------------------------------------------------------------------
bool TMsgSocket::CanRead(int Timeout)
{
timeval TimeV;
int64_t x;
fd_set FDset;
if(FSocket == INVALID_SOCKET)
return false;
TimeV.tv_usec = (Timeout % 1000) * 1000;
TimeV.tv_sec = Timeout / 1000;
FD_ZERO(&FDset);
FD_SET(FSocket, &FDset);
x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
if (x==(int)SOCKET_ERROR)
{
LastTcpError = GetLastSocketError();
x=0;
}
return (x > 0);
}
//---------------------------------------------------------------------------
#ifdef NON_BLOCKING_CONNECT
//
// Non blocking connection (UNIX) Thanks to Rolf Stalder
//
int TMsgSocket::SckConnect()
{
int n, flags, err;
socklen_t len;
fd_set rset, wset;
struct timeval tval;
SetSin(RemoteSin, RemoteAddress, RemotePort);
if (LastTcpError == 0) {
CreateSocket();
if (LastTcpError == 0) {
flags = fcntl(FSocket, F_GETFL, 0);
if (flags >= 0) {
if (fcntl(FSocket, F_SETFL, flags | O_NONBLOCK) != -1) {
n = connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
if (n < 0) {
if (errno != EINPROGRESS) {
LastTcpError = GetLastSocketError();
}
else {
// still connecting ...
FD_ZERO(&rset);
FD_SET(FSocket, &rset);
wset = rset;
tval.tv_sec = PingTimeout / 1000;
tval.tv_usec = (PingTimeout % 1000) * 1000;
n = select(FSocket+1, &rset, &wset, NULL,
(PingTimeout ? &tval : NULL));
if (n == 0) {
// timeout
LastTcpError = WSAEHOSTUNREACH;
}
else {
if (FD_ISSET(FSocket, &rset) || FD_ISSET(FSocket, &wset)) {
err = 0;
len = sizeof(err);
if (getsockopt(
FSocket, SOL_SOCKET, SO_ERROR, &err, &len) == 0) {
if (err) {
LastTcpError = err;
}
else {
if (fcntl(FSocket, F_SETFL, flags) != -1) {
GetLocal();
ClientHandle = LocalSin.sin_addr.s_addr;
}
else {
LastTcpError = GetLastSocketError();
}
}
}
else {
LastTcpError = GetLastSocketError();
}
}
else {
LastTcpError = -1;
}
}
} // still connecting
}
else if (n == 0) {
// connected immediatly
GetLocal();
ClientHandle = LocalSin.sin_addr.s_addr;
}
}
else {
LastTcpError = GetLastSocketError();
} // fcntl(F_SETFL)
}
else {
LastTcpError = GetLastSocketError();
} // fcntl(F_GETFL)
} //valid socket
} // LastTcpError==0
Connected=LastTcpError==0;
return LastTcpError;
}
#else
//
// Regular connection (Windows)
//
int TMsgSocket::SckConnect()
{
int Result;
SetSin(RemoteSin, RemoteAddress, RemotePort);
if (LastTcpError==0)
{
if (Ping(RemoteSin))
{
CreateSocket();
if (LastTcpError==0)
{
Result=connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
if (SockCheck(Result)==0)
{
GetLocal();
// Client handle is self_address (here the connection is ACTIVE)
ClientHandle=LocalSin.sin_addr.s_addr;
}
}
}
else
LastTcpError=WSAEHOSTUNREACH;
}
Connected=LastTcpError==0;
return LastTcpError;
}
#endif
//---------------------------------------------------------------------------
void TMsgSocket::SckDisconnect()
{
DestroySocket();
Connected=false;
}
//---------------------------------------------------------------------------
void TMsgSocket::ForceClose()
{
if(FSocket != INVALID_SOCKET)
{
try {
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
} catch (...) {
}
FSocket=INVALID_SOCKET;
}
LastTcpError=0;
}
//---------------------------------------------------------------------------
int TMsgSocket::SckBind()
{
int Res;
int Opt=1;
SetSin(LocalSin, LocalAddress, LocalPort);
if (LastTcpError==0)
{
CreateSocket();
if (LastTcpError==0)
{
setsockopt(FSocket ,SOL_SOCKET, SO_REUSEADDR, (const char *)&Opt, sizeof(int));
Res =bind(FSocket, (struct sockaddr*)&LocalSin, sizeof(sockaddr_in));
SockCheck(Res);
if (Res==0)
{
LocalBind=LocalSin.sin_addr.s_addr;
}
}
}
else
LastTcpError=WSAEINVALIDADDRESS;
return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::SckListen()
{
LastTcpError=0;
SockCheck(listen(FSocket ,SOMAXCONN));
return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::Ping(char *Host)
{
return Pinger->Ping(Host, PingTimeout);
}
//---------------------------------------------------------------------------
bool TMsgSocket::Ping(sockaddr_in Addr)
{
if (PingTimeout == 0)
return true;
else
return Pinger->Ping(Addr.sin_addr.s_addr, PingTimeout);
}
//---------------------------------------------------------------------------
socket_t TMsgSocket::SckAccept()
{
socket_t result;
LastTcpError=0;
result = accept(FSocket, NULL, NULL);
if(result==INVALID_SOCKET)
LastTcpError =GetLastSocketError();
return result;
}
//---------------------------------------------------------------------------
int TMsgSocket::SendPacket(void *Data, int Size)
{
int Result;
LastTcpError=0;
if (SendTimeout>0)
{
if (!CanWrite(SendTimeout))
{
LastTcpError = WSAETIMEDOUT;
return LastTcpError;
}
}
if (send(FSocket, (char*)Data, Size, MSG_NOSIGNAL)==Size)
return 0;
else
Result =SOCKET_ERROR;
SockCheck(Result);
return Result;
}
//---------------------------------------------------------------------------
bool TMsgSocket::PacketReady(int Size)
{
return (WaitingData()>=Size);
}
//---------------------------------------------------------------------------
int TMsgSocket::Receive(void *Data, int BufSize, int &SizeRecvd)
{
LastTcpError=0;
if (CanRead(RecvTimeout))
{
SizeRecvd=recv(FSocket ,(char*)Data ,BufSize ,MSG_NOSIGNAL );
if (SizeRecvd>0) // something read (default case)
LastTcpError=0;
else
if (SizeRecvd==0)
LastTcpError = WSAECONNRESET; // Connection reset by Peer
else
LastTcpError=GetLastSocketError(); // we need to know what happened
}
else
LastTcpError = WSAETIMEDOUT;
if (LastTcpError==WSAECONNRESET)
Connected = false;
return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::RecvPacket(void *Data, int Size)
{
int BytesRead;
WaitForData(Size, RecvTimeout);
if (LastTcpError==0)
{
BytesRead=recv(FSocket, (char*)Data, Size, MSG_NOSIGNAL);
if (BytesRead==0)
LastTcpError = WSAECONNRESET; // Connection reset by Peer
else
if (BytesRead<0)
LastTcpError = GetLastSocketError();
}
else // After the timeout the bytes waiting were less then we expected
if (LastTcpError==WSAETIMEDOUT)
Purge();
if (LastTcpError==WSAECONNRESET)
Connected =false;
return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::PeekPacket(void *Data, int Size)
{
int BytesRead;
WaitForData(Size, RecvTimeout);
if (LastTcpError==0)
{
BytesRead=recv(FSocket, (char*)Data, Size, MSG_PEEK | MSG_NOSIGNAL );
if (BytesRead==0)
LastTcpError = WSAECONNRESET; // Connection reset by Peer
else
if (BytesRead<0)
LastTcpError = GetLastSocketError();
}
else // After the timeout the bytes waiting were less then we expected
if (LastTcpError==WSAETIMEDOUT)
Purge();
if (LastTcpError==WSAECONNRESET)
Connected =false;
return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::Execute()
{
return true;
}
//==============================================================================
// PING
//==============================================================================
static int PingKind;
#ifdef OS_WINDOWS
// iphlpapi, is loaded dinamically because if this fails we can still try
// to use raw sockets
static char const *iphlpapi = "\\iphlpapi.dll";
#pragma pack(1)
//typedef byte TTxBuffer[40];
typedef byte TTxBuffer[32];
#pragma pack()
typedef HANDLE (__stdcall *pfn_IcmpCreateFile)();
typedef bool (__stdcall *pfn_IcmpCloseHandle)(HANDLE PingHandle);
typedef int (__stdcall *pfn_IcmpSendEcho2)(
HANDLE PingHandle,
void *Event,
void *AcpRoutine,
void *AcpContext,
unsigned long DestinationAddress,
void *RequestData,
int RequestSize,
void *not_used, //should be *IP_OPTION_INFORMATION but we don't use it
void *ReplyBuffer,
int ReplySize,
int Timeout
);
static pfn_IcmpCreateFile IcmpCreateFile;
static pfn_IcmpCloseHandle IcmpCloseHandle;
static pfn_IcmpSendEcho2 IcmpSendEcho2;
static HINSTANCE IcmpDllHandle = 0;
static bool IcmpAvail = false;
bool IcmpInit()
{
char iphlppath[MAX_PATH+12];
int PathLen = GetSystemDirectoryA(iphlppath, MAX_PATH);
if (PathLen != 0)
{
strcat(iphlppath, iphlpapi);
IcmpDllHandle = LoadLibraryA(iphlppath);
}
else
IcmpDllHandle = 0;
if (IcmpDllHandle != 0)
{
IcmpCreateFile=(pfn_IcmpCreateFile)GetProcAddress(IcmpDllHandle,"IcmpCreateFile");
IcmpCloseHandle=(pfn_IcmpCloseHandle)GetProcAddress(IcmpDllHandle,"IcmpCloseHandle");
IcmpSendEcho2=(pfn_IcmpSendEcho2)GetProcAddress(IcmpDllHandle,"IcmpSendEcho2");
return (IcmpCreateFile!=NULL) && (IcmpCloseHandle!=NULL) && (IcmpSendEcho2!=NULL);
}
else
return false;
}
void IcmpDone()
{
if (IcmpDllHandle!=0)
FreeLibrary(IcmpDllHandle);
IcmpAvail=false;
}
#endif
//---------------------------------------------------------------------------
// RAW Socket Pinger
//---------------------------------------------------------------------------
TRawSocketPinger::TRawSocketPinger()
{
FSocket =socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
FId =word(size_t(this));
FSeq =0;
}
//---------------------------------------------------------------------------
TRawSocketPinger::~TRawSocketPinger()
{
if (FSocket!=INVALID_SOCKET)
{
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
FSocket=INVALID_SOCKET;
};
}
//---------------------------------------------------------------------------
void TRawSocketPinger::InitPacket()
{
memset(&IcmpBuffer,0,ICmpBufferSize);
FSeq++;
SendPacket=PIcmpPacket(pbyte(&IcmpBuffer)+sizeof(TIPHeader));
SendPacket->Header.ic_type=ICMP_ECHORQ;
SendPacket->Header.ic_code=0;
SendPacket->Header.ic_cksum=0;
SendPacket->Header.ic_id=FId;
SendPacket->Header.ic_seq=FSeq;
memset(&SendPacket->Data,0,sizeof(SendPacket->Data));
SendPacket->Header.ic_cksum=PacketChecksum();
}
//---------------------------------------------------------------------------
word TRawSocketPinger::PacketChecksum()
{
word *P = (word*)(SendPacket);
longword Sum = 0;
int c;
for (c = 0; c < int(sizeof(TIcmpPacket) / 2); c++) {
Sum+=*P;
P++;
}
Sum=(Sum >> 16) + (Sum & 0xFFFF);
Sum=Sum+(Sum >> 16);
return word(~Sum);
}
//---------------------------------------------------------------------------
bool TRawSocketPinger::CanRead(int Timeout)
{
timeval TimeV;
int64_t x;
fd_set FDset;
TimeV.tv_usec = (Timeout % 1000) * 1000;
TimeV.tv_sec = Timeout / 1000;
FD_ZERO(&FDset);
FD_SET(FSocket, &FDset);
x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
if (x==(int)(SOCKET_ERROR))
x=0;
return (x > 0);
}
//---------------------------------------------------------------------------
bool TRawSocketPinger::Ping(longword ip_addr, int Timeout)
{
sockaddr_in LSockAddr;
sockaddr_in RSockAddr;
PIcmpReply Reply;
if (FSocket==INVALID_SOCKET)
return true;
// Init packet
InitPacket();
Reply=PIcmpReply(&IcmpBuffer);
// Init Remote and Local Addresses struct
RSockAddr.sin_family=AF_INET;
RSockAddr.sin_port=0;
RSockAddr.sin_addr.s_addr=ip_addr;
LSockAddr.sin_family=AF_INET;
LSockAddr.sin_port=0;
LSockAddr.sin_addr.s_addr=inet_addr("0.0.0.0");
// Bind to local
if (bind(FSocket, (struct sockaddr*)&LSockAddr, sizeof(sockaddr_in))!=0)
return false;
// Connect to remote (not a really TCP connection, only to setup the socket)
if (connect(FSocket, (struct sockaddr*)&RSockAddr, sizeof(sockaddr_in))!=0)
return false;
// Send ICMP packet
if (send(FSocket, (char*)SendPacket, sizeof(TIcmpPacket), MSG_NOSIGNAL)!=int(sizeof(TIcmpPacket)))
return false;
// Wait for a reply
if (!CanRead(Timeout))
return false;// time expired
// Get the answer
if (recv(FSocket, (char*)&IcmpBuffer, ICmpBufferSize, MSG_NOSIGNAL)<int(sizeof(TIcmpReply)))
return false;
// Check the answer
return (Reply->IPH.ip_src==RSockAddr.sin_addr.s_addr) && // the peer is what we are looking for
(Reply->ICmpReply.Header.ic_type==ICMP_ECHORP); // type = reply
}
//---------------------------------------------------------------------------
// Pinger
//---------------------------------------------------------------------------
TPinger::TPinger()
{
}
//---------------------------------------------------------------------------
TPinger::~TPinger()
{
}
//---------------------------------------------------------------------------
bool TPinger::RawPing(longword ip_addr, int Timeout)
{
PRawSocketPinger RawPinger = new TRawSocketPinger();
bool Result;
Result=RawPinger->Ping(ip_addr, Timeout);
delete RawPinger;
return Result;
}
//---------------------------------------------------------------------------
#ifdef OS_WINDOWS
bool TPinger::WinPing(longword ip_addr, int Timeout)
{
HANDLE PingHandle;
TTxBuffer TxBuffer;
TIcmpBuffer IcmpBuffer;
bool Result;
PingHandle = IcmpCreateFile();
if (PingHandle != INVALID_HANDLE_VALUE)
{
memset(&TxBuffer,'\55',sizeof(TTxBuffer));
Result=(IcmpSendEcho2(PingHandle, NULL, NULL, NULL, ip_addr,
&TxBuffer, sizeof(TxBuffer), NULL, &IcmpBuffer, ICmpBufferSize, Timeout))>0;
IcmpCloseHandle(PingHandle);
return Result;
}
else
return false;
}
#endif
//---------------------------------------------------------------------------
bool TPinger::Ping(char *Host, int Timeout)
{
longword Addr;
Addr=inet_addr(Host);
return Ping(Addr, Timeout);
}
//---------------------------------------------------------------------------
bool TPinger::Ping(longword ip_addr, int Timeout)
{
#ifdef OS_WINDOWS
if (PingKind==pkWinHelper)
return WinPing(ip_addr, Timeout);
else
#endif
if (PingKind==pkRawSocket)
return RawPing(ip_addr, Timeout);
else
return true; // we still need to continue
}
//---------------------------------------------------------------------------
// Checks if raw sockets are allowed
//---------------------------------------------------------------------------
bool RawSocketsCheck()
{
socket_t RawSocket;
bool Result;
RawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
Result=RawSocket != INVALID_SOCKET;
if (Result)
#ifdef OS_WINDOWS
closesocket(RawSocket);
#else
close(RawSocket);
#endif
return Result;
}
//---------------------------------------------------------------------------
// Sockets init
// - Winsock Startup (Windows)
// - ICMP Helper Load (Windows)
// - Check for raw socket (Unix or Windows if ICMP load failed)
//---------------------------------------------------------------------------
SocketsLayer::SocketsLayer()
{
#ifdef OS_WINDOWS
timeBeginPeriod(1); // it's not strictly related to socket but here is a nice place
WSAStartup(0x202,&wsaData);
if (IcmpInit())
PingKind=pkWinHelper;
else
#endif
if (RawSocketsCheck())
PingKind=pkRawSocket;
else
PingKind=pkCannotPing;
}
SocketsLayer::~SocketsLayer()
{
#ifdef OS_WINDOWS
IcmpDone();
WSACleanup();
timeEndPeriod(1);
#endif
}

View File

@ -0,0 +1,339 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#ifndef snap_msgsock_h
#define snap_msgsock_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
//----------------------------------------------------------------------------
#if defined(OS_WINDOWS) || defined (OS_SOLARIS) || defined(OS_OSX)
# define MSG_NOSIGNAL 0
#endif
//----------------------------------------------------------------------------
// Non blocking connection to avoid root priviledges under UNIX
// i.e. raw socket pinger is not more used.
// Thanks to Rolf Stalder that made it ;)
//----------------------------------------------------------------------------
#ifdef PLATFORM_UNIX
#define NON_BLOCKING_CONNECT
#endif
#ifdef NON_BLOCKING_CONNECT
#include <fcntl.h>
#endif
//----------------------------------------------------------------------------
/*
In Windows sizeof socket varies depending of the platform :
win32 -> sizeof(SOCKET) = 4
win64 -> sizeof(SOCKET) = 8
Even though sizeof(SOCKET) is 8, should be safe to cast it to int, because
the value constitutes an index in per-process table of limited size
and not a real pointer.
Other Os define the socket as int regardless of the processor.
We want to sleep peacefully, so it's better to define a portable socket.
*/
#ifdef OS_WINDOWS
typedef SOCKET socket_t;
#else
typedef int socket_t;
#endif
//----------------------------------------------------------------------------
#define SD_RECEIVE 0x00
#define SD_SEND 0x01
#define SD_BOTH 0x02
#define MaxPacketSize 65536
//----------------------------------------------------------------------------
// For other platform we need to re-define next constants
#if defined(PLATFORM_UNIX) || defined(OS_OSX)
#define INVALID_SOCKET (socket_t)(~0)
#define SOCKET_ERROR (-1)
#define WSAEINTR EINTR
#define WSAEBADF EBADF
#define WSAEACCES EACCES
#define WSAEFAULT EFAULT
#define WSAEINVAL EINVAL
#define WSAEMFILE EMFILE
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEINPROGRESS EINPROGRESS
#define WSAEALREADY EALREADY
#define WSAENOTSOCK ENOTSOCK
#define WSAEDESTADDRREQ EDESTADDRREQ
#define WSAEMSGSIZE EMSGSIZE
#define WSAEPROTOTYPE EPROTOTYPE
#define WSAENOPROTOOPT ENOPROTOOPT
#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
#define WSAEOPNOTSUPP EOPNOTSUPP
#define WSAEPFNOSUPPORT EPFNOSUPPORT
#define WSAEAFNOSUPPORT EAFNOSUPPORT
#define WSAEADDRINUSE EADDRINUSE
#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
#define WSAENETDOWN ENETDOWN
#define WSAENETUNREACH ENETUNREACH
#define WSAENETRESET ENETRESET
#define WSAECONNABORTED ECONNABORTED
#define WSAECONNRESET ECONNRESET
#define WSAENOBUFS ENOBUFS
#define WSAEISCONN EISCONN
#define WSAENOTCONN ENOTCONN
#define WSAESHUTDOWN ESHUTDOWN
#define WSAETOOMANYREFS ETOOMANYREFS
#define WSAETIMEDOUT ETIMEDOUT
#define WSAECONNREFUSED ECONNREFUSED
#define WSAELOOP ELOOP
#define WSAENAMETOOLONG ENAMETOOLONG
#define WSAEHOSTDOWN EHOSTDOWN
#define WSAEHOSTUNREACH EHOSTUNREACH
#define WSAENOTEMPTY ENOTEMPTY
#define WSAEUSERS EUSERS
#define WSAEDQUOT EDQUOT
#define WSAESTALE ESTALE
#define WSAEREMOTE EREMOTE
#endif
#define WSAEINVALIDADDRESS 12001
#define ICmpBufferSize 4096
typedef byte TIcmpBuffer[ICmpBufferSize];
// Ping result
#define PR_CANNOT_PERFORM -1 // cannot ping :
// unix : no root rights or SUID flag set to
// open raw sockets
// windows : neither helper DLL found nor raw
// sockets can be opened (no administrator rights)
// In this case the execution continues whitout
// the benefit of the smart-connect.
#define PR_SUCCESS 0 // Host found
#define PR_ERROR 1 // Ping Error, Ping was performed but ...
// - host didn't replied (not found)
// - routing error
// - TTL expired
// - ... all other icmp error that we don't need
// to know.
// Ping Kind
#define pkCannotPing 1 // see PR_CANNOT_PERFORM comments
#define pkWinHelper 2 // use iphlpapi.dll (only windows)
#define pkRawSocket 3 // use raw sockets (unix/windows)
const byte ICMP_ECHORP = 0; // ECHO Reply
const byte ICMP_ECHORQ = 8; // ECHO Request
//---------------------------------------------------------------------------
// RAW SOCKET PING STRUCTS
//---------------------------------------------------------------------------
#pragma pack(1)
typedef struct{
byte ip_hl_v;
byte ip_tos;
word ip_len;
word ip_id ;
word ip_off;
byte ip_ttl;
byte ip_p;
word ip_sum;
longword ip_src;
longword ip_dst;
}TIPHeader;
typedef struct{
byte ic_type; // Type of message
byte ic_code; // Code
word ic_cksum; // 16 bit checksum
word ic_id; // ID (ic1 : ipv4)
word ic_seq; // Sequence
}TIcmpHeader;
typedef struct{
TIcmpHeader Header;
byte Data[32]; // use the well known default
}TIcmpPacket, *PIcmpPacket;
typedef struct{
TIPHeader IPH;
TIcmpPacket ICmpReply;
}TIcmpReply, *PIcmpReply;
#pragma pack()
//---------------------------------------------------------------------------
class TRawSocketPinger
{
private:
socket_t FSocket;
PIcmpPacket SendPacket;
TIcmpBuffer IcmpBuffer;
word FId, FSeq;
void InitPacket();
word PacketChecksum();
bool CanRead(int Timeout);
public:
bool Ping(longword ip_addr, int Timeout);
TRawSocketPinger();
~TRawSocketPinger();
};
typedef TRawSocketPinger *PRawSocketPinger;
//---------------------------------------------------------------------------
class TPinger
{
private:
PRawSocketPinger RawPinger;
bool RawAvail;
#ifdef OS_WINDOWS
bool WinPing(longword ip_addr, int Timeout);
#endif
bool RawPing(longword ip_addr, int Timeout);
public:
TPinger();
~TPinger();
bool Ping(char *Host, int Timeout);
bool Ping(longword ip_addr, int Timeout);
};
typedef TPinger *PPinger;
//---------------------------------------------------------------------------
class TSnapBase // base class endian-aware
{
private:
bool LittleEndian;
protected:
longword SwapDWord(longword Value);
word SwapWord(word Value);
public:
TSnapBase();
};
//---------------------------------------------------------------------------
class TMsgSocket : public TSnapBase
{
private:
PPinger Pinger;
int GetLastSocketError();
int SockCheck(int SockResult);
void DestroySocket();
void SetSocketOptions();
bool CanWrite(int Timeout);
void GetLocal();
void GetRemote();
void SetSin(sockaddr_in &sin, char *Address, u_short Port);
void GetSin(sockaddr_in sin, char *Address, u_short &Port);
protected:
socket_t FSocket;
sockaddr_in LocalSin;
sockaddr_in RemoteSin;
//--------------------------------------------------------------------------
// low level socket
void CreateSocket();
// Called when a socket is assigned externally
void GotSocket();
// Returns how many bytes are ready to be read in the winsock buffer
int WaitingData();
// Waits until there at least "size" bytes ready to be read or until receive timeout occurs
int WaitForData(int Size, int Timeout);
// Clear socket input buffer
void Purge();
public:
longword ClientHandle;
longword LocalBind;
// Coordinates Address:Port
char LocalAddress[16];
char RemoteAddress[16];
word LocalPort;
word RemotePort;
// "speed" of the socket listener (used server-side)
int WorkInterval;
// Timeouts : 3 different values for fine tuning.
// Send timeout should be small since with work with small packets and TCP_NO_DELAY
// option, so we don't expect "time to wait".
// Recv timeout depends of equipment's processing time : we send a packet, the equipment
// processes the message, finally it sends the answer. In any case Recv timeout > Send Timeout.
// PingTimeout is the maximum time interval during which we expect that the PLC answers.
// By default is 750 ms, increase it if there are many switch/repeaters.
int PingTimeout;
int RecvTimeout;
int SendTimeout;
//int ConnTimeout;
// Output : Last operation error
int LastTcpError;
// Output : Connected to the remote Host/Peer/Client
bool Connected;
//--------------------------------------------------------------------------
TMsgSocket();
virtual ~TMsgSocket();
// Returns true if "something" can be read during the Timeout interval..
bool CanRead(int Timeout);
// Connects to a peer (using RemoteAddress and RemotePort)
int SckConnect(); // (client-side)
// Disconnects from a peer (gracefully)
void SckDisconnect();
// Disconnects RAW
void ForceClose();
// Binds to a local adapter (using LocalAddress and LocalPort) (server-side)
int SckBind();
// Listens for an incoming connection (server-side)
int SckListen();
// Set an external socket reference (tipically from a listener)
void SetSocket(socket_t s);
// Accepts an incoming connection returning a socket descriptor (server-side)
socket_t SckAccept();
// Pings the peer before connecting
bool Ping(char *Host);
bool Ping(sockaddr_in Addr);
// Sends a packet
int SendPacket(void *Data, int Size);
// Returns true if a Packet at least of "Size" bytes is ready to be read
bool PacketReady(int Size);
// Receives everything
int Receive(void *Data, int BufSize, int & SizeRecvd);
// Receives a packet of size specified.
int RecvPacket(void *Data, int Size);
// Peeks a packet of size specified without extract it from the socket queue
int PeekPacket(void *Data, int Size);
virtual bool Execute();
};
typedef TMsgSocket *PMsgSocket;
//---------------------------------------------------------------------------
void Msg_CloseSocket(socket_t FSocket);
longword Msg_GetSockAddr(socket_t FSocket);
//---------------------------------------------------------------------------
class SocketsLayer
{
private:
#ifdef OS_WINDOWS
WSADATA wsaData;
#endif
public:
SocketsLayer();
~SocketsLayer();
};
#endif // snap_msgsock_h

View File

@ -0,0 +1,152 @@
/*=============================================================================|
| PROJECT SNAP7 1.4.1 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#ifndef snap_platform_h
#define snap_platform_h
//---------------------------------------------------------------------------
#if defined (_WIN32)|| defined(_WIN64)|| defined(__WIN32__) || defined(__WINDOWS__)
# define OS_WINDOWS
#endif
// Visual Studio needs this to use the correct time_t size
#if defined (_WIN32) && !defined(_WIN64) && !defined(_EMBEDDING_VS2013UP)
# define _USE_32BIT_TIME_T
#endif
// Linux, BSD and Solaris define "unix", OSX doesn't, even though it derives from BSD
#if defined(unix) || defined(__unix__) || defined(__unix)
# define PLATFORM_UNIX
#endif
#if BSD>=0
# define OS_BSD
#endif
#if __APPLE__
# define OS_OSX
#endif
#if defined(__SVR4) || defined(__svr4__)
# define OS_SOLARIS
// Thanks to Rolf Stalder now it's possible to use pthreads also for Solaris
// In any case the Solaris native threads model is still present and can be
// used uncommenting the #define line below.
# undef OS_SOLARIS_NATIVE_THREADS
// # define OS_SOLARIS_NATIVE_THREADS
#endif
#if defined(PLATFORM_UNIX)
# include <unistd.h>
# include <sys/param.h>
# if defined(_POSIX_VERSION)
# define POSIX
# endif
#endif
#ifdef OS_OSX
# include <unistd.h>
#endif
#if (!defined (OS_WINDOWS)) && (!defined(PLATFORM_UNIX)) && (!defined(OS_BSD)) && (!defined(OS_OSX))
# error platform still unsupported (please add it yourself and report ;-)
#endif
// Visual C++ not C99 compliant (VS2008--)
#ifdef _MSC_VER
# if _MSC_VER >= 1600
# include <stdint.h> // VS2010++ have it
# else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef signed __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef unsigned __int32 uintptr_t;
#endif
# endif
#else
# include <stdint.h>
#endif
#include <time.h>
#include <cstring>
#include <stdlib.h>
#ifdef OS_WINDOWS
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winsock2.h>
# include <mmsystem.h>
#endif
#ifdef OS_SOLARIS
# include <sys/filio.h>
# include <cstdlib>
# include <string.h>
#endif
#if defined(PLATFORM_UNIX) || defined(OS_OSX)
# include <errno.h>
# include <sys/time.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netinet/tcp.h>
# include <netinet/in.h>
# include <sys/ioctl.h>
#endif
#ifdef OS_WINDOWS
# define EXPORTSPEC extern "C" __declspec ( dllexport )
# define S7API __stdcall
#else
# define EXPORTSPEC extern "C"
# define S7API
#endif
// Exact length types regardless of platform/processor
// We absolute need of them, all structs have an exact size that
// must be the same across the processor used 32/64 bit
// *Use them* if you change/expand the code and avoid long, u_long and so on...
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t longword;
typedef byte *pbyte;
typedef word *pword;
typedef uintptr_t snap_obj; // multi platform/processor object reference
#ifndef OS_WINDOWS
# define INFINITE 0XFFFFFFFF
#endif
#endif // snap_platform_h

View File

@ -0,0 +1,73 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#include "snap_sysutils.h"
#ifdef OS_OSX
int clock_gettime(int clk_id, struct timespec* t)
{
struct timeval now;
int rv = gettimeofday(&now, NULL);
if (rv) return rv;
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
return 0;
}
#endif
//---------------------------------------------------------------------------
longword SysGetTick()
{
#ifdef OS_WINDOWS
return timeGetTime();
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (longword) (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
#endif
}
//---------------------------------------------------------------------------
void SysSleep(longword Delay_ms)
{
#ifdef OS_WINDOWS
Sleep(Delay_ms);
#else
struct timespec ts;
ts.tv_sec = (time_t)(Delay_ms / 1000);
ts.tv_nsec =(long)((Delay_ms - ts.tv_sec) * 1000000);
nanosleep(&ts, (struct timespec *)0);
#endif
}
//---------------------------------------------------------------------------
longword DeltaTime(longword &Elapsed)
{
longword TheTime;
TheTime=SysGetTick();
// Checks for rollover
if (TheTime<Elapsed)
Elapsed=0;
return TheTime-Elapsed;
}

View File

@ -0,0 +1,39 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#ifndef snap_sysutils_h
#define snap_sysutils_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
//---------------------------------------------------------------------------
#ifdef OS_OSX
# define CLOCK_MONOTONIC 0
#endif
longword SysGetTick();
void SysSleep(longword Delay_ms);
longword DeltaTime(longword &Elapsed);
#endif // snap_sysutils_h

View File

@ -0,0 +1,487 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#include "snap_tcpsrvr.h"
//---------------------------------------------------------------------------
// EVENTS QUEUE
//---------------------------------------------------------------------------
TMsgEventQueue::TMsgEventQueue(const int Capacity, const int BlockSize)
{
FCapacity = Capacity;
Max = FCapacity - 1;
FBlockSize = BlockSize;
Buffer = new byte[FCapacity * FBlockSize];
Flush();
}
//---------------------------------------------------------------------------
TMsgEventQueue::~TMsgEventQueue()
{
delete[] Buffer;
}
//---------------------------------------------------------------------------
void TMsgEventQueue::Flush()
{
IndexIn = 0;
IndexOut = 0;
}
//---------------------------------------------------------------------------
void TMsgEventQueue::Insert(void *lpdata)
{
pbyte PBlock;
if (!Full())
{
// Calc offset
if (IndexIn < Max) IndexIn++;
else IndexIn = 0;
PBlock = Buffer + uintptr_t(IndexIn * FBlockSize);
memcpy(PBlock, lpdata, FBlockSize);
};
}
//---------------------------------------------------------------------------
bool TMsgEventQueue::Extract(void *lpdata)
{
int IdxOut;
pbyte PBlock;
if (!Empty())
{
// stores IndexOut
IdxOut = IndexOut;
if (IdxOut < Max) IdxOut++;
else IdxOut = 0;
PBlock = Buffer + uintptr_t(IdxOut * FBlockSize);
// moves data
memcpy(lpdata, PBlock, FBlockSize);
// Updates IndexOut
IndexOut = IdxOut;
return true;
}
else
return false;
}
//---------------------------------------------------------------------------
bool TMsgEventQueue::Empty()
{
return (IndexIn == IndexOut);
}
//---------------------------------------------------------------------------
bool TMsgEventQueue::Full()
{
int IdxOut = IndexOut; // To avoid troubles if IndexOut changes during next line
return ( (IdxOut == IndexIn + 1) || ((IndexIn == Max) && (IdxOut == 0)));
}
//---------------------------------------------------------------------------
// WORKER THREAD
//---------------------------------------------------------------------------
TMsgWorkerThread::TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server)
{
FreeOnTerminate = true;
WorkerSocket = Socket;
FServer = Server;
}
//---------------------------------------------------------------------------
void TMsgWorkerThread::Execute()
{
bool Exception = false;
bool SelfClose = false;
// Working loop
while (!Terminated && !SelfClose && !Exception && !FServer->Destroying)
{
try
{
if (!WorkerSocket->Execute()) // False -> End of Activities
SelfClose = true;
} catch (...)
{
Exception = true;
}
};
if (!FServer->Destroying)
{
// Exception detected during Worker activity
if (Exception)
{
WorkerSocket->ForceClose();
FServer->DoEvent(WorkerSocket->ClientHandle, evcClientException, 0, 0, 0, 0, 0);
}
else
if (SelfClose)
{
FServer->DoEvent(WorkerSocket->ClientHandle, evcClientDisconnected, 0, 0, 0, 0, 0);
}
else
FServer->DoEvent(WorkerSocket->ClientHandle, evcClientTerminated, 0, 0, 0, 0, 0);
}
delete WorkerSocket;
// Delete reference from list
FServer->Delete(Index);
}
//---------------------------------------------------------------------------
// LISTENER THREAD
//---------------------------------------------------------------------------
TMsgListenerThread::TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server)
{
FServer = Server;
FListener = Listener;
FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
void TMsgListenerThread::Execute()
{
socket_t Sock;
bool Valid;
while (!Terminated)
{
if (FListener->CanRead(FListener->WorkInterval))
{
Sock = FListener->SckAccept(); // in any case we must accept
Valid = Sock != INVALID_SOCKET;
// check if we are not destroying
if ((!Terminated) && (!FServer->Destroying))
{
if (Valid)
FServer->Incoming(Sock);
}
else
if (Valid)
Msg_CloseSocket(Sock);
};
}
}
//---------------------------------------------------------------------------
// TCP SERVER
//---------------------------------------------------------------------------
TCustomMsgServer::TCustomMsgServer()
{
strcpy(FLocalAddress, "0.0.0.0");
CSList = new TSnapCriticalSection();
CSEvent = new TSnapCriticalSection();
FEventQueue = new TMsgEventQueue(MaxEvents, sizeof (TSrvEvent));
memset(Workers, 0, sizeof (Workers));
for (int i = 0; i < MaxWorkers; i++)
Workers[i] = NULL;
Status = SrvStopped;
EventMask = 0xFFFFFFFF;
LogMask = 0xFFFFFFFF;
Destroying = false;
FLastError = 0;
ClientsCount = 0;
LocalBind = 0;
MaxClients = MaxWorkers;
OnEvent = NULL;
}
//---------------------------------------------------------------------------
TCustomMsgServer::~TCustomMsgServer()
{
Destroying = true;
Stop();
OnEvent = NULL;
delete CSList;
delete CSEvent;
delete FEventQueue;
}
//---------------------------------------------------------------------------
void TCustomMsgServer::LockList()
{
CSList->Enter();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::UnlockList()
{
CSList->Leave();
}
//---------------------------------------------------------------------------
int TCustomMsgServer::FirstFree()
{
int i;
for (i = 0; i < MaxWorkers; i++)
{
if (Workers[i] == 0)
return i;
}
return -1;
}
//---------------------------------------------------------------------------
int TCustomMsgServer::StartListener()
{
int Result;
// Creates the listener
SockListener = new TMsgSocket();
strncpy(SockListener->LocalAddress, FLocalAddress, 16);
SockListener->LocalPort = LocalPort;
// Binds
Result = SockListener->SckBind();
if (Result == 0)
{
LocalBind = SockListener->LocalBind;
// Listen
Result = SockListener->SckListen();
if (Result == 0)
{
// Creates the Listener thread
ServerThread = new TMsgListenerThread(SockListener, this);
ServerThread->Start();
}
else
delete SockListener;
}
else
delete SockListener;
return Result;
}
//---------------------------------------------------------------------------
void TCustomMsgServer::TerminateAll()
{
int c;
longword Elapsed;
bool Timeout;
if (ClientsCount > 0)
{
for (c = 0; c < MaxWorkers; c++)
{
if (Workers[c] != 0)
PMsgWorkerThread(Workers[c])->Terminate();
}
// Wait for closing
Elapsed = SysGetTick();
Timeout = false;
while (!Timeout && (ClientsCount > 0))
{
Timeout = DeltaTime(Elapsed) > WkTimeout;
if (!Timeout)
SysSleep(100);
};
if (ClientsCount > 0)
KillAll(); // one o more threads are hanged
ClientsCount = 0;
}
}
//---------------------------------------------------------------------------
void TCustomMsgServer::KillAll()
{
int c, cnt = 0;
LockList();
for (c = 0; c < MaxWorkers; c++)
{
if (Workers[c] != 0)
try
{
PMsgWorkerThread(Workers[c])->Kill();
PMsgWorkerThread(Workers[c])->WorkerSocket->ForceClose();
delete PMsgWorkerThread(Workers[c]);
Workers[c] = 0;
cnt++;
} catch (...)
{
};
}
UnlockList();
DoEvent(0, evcClientsDropped, 0, cnt, 0, 0, 0);
}
//---------------------------------------------------------------------------
bool TCustomMsgServer::CanAccept(socket_t Socket)
{
return ((MaxClients == 0) || (ClientsCount < MaxClients));
}
//---------------------------------------------------------------------------
PWorkerSocket TCustomMsgServer::CreateWorkerSocket(socket_t Sock)
{
PWorkerSocket Result;
// Creates a funny default class : a tcp echo worker
Result = new TEcoTcpWorker();
Result->SetSocket(Sock);
return Result;
}
//---------------------------------------------------------------------------
void TCustomMsgServer::DoEvent(int Sender, longword Code, word RetCode, word Param1, word Param2, word Param3, word Param4)
{
TSrvEvent SrvEvent;
bool GoLog = (Code & LogMask) != 0;
bool GoEvent = (Code & EventMask) != 0;
if (!Destroying && (GoLog || GoEvent))
{
CSEvent->Enter();
time(&SrvEvent.EvtTime);
SrvEvent.EvtSender = Sender;
SrvEvent.EvtCode = Code;
SrvEvent.EvtRetCode = RetCode;
SrvEvent.EvtParam1 = Param1;
SrvEvent.EvtParam2 = Param2;
SrvEvent.EvtParam3 = Param3;
SrvEvent.EvtParam4 = Param4;
if (GoEvent && (OnEvent != NULL))
try
{ // callback is outside here, we have to shield it
OnEvent(FUsrPtr, &SrvEvent, sizeof (TSrvEvent));
} catch (...)
{
};
if (GoLog)
FEventQueue->Insert(&SrvEvent);
CSEvent->Leave();
};
}
//---------------------------------------------------------------------------
void TCustomMsgServer::Delete(int Index)
{
LockList();
Workers[Index] = 0;
ClientsCount--;
UnlockList();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::Incoming(socket_t Sock)
{
int idx;
PWorkerSocket WorkerSocket;
longword ClientHandle = Msg_GetSockAddr(Sock);
if (CanAccept(Sock))
{
LockList();
// First position available in the thread buffer
idx = FirstFree();
if (idx >= 0)
{
// Creates the Worker and assigns it the connected socket
WorkerSocket = CreateWorkerSocket(Sock);
// Creates the Worker thread
Workers[idx] = new TMsgWorkerThread(WorkerSocket, this);
PMsgWorkerThread(Workers[idx])->Index = idx;
// Update the number
ClientsCount++;
// And Starts the worker
PMsgWorkerThread(Workers[idx])->Start();
DoEvent(WorkerSocket->ClientHandle, evcClientAdded, 0, 0, 0, 0, 0);
}
else
{
DoEvent(ClientHandle, evcClientNoRoom, 0, 0, 0, 0, 0);
Msg_CloseSocket(Sock);
}
UnlockList();
}
else
{
Msg_CloseSocket(Sock);
DoEvent(ClientHandle, evcClientRejected, 0, 0, 0, 0, 0);
};
}
//---------------------------------------------------------------------------
int TCustomMsgServer::Start()
{
int Result = 0;
if (Status != SrvRunning)
{
Result = StartListener();
if (Result != 0)
{
DoEvent(0, evcListenerCannotStart, Result, 0, 0, 0, 0);
Status = SrvError;
}
else
{
DoEvent(0, evcServerStarted, SockListener->ClientHandle, LocalPort, 0, 0, 0);
Status = SrvRunning;
};
};
FLastError = Result;
return Result;
}
//---------------------------------------------------------------------------
int TCustomMsgServer::StartTo(const char *Address, word Port)
{
strncpy(FLocalAddress, Address, 16);
LocalPort = Port;
return Start();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::Stop()
{
if (Status == SrvRunning)
{
// Kills the listener thread
ServerThread->Terminate();
if (ServerThread->WaitFor(ThTimeout) != WAIT_OBJECT_0)
ServerThread->Kill();
delete ServerThread;
// Kills the listener
delete SockListener;
// Terminate all client threads
TerminateAll();
Status = SrvStopped;
LocalBind = 0;
DoEvent(0, evcServerStopped, 0, 0, 0, 0, 0);
};
FLastError = 0;
}
//---------------------------------------------------------------------------
int TCustomMsgServer::SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr)
{
OnEvent = PCallBack;
FUsrPtr = UsrPtr;
return 0;
}
//---------------------------------------------------------------------------
bool TCustomMsgServer::PickEvent(void *pEvent)
{
try
{
return FEventQueue->Extract(pEvent);
} catch (...)
{
return false;
};
}
//---------------------------------------------------------------------------
bool TCustomMsgServer::EventEmpty()
{
return FEventQueue->Empty();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::EventsFlush()
{
CSEvent->Enter();
FEventQueue->Flush();
CSEvent->Leave();
}
//---------------------------------------------------------------------------

View File

@ -0,0 +1,247 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#ifndef snap_tcpsrvr_h
#define snap_tcpsrvr_h
//---------------------------------------------------------------------------
#include "snap_msgsock.h"
#include "snap_threads.h"
//---------------------------------------------------------------------------
#define MaxWorkers 1024
#define MaxEvents 1500
const int SrvStopped = 0;
const int SrvRunning = 1;
const int SrvError = 2;
const longword evcServerStarted = 0x00000001;
const longword evcServerStopped = 0x00000002;
const longword evcListenerCannotStart = 0x00000004;
const longword evcClientAdded = 0x00000008;
const longword evcClientRejected = 0x00000010;
const longword evcClientNoRoom = 0x00000020;
const longword evcClientException = 0x00000040;
const longword evcClientDisconnected = 0x00000080;
const longword evcClientTerminated = 0x00000100;
const longword evcClientsDropped = 0x00000200;
const longword evcReserved_00000400 = 0x00000400;
const longword evcReserved_00000800 = 0x00000800;
const longword evcReserved_00001000 = 0x00001000;
const longword evcReserved_00002000 = 0x00002000;
const longword evcReserved_00004000 = 0x00004000;
const longword evcReserved_00008000 = 0x00008000;
// Server Interface errors
const longword errSrvBase = 0x0000FFFF;
const longword errSrvMask = 0xFFFF0000;
const longword errSrvCannotStart = 0x00100000;
const longword ThTimeout = 2000; // Thread timeout
const longword WkTimeout = 3000; // Workers termination timeout
#pragma pack(1)
typedef struct{
time_t EvtTime; // Timestamp
int EvtSender; // Sender
longword EvtCode; // Event code
word EvtRetCode; // Event result
word EvtParam1; // Param 1 (if available)
word EvtParam2; // Param 2 (if available)
word EvtParam3; // Param 3 (if available)
word EvtParam4; // Param 4 (if available)
}TSrvEvent, *PSrvEvent;
extern "C"
{
typedef void (S7API *pfn_SrvCallBack)(void * usrPtr, PSrvEvent PEvent, int Size);
}
#pragma pack()
//---------------------------------------------------------------------------
// EVENTS QUEUE
//---------------------------------------------------------------------------
class TMsgEventQueue
{
private:
int IndexIn; // <-- insert index
int IndexOut; // --> extract index
int Max; // Buffer upper bound [0..Max]
int FCapacity; // Queue capacity
pbyte Buffer;
int FBlockSize;
public:
TMsgEventQueue(const int Capacity, const int BlockSize);
~TMsgEventQueue();
void Flush();
void Insert(void *lpdata);
bool Extract(void *lpdata);
bool Empty();
bool Full();
};
typedef TMsgEventQueue *PMsgEventQueue;
//---------------------------------------------------------------------------
// WORKER THREAD
//---------------------------------------------------------------------------
class TCustomMsgServer; // forward declaration
// It's created when connection is accepted, it will interface with the client.
class TMsgWorkerThread : public TSnapThread
{
private:
TCustomMsgServer *FServer;
protected:
TMsgSocket *WorkerSocket;
public:
int Index;
friend class TCustomMsgServer;
TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server);
void Execute();
};
typedef TMsgWorkerThread *PMsgWorkerThread;
//---------------------------------------------------------------------------
// LISTENER THREAD
//---------------------------------------------------------------------------
// It listens for incoming connection.
class TMsgListenerThread : public TSnapThread
{
private:
TMsgSocket *FListener;
TCustomMsgServer *FServer;
public:
TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server);
void Execute();
};
typedef TMsgListenerThread *PMsgListenerThread;
//---------------------------------------------------------------------------
// TCP SERVER
//---------------------------------------------------------------------------
typedef TMsgSocket *PWorkerSocket;
class TCustomMsgServer
{
private:
int FLastError;
char FLocalAddress[16];
// Socket listener
PMsgSocket SockListener;
// Server listener
PMsgListenerThread ServerThread;
// Critical section to lock Workers list activities
PSnapCriticalSection CSList;
// Event queue
PMsgEventQueue FEventQueue;
// Callback related
pfn_SrvCallBack OnEvent;
void *FUsrPtr;
// private methods
int StartListener();
void LockList();
void UnlockList();
int FirstFree();
protected:
bool Destroying;
// Critical section to lock Event activities
PSnapCriticalSection CSEvent;
// Workers list
void *Workers[MaxWorkers];
// Terminates all worker threads
virtual void TerminateAll();
// Kills all worker threads that are unresponsive
void KillAll();
// if (true the connection is accepted, otherwise the connection
// is closed gracefully
virtual bool CanAccept(socket_t Socket);
// Returns the class of the worker socket, override it for real servers
virtual PWorkerSocket CreateWorkerSocket(socket_t Sock);
// Handles the event
virtual void DoEvent(int Sender, longword Code, word RetCode, word Param1,
word Param2, word Param3, word Param4);
// Delete the worker from the list (It's invoked by Worker Thread)
void Delete(int Index);
// Incoming connection (It's invoked by ServerThread, the listener)
virtual void Incoming(socket_t Sock);
public:
friend class TMsgWorkerThread;
friend class TMsgListenerThread;
word LocalPort;
longword LocalBind;
longword LogMask;
longword EventMask;
int Status;
int ClientsCount;
int MaxClients;
TCustomMsgServer();
virtual ~TCustomMsgServer();
// Starts the server
int Start();
int StartTo(const char *Address, word Port);
// Stops the server
void Stop();
// Sets Event callback
int SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr);
// Pick an event from the circular queue
bool PickEvent(void *pEvent);
// Returns true if (the Event queue is empty
bool EventEmpty();
// Flushes Event queue
void EventsFlush();
};
//---------------------------------------------------------------------------
// TCP WORKER
//---------------------------------------------------------------------------
// Default worker class, a simply tcp echo to test the connection and
// data I/O to use the server outside the project
class TEcoTcpWorker : public TMsgSocket
{
public:
bool Execute()
{
byte Buffer[4096];
int Size;
if (CanRead(WorkInterval)) // Small time to avoid time wait during the close
{
Receive(&Buffer,sizeof(Buffer),Size);
if ((LastTcpError==0) && (Size>0))
{
SendPacket(&Buffer,Size);
return LastTcpError==0;
}
else
return false;
}
else
return true;
};
};
//---------------------------------------------------------------------------
#endif // snap_tcpsrvr_h

View File

@ -0,0 +1,162 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#include "snap_threads.h"
//---------------------------------------------------------------------------
#ifdef OS_WINDOWS
DWORD WINAPI ThreadProc(LPVOID param)
#else
void* ThreadProc(void* param)
#endif
{
PSnapThread Thread;
// Unix but not Solaris
#if (defined(POSIX) || defined(OS_OSX)) && (!defined(OS_SOLARIS_NATIVE_THREADS))
int last_type, last_state;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &last_type);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &last_state);
#endif
Thread = PSnapThread(param);
if (!Thread->Terminated)
try
{
Thread->Execute();
} catch (...)
{
};
Thread->Closed = true;
if (Thread->FreeOnTerminate)
{
delete Thread;
};
#ifdef OS_WINDOWS
ExitThread(0);
#endif
#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
pthread_exit((void*)0);
#endif
#if defined(OS_OSX)
pthread_exit((void*)0);
#endif
#ifdef OS_SOLARIS_NATIVE_THREADS
thr_exit((void*)0);
#endif
return 0; // never reach, only to avoid compiler warning
}
//---------------------------------------------------------------------------
TSnapThread::TSnapThread()
{
Started = false;
Closed=false;
Terminated = false;
FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
TSnapThread::~TSnapThread()
{
if (Started && !Closed)
{
Terminate();
Join();
};
#ifdef OS_WINDOWS
if (Started)
CloseHandle(th);
#endif
}
//---------------------------------------------------------------------------
void TSnapThread::ThreadCreate()
{
#ifdef OS_WINDOWS
th = CreateThread(0, 0, ThreadProc, this, 0, 0);
#endif
#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
pthread_attr_t a;
pthread_attr_init(&a);
pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
pthread_create(&th, &a, &ThreadProc, this);
#endif
#if defined(OS_OSX)
pthread_create(&th, 0, &ThreadProc, this);
#endif
#ifdef OS_SOLARIS_NATIVE_THREADS
thr_create(0, // default stack base
0, // default stack size
&ThreadProc, // Thread routine
this, // argument
0,
&th);
#endif
}
//---------------------------------------------------------------------------
void TSnapThread::Start()
{
if (!Started)
{
ThreadCreate();
Started = true;
}
}
//---------------------------------------------------------------------------
void TSnapThread::Terminate()
{
Terminated = true;
}
//---------------------------------------------------------------------------
void TSnapThread::Kill()
{
if (Started && !Closed)
{
ThreadKill();
Closed = true;
}
}
//---------------------------------------------------------------------------
void TSnapThread::Join()
{
if (Started && !Closed)
{
ThreadJoin();
Closed = true;
}
}
//---------------------------------------------------------------------------
longword TSnapThread::WaitFor(uint64_t Timeout)
{
if (Started)
{
if (!Closed)
return ThreadWait(Timeout);
else
return WAIT_OBJECT_0;
}
else
return WAIT_OBJECT_0;
}
//---------------------------------------------------------------------------

View File

@ -0,0 +1,45 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#ifndef snap_threads_h
#define snap_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#ifdef OS_WINDOWS
# include "win_threads.h"
#endif
#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
# include "unix_threads.h"
#endif
#ifdef OS_SOLARIS_NATIVE_THREADS
# include "sol_threads.h"
#endif
#if defined(OS_OSX)
# include "unix_threads.h"
#endif
//---------------------------------------------------------------------------
#endif // snap_threads_h

View File

@ -0,0 +1,208 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|==============================================================================|
| |
| Solaris 11 Threads support |
| |
|=============================================================================*/
#ifndef sol_threads_h
#define sol_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
#include <thread.h>
#include <synch.h>
//---------------------------------------------------------------------------
class TSnapCriticalSection {
private:
mutex_t mx;
int result;
public:
TSnapCriticalSection() {
mutex_init(&mx, USYNC_THREAD, 0);
};
~TSnapCriticalSection() {
mutex_destroy(&mx);
};
void Enter() {
mutex_lock(&mx);
};
void Leave() {
mutex_unlock(&mx);
};
bool TryEnter() {
return mutex_trylock(&mx) == 0;
};
};
typedef TSnapCriticalSection *PSnapCriticalSection;
//---------------------------------------------------------------------------
const longword WAIT_OBJECT_0 = 0x00000000L;
const longword WAIT_ABANDONED = 0x00000080L;
const longword WAIT_TIMEOUT = 0x00000102L;
const longword WAIT_FAILED = 0xFFFFFFFFL;
class TSnapEvent {
private:
cond_t CVariable;
mutex_t Mutex;
bool AutoReset;
bool State;
public:
TSnapEvent(bool ManualReset)
{
AutoReset = !ManualReset;
cond_init(&CVariable, USYNC_THREAD, 0) == 0;
mutex_init(&Mutex, USYNC_THREAD, 0);
State = false;
}
~TSnapEvent()
{
cond_destroy(&CVariable);
mutex_destroy(&Mutex);
};
void Set()
{
mutex_lock(&Mutex);
State = true;
if (AutoReset)
cond_signal(&CVariable);
else
cond_broadcast(&CVariable);
mutex_unlock(&Mutex);
};
void Reset()
{
mutex_lock(&Mutex);
State = false;
mutex_unlock(&Mutex);
}
longword WaitForever()
{
mutex_lock(&Mutex);
while (!State) // <-- to avoid spurious wakeups
cond_wait(&CVariable, &Mutex);
if (AutoReset)
State = false;
mutex_unlock(&Mutex);
return WAIT_OBJECT_0;
};
longword WaitFor(int64_t Timeout)
{
longword Result = WAIT_OBJECT_0;
if (Timeout == 0)
Timeout = 1; // 0 is not allowed
if (Timeout > 0) {
mutex_lock(&Mutex);
if (!State) {
timespec ts;
timeval tv;
gettimeofday(&tv, NULL);
uint64_t nsecs = ((uint64_t) tv.tv_sec) * 1000000000 +
Timeout * 1000000 +
((uint64_t) tv.tv_usec) * 1000;
ts.tv_sec = nsecs / 1000000000;
ts.tv_nsec = (nsecs - ((uint64_t) ts.tv_sec) * 1000000000);
do
{
Result = cond_timedwait(&CVariable, &Mutex, &ts);
if (Result == ETIMEDOUT)
Result = WAIT_TIMEOUT;
}
while (Result == 0 && !State);
}
else
if (AutoReset) // take the ownership
State = false;
mutex_unlock(&Mutex);
return Result;
}
else // Timeout<0
return WaitForever();
};
};
typedef TSnapEvent *PSnapEvent;
//---------------------------------------------------------------------------
class TSnapThread {
private:
thread_t th;
bool FCreateSuspended;
void ThreadCreate();
void ThreadJoin()
{
thr_join(th, 0, 0);
};
void ThreadKill()
{
thr_kill(th, 0);
};
longword ThreadWait(uint64_t Timeout)
{
longword Elapsed = SysGetTick();
while (!Closed && !(DeltaTime(Elapsed) > Timeout))
SysSleep(100);
if (Closed)
return WAIT_OBJECT_0;
else
return WAIT_TIMEOUT;
};
protected:
bool Started;
public:
bool Terminated;
bool Closed;
bool FreeOnTerminate;
TSnapThread();
virtual ~TSnapThread();
virtual void Execute() {
};
void Start();
void Terminate();
void Kill();
void Join();
longword WaitFor(uint64_t Timeout);
};
typedef TSnapThread *PSnapThread;
//---------------------------------------------------------------------------
#endif // sol_threads_h

View File

@ -0,0 +1,228 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|==============================================================================|
| |
| Posix Threads support (Linux, FreeBSD) |
| |
|=============================================================================*/
#ifndef unix_threads_h
#define unix_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
#include <semaphore.h>
#include <pthread.h>
//---------------------------------------------------------------------------
class TSnapCriticalSection
{
private:
pthread_mutex_t mx;
// int result;
public:
TSnapCriticalSection()
{
/*
This would be the best code, but very often it causes a segmentation fault in many
unix systems (the problem seems to be pthread_mutexattr_destroy()).
So, to avoid problems in future kernel/libc release, we use the "safe" default.
pthread_mutexattr_t mxAttr;
pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mx, &mxAttr);
pthread_mutexattr_destroy(&mxAttr);
*/
pthread_mutex_init(&mx, 0);
};
~TSnapCriticalSection()
{
pthread_mutex_destroy(&mx);
};
void Enter()
{
pthread_mutex_lock(&mx);
};
void Leave()
{
pthread_mutex_unlock(&mx);
};
bool TryEnter()
{
return pthread_mutex_trylock(&mx) == 0;
};
};
typedef TSnapCriticalSection *PSnapCriticalSection;
//---------------------------------------------------------------------------
const longword WAIT_OBJECT_0 = 0x00000000L;
const longword WAIT_ABANDONED = 0x00000080L;
const longword WAIT_TIMEOUT = 0x00000102L;
const longword WAIT_FAILED = 0xFFFFFFFFL;
class TSnapEvent
{
private:
pthread_cond_t CVariable;
pthread_mutex_t Mutex;
bool AutoReset;
bool State;
public:
TSnapEvent(bool ManualReset)
{
AutoReset = !ManualReset;
if (pthread_cond_init(&CVariable, 0) == 0)
pthread_mutex_init(&Mutex, 0);
State = false;
}
~TSnapEvent()
{
pthread_cond_destroy(&CVariable);
pthread_mutex_destroy(&Mutex);
};
void Set()
{
pthread_mutex_lock(&Mutex);
State = true;
if (AutoReset)
pthread_cond_signal(&CVariable);
else
pthread_cond_broadcast(&CVariable);
pthread_mutex_unlock(&Mutex);
};
void Reset()
{
pthread_mutex_lock(&Mutex);
State = false;
pthread_mutex_unlock(&Mutex);
}
longword WaitForever()
{
pthread_mutex_lock(&Mutex);
while (!State) // <-- to avoid spurious wakeups
pthread_cond_wait(&CVariable, &Mutex);
if (AutoReset)
State = false;
pthread_mutex_unlock(&Mutex);
return WAIT_OBJECT_0;
};
longword WaitFor(int64_t Timeout)
{
longword Result = WAIT_OBJECT_0;
if (Timeout == 0)
Timeout = 1; // 0 is not allowed
if (Timeout > 0)
{
pthread_mutex_lock(&Mutex);
if (!State)
{
timespec ts;
timeval tv;
gettimeofday(&tv, NULL);
uint64_t nsecs = ((uint64_t) tv.tv_sec) * 1000000000 +
Timeout * 1000000 +
((uint64_t) tv.tv_usec) * 1000;
ts.tv_sec = nsecs / 1000000000;
ts.tv_nsec = (nsecs - ((uint64_t) ts.tv_sec) * 1000000000);
do {
Result = pthread_cond_timedwait(&CVariable, &Mutex, &ts);
if (Result == ETIMEDOUT)
Result = WAIT_TIMEOUT;
} while (Result == 0 && !State);
}
else
if (AutoReset) // take the ownership
State = false;
pthread_mutex_unlock(&Mutex);
return Result;
}
else // Timeout<0
return WaitForever();
};
};
typedef TSnapEvent *PSnapEvent;
//---------------------------------------------------------------------------
class TSnapThread
{
private:
pthread_t th;
bool FCreateSuspended;
void ThreadCreate();
void ThreadJoin()
{
pthread_join(th, 0);
};
void ThreadKill()
{
pthread_cancel(th);
};
longword ThreadWait(uint64_t Timeout)
{
longword Elapsed = SysGetTick();
while (!Closed && !(DeltaTime(Elapsed) > Timeout))
SysSleep(100);
if (Closed)
return WAIT_OBJECT_0;
else
return WAIT_TIMEOUT;
};
protected:
bool Started;
public:
bool Terminated;
bool Closed;
bool FreeOnTerminate;
TSnapThread();
virtual ~TSnapThread();
virtual void Execute()
{
};
void Start();
void Terminate();
void Kill();
void Join();
longword WaitFor(uint64_t Timeout);
};
typedef TSnapThread *PSnapThread;
//---------------------------------------------------------------------------
#endif // unix_threads_h

View File

@ -0,0 +1,159 @@
/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|==============================================================================|
| |
| Windows Threads support (Windows, ReactOS) |
| |
|=============================================================================*/
#ifndef win_threads_h
#define win_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
//---------------------------------------------------------------------------
class TSnapCriticalSection
{
private:
CRITICAL_SECTION cs;
public:
TSnapCriticalSection()
{
InitializeCriticalSection(&cs);
};
~TSnapCriticalSection()
{
DeleteCriticalSection(&cs);
};
void Enter()
{
EnterCriticalSection(&cs);
};
void Leave()
{
LeaveCriticalSection(&cs);
};
bool TryEnter()
{
return (TryEnterCriticalSection(&cs) != 0);
};
};
typedef TSnapCriticalSection *PSnapCriticalSection;
//---------------------------------------------------------------------------
class TSnapEvent
{
private:
HANDLE Event;
public:
TSnapEvent(bool ManualReset)
{
Event = CreateEvent(0, ManualReset, false, 0);
};
~TSnapEvent()
{
if (Event != 0)
CloseHandle(Event);
};
void Set()
{
if (Event != 0)
SetEvent(Event);
};
void Reset()
{
if (Event != 0)
ResetEvent(Event);
};
longword WaitForever()
{
if (Event != 0)
return WaitForSingleObject(Event, INFINITE);
else
return WAIT_FAILED;
};
longword WaitFor(int64_t Timeout) {
if (Event != 0)
return WaitForSingleObject(Event, DWORD(Timeout));
else
return WAIT_FAILED;
};
};
typedef TSnapEvent *PSnapEvent;
//---------------------------------------------------------------------------
class TSnapThread {
private:
HANDLE th;
bool FCreateSuspended;
void ThreadCreate();
void ThreadJoin()
{
WaitForSingleObject(th, INFINITE);
};
void ThreadKill()
{
TerminateThread(th, 0);
};
longword ThreadWait(uint64_t Timeout)
{
return WaitForSingleObject(th, DWORD(Timeout));
};
protected:
bool Started;
public:
bool Terminated;
bool Closed;
bool FreeOnTerminate;
TSnapThread();
virtual ~TSnapThread();
virtual void Execute()
{
};
void Start();
void Terminate();
void Kill();
void Join();
longword WaitFor(uint64_t Timeout);
};
typedef TSnapThread *PSnapThread;
//---------------------------------------------------------------------------
#endif // win_threads_h