OOSMOS's set of Thread functions and the
reusable sock class, this
example demonstrates how to connect and communicate over many connections
at the same time using only OOSMOS objects and without the use of RTOS threads.
client objects that connect to a waiting listener
object which then creates a corresponding server object. Thus the client
object and the new server object have a peer-to-peer connection.
The client object
then repeatedly sends data to the server object which then echoes the data back
to the client. This continues until the programs are forcibly terminated.
clientNew for each desired client connection.CNTL-C or closes the window.listener object to which clients will attempt to connect. This is
done by calling listenerNew. The arguments to listenerNew
are the TCP port to listen on as well as the address of a routine to call when
a new client connection request is made.listener object waits for
incoming connection request and then calls a callback routine (called SpawnServer)
which is implemented in servertest.c.servertest.c
by function SpawnServer as new connection requests come in from clients. Once created, each server is now paired with a
client. Because the client sends data and then waits for a response, the server does the
complement of that and waits for data and then send it back to the client.| 1 | |
|---|---|
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| [GPLv2] | |
| #ifndef client_h | |
| #define client_h | |
| typedef struct clientTag client; | |
| extern client * clientNew(const char * pHost, unsigned Port); | |
| extern void clientDelete(client * pClient); | |
| #endif |
client.c implements the client class. The object has only one state,
Running, that uses an oosmos_POLL event to make
thread calls.
sockConnect inside an
oosmos_ThreadWaitCond_TimeoutMS_Event thread call to connect to the server.
for loop
and immediately send the string 123456 to the server. OOSMOS
waits on lines line 65 until the data is delivered.
for loop beginning
on line 61.
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | |
| 66 | |
| 67 | |
| 68 | |
| 69 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | |
| 85 | |
| 86 | |
| 87 | |
| 88 | |
| 89 | |
| 90 | |
| 91 | |
| 92 | |
| 93 | |
| 94 | |
| 95 | |
| 96 | |
| 97 | |
| 98 | |
| 99 | |
| 100 | |
| 101 | |
| 102 | |
| 103 | |
| 104 | |
| 105 | |
| 106 | |
| 107 | |
| 108 | |
| 109 | |
| 110 | |
| 111 | |
| 112 | |
| 113 | |
| 114 | |
| 115 | |
| 116 | |
| 117 | |
| 118 | |
| [GPLv2] | |
| #includes... | |
| enum | |
| { | |
| ConnectionTimeoutEvent = 1, | |
| ClosedEvent | |
| }; | |
| struct clientTag | |
| { | |
| oosmos_sStateMachine(StateMachine, oosmos_sEvent, 1); | |
| oosmos_sLeaf Running_State; | |
| const char * m_pHost; | |
| unsigned m_Port; | |
| sock * m_pSock; | |
| char m_Buffer[100]; | |
| }; | |
| static void ClientThread(client * pClient, oosmos_sState * pState, uint32_t IP_HostByteOrder) | |
| { | |
| bool TimedOut; | |
| oosmos_ThreadBegin(); | |
| oosmos_ThreadWaitCond_TimeoutMS(sockConnect(pClient->m_pSock, IP_HostByteOrder, pClient->m_Port), 2000, &TimedOut); | |
| if (TimedOut) { | |
| printf("%p: Unable to connect to server: Timed out. Terminating.\n", (void *) pClient->m_pSock); | |
| exit(1); | |
| } | |
| printf("%p: CONNECTED\n", (void *) pClient->m_pSock); | |
| for (;;) { | |
| size_t BytesReceived; | |
| printf("%p: Sending...\n", (void *) pClient->m_pSock); | |
| oosmos_ThreadWaitCond(sockSend(pClient->m_pSock, "123456", sizeof("123456"))); | |
| printf("%p: Waiting for incoming data...\n", (void *) pClient->m_pSock); | |
| oosmos_ThreadWaitCond(sockReceive(pClient->m_pSock, pClient->m_Buffer, sizeof(pClient->m_Buffer), &BytesReceived)); | |
| printf("%p: Client side Received '%s', BytesReceived: %u\n", (void *) pClient->m_pSock, pClient->m_Buffer, (unsigned) BytesReceived); | |
| } | |
| oosmos_ThreadEnd(); | |
| } | |
| static bool Running_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent) | |
| { | |
| client * pClient = (client *) pObject; | |
| switch (oosmos_EventCode(pEvent)) { | |
| case oosmos_POLL: { | |
| const uint32_t IP_HostByteOrder = sockDotToIP_HostByteOrder(pClient->m_pHost); | |
| ClientThread(pClient, pState, IP_HostByteOrder); | |
| return true; | |
| } | |
| case ClosedEvent: { | |
| printf("Server closed. Terminating...\n"); | |
| exit(1); | |
| } | |
| } | |
| return false; | |
| } | |
| extern client * clientNew(const char * pHost, unsigned Port) | |
| { | |
| client * pClient = (client *) malloc(sizeof(client)); | |
| // StateName Parent | |
| // =========================================================== | |
| oosmos_StateMachineInit(pClient, StateMachine, NULL, Running_State ); | |
| oosmos_LeafInit (pClient, Running_State, StateMachine, Running_State_Code); | |
| pClient->m_pSock = sockNew(); | |
| pClient->m_pHost = pHost; | |
| pClient->m_Port = Port; | |
| sockSubscribeClosedEvent(pClient->m_pSock, oosmos_EventQueue(pClient), ClosedEvent, NULL); | |
| return pClient; | |
| } | |
| extern void clientDelete(client * pClient) | |
| { | |
| printf("In clientDelete\n"); | |
| oosmos_StateMachineDetach(pClient, StateMachine); | |
| free(pClient); | |
| } |
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| [GPLv2] | |
| #ifndef server_h | |
| #define server_h | |
| #include "sock.h" | |
| typedef struct serverTag server; | |
| extern server * serverNew(sock * pSock); | |
| extern void serverDelete(server * pServer); | |
| #endif |
server.c implements the server class. The object has only
one state,
Running, that uses an oosmos_POLL event to make
thread calls.
sockReceive call inside
an oosmos_ThreadWaitCond thread call for data to be delivered by a client.
for loop starting on line
48.
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | |
| 66 | |
| 67 | |
| 68 | |
| 69 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | |
| 85 | |
| 86 | |
| 87 | |
| 88 | |
| 89 | |
| 90 | |
| 91 | |
| 92 | |
| 93 | |
| 94 | |
| 95 | |
| 96 | |
| 97 | |
| 98 | |
| 99 | |
| 100 | |
| 101 | |
| [GPLv2] | |
| #includes... | |
| enum | |
| { | |
| ClosedEvent = 1 | |
| }; | |
| struct serverTag | |
| { | |
| oosmos_sStateMachine(StateMachine, oosmos_sEvent, 1); | |
| oosmos_sLeaf Running_State; | |
| sock * m_pSock; | |
| size_t m_BytesReceived; | |
| char m_Buffer[100]; | |
| }; | |
| static void IncomingThread(server * pServer, oosmos_sState * pState) | |
| { | |
| oosmos_ThreadBegin(); | |
| for (;;) { | |
| printf("Waiting for incoming data...\n"); | |
| oosmos_ThreadWaitCond(sockReceive(pServer->m_pSock, pServer->m_Buffer, sizeof(pServer->m_Buffer), &pServer->m_BytesReceived)); | |
| printf("Server side, String: '%s', BytesReceived: %u\n", pServer->m_Buffer, (unsigned) pServer->m_BytesReceived); | |
| oosmos_ThreadWaitCond(sockSend(pServer->m_pSock, pServer->m_Buffer, pServer->m_BytesReceived)); | |
| } | |
| oosmos_ThreadEnd(); | |
| } | |
| static bool Running_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent) | |
| { | |
| server * pServer = (server *) pObject; | |
| switch (oosmos_EventCode(pEvent)) { | |
| case oosmos_POLL: { | |
| IncomingThread(pServer, pState); | |
| return true; | |
| } | |
| case ClosedEvent: { | |
| serverDelete(pServer); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| extern server * serverNew(sock * pSock) | |
| { | |
| server * pServer = (server *) malloc(sizeof(server)); | |
| printf("New Server %p.\n", (void *) pServer); | |
| // StateName Parent | |
| // =========================================================== | |
| oosmos_StateMachineInit(pServer, StateMachine, NULL, Running_State ); | |
| oosmos_LeafInit (pServer, Running_State, StateMachine, Running_State_Code); | |
| pServer->m_pSock = pSock; | |
| sockSubscribeClosedEvent(pSock, oosmos_EventQueue(pServer), ClosedEvent, NULL); | |
| return pServer; | |
| } | |
| extern void serverDelete(server * pServer) | |
| { | |
| oosmos_StateMachineDetach(pServer, StateMachine); | |
| printf("Delete Server %p.\n", (void *) pServer); | |
| sockDelete(pServer->m_pSock); | |
| // Note: Once an oosmos object is created and registered on the object list, it can not be deleted. | |
| } |
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| [GPLv2] | |
| #ifndef listener_h | |
| #define listener_h | |
| #include "sock.h" | |
| typedef struct listenerTag listener; | |
| extern listener * listenerNew(int Port, void (*pNewConnection)(sock *)); | |
| #endif |
listener.c implements the listener class. The object has only one state,
Running. As we enter the Running state,
we issue the sockListen function (line
59) to establish the TCP port
to which clients will connect.
pNewSock will hold a pointer to a new sock
object.
sock object, we call a user
function that will create a new server object (line
47). See servertest.c.
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | |
| 66 | |
| 67 | |
| 68 | |
| 69 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | |
| [GPLv2] | |
| #includes... | |
| struct listenerTag | |
| { | |
| oosmos_sStateMachineNoQueue(StateMachine); | |
| oosmos_sLeaf Running_State; | |
| sock * m_pSock; | |
| void (*m_pAcceptedFunc)(sock *); | |
| }; | |
| static void ListeningThread(listener * pListener, oosmos_sState * pState) | |
| { | |
| oosmos_ThreadBegin(); | |
| for (;;) { | |
| sock * pNewSock; | |
| printf("Waiting for incoming connections...\n"); | |
| oosmos_ThreadWaitCond(sockAccepted(pListener->m_pSock, &pNewSock)); | |
| pListener->m_pAcceptedFunc(pNewSock); | |
| } | |
| oosmos_ThreadEnd(); | |
| } | |
| static bool Running_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent) | |
| { | |
| listener * pListener = (listener *) pObject; | |
| switch (oosmos_EventCode(pEvent)) { | |
| case oosmos_ENTER: { | |
| sockListen(pListener->m_pSock, 60009, 50); | |
| return true; | |
| } | |
| case oosmos_POLL: { | |
| ListeningThread(pListener, pState); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| extern listener * listenerNew(int Port, void (*pAcceptedFunc)(sock *)) | |
| { | |
| oosmos_Allocate(pListener, listener, 1, NULL); | |
| // StateName Parent | |
| // ============================================================= | |
| oosmos_StateMachineInitNoQueue(pListener, StateMachine, NULL, Running_State ); | |
| oosmos_LeafInit (pListener, Running_State, StateMachine, Running_State_Code); | |
| pListener->m_pSock = sockNew(); | |
| pListener->m_pAcceptedFunc = pAcceptedFunc; | |
| return pListener; | |
| } |
clienttest.c contains the client-side main program. It
simply creates a number of client objects passing
the IP and port number of a listener object to which
each client will connect (line 31).
client objects are created, we then drop into
the endless loop to call oosmos_RunStateMachines. Because
we are running on either Windows or Linux, we don't own the processor,
so we relinquish control for a number of milliseconds. The net effect
is that this example program takes a very small amount of CPU time.
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| [GPLv2] | |
| #includes... | |
| extern int main(int argc, char *argv[]) | |
| { | |
| for (unsigned Count = 1; Count <= 5; Count += 1) { | |
| clientNew("127.0.0.1", 60009); | |
| } | |
| for (;;) { | |
| oosmos_RunStateMachines(); | |
| oosmos_DelayMS(1); | |
| } | |
| } |
servertest.c contains the server-side main program. Initially it creates
a listener object with two arguments: 1) the TCP port number
on which to listen for connection requests and 2) the address of a subroutine
to call when new connection requests come in (line 37).
SpawnServer, as the second argument
(lines 30-33), which calls serverNew to create
a new server object.
| 1 | |
|---|---|
| 22 | |
| 23 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| [GPLv2] | |
| #includes... | |
| static void SpawnServer(sock * pSock) | |
| { | |
| serverNew(pSock); | |
| } | |
| extern int main(int argc, char *argv[]) | |
| { | |
| listenerNew(60009, SpawnServer); | |
| for (;;) { | |
| oosmos_RunStateMachines(); | |
| oosmos_DelayMS(1); | |
| } | |
| } |