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