2024-03-19 17:45:12 +08:00

248 lines
9.3 KiB
C++

/*=============================================================================|
| 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