Combining
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.
In this example, we create
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.
You'll find the full source code for all the objects of the example below. The code depends on the reusable
sock class, so you will
want to familiarize yourself with that class before you can effectively read the example code.
Refer to the
Object Diagram below for an overview of how the objects interact:
- clienttest.c - Is the client side main program. Its job is simple:
call
clientNew for each desired client connection.
- client - Each client attempts to connect to the port that a
listener is listening on. Once the connection is established, it sends some data to
the server and then waits for a response. It does this endlessly until the program is
forcibly terminated. E.g. the user presses
CNTL-C or closes the window.
- servertest.c - Is the server side main program. It creates
a single
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 - The
listener object waits for
incoming connection request and then calls a callback routine (called SpawnServer)
which is implemented in servertest.c.
- server - Server objects are created in
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 sends it back to the client.
1.1 Object Diagram
Sockets Example Object Diagram
1.2 client.h
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| //
|
[MIT] |
|
| // OOSMOS client Class
|
|
| //
|
|
| // Copyright (c) 2014-2026 OOSMOS, LLC
|
|
| // SPDX-License-Identifier: MIT
|
|
| //
|
|
| |
|
| #ifndef client_h
|
|
| #define client_h
|
|
| |
|
| typedef struct clientTag client;
|
|
| |
|
| extern client * clientNew(const char * pHost, unsigned Port);
|
|
| extern void clientDelete(client * pClient);
|
|
| |
|
| #endif
|
|
oosmos/Classes/Tests/Sockets/client.h
1.3 client.c
client.c implements the
client class. The object has only one state,
Running, that uses an
oosmos_POLL event to make
thread calls.
On line 37, we call
sockConnect inside an
oosmos_ThreadWaitCond_TimeoutMS_Event thread call to connect to the server.
Once the connection is established, we drop down into the
for loop
and immediately send the string
123456 to the server.
OOSMOS
waits on line 50 until the data is delivered.
Then, we wait on line 54 as we receive the echoed data
back from the server.
We do this forever inside the
for loop beginning
on line 46.
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 |
|
| 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 |
|
| //
|
[MIT] |
|
| // OOSMOS client Class
|
|
| //
|
|
| // Copyright (c) 2014-2026 OOSMOS, LLC
|
|
| // SPDX-License-Identifier: MIT
|
|
| //
|
|
| |
|
| #include <stdio.h>
|
#includes... |
|
| #include <stdlib.h>
|
|
| |
|
| #include "oosmos.h"
|
|
| #include "client.h"
|
|
| #include "sock.h"
|
|
| |
|
| 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 = 0;
|
|
| |
|
| 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 = 0;
|
|
| |
|
| 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);
|
|
| }
|
|
oosmos/Classes/Tests/Sockets/client.c
1.4 server.h
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| //
|
[MIT] |
|
| // OOSMOS server Class
|
|
| //
|
|
| // Copyright (c) 2014-2026 OOSMOS, LLC
|
|
| // SPDX-License-Identifier: MIT
|
|
| //
|
|
| |
|
| #ifndef server_h
|
|
| #define server_h
|
|
| |
|
| #include "sock.h"
|
|
| |
|
| typedef struct serverTag server;
|
|
| |
|
| extern server * serverNew(sock * pSock);
|
|
| extern void serverDelete(server * pServer);
|
|
| |
|
| #endif
|
|
oosmos/Classes/Tests/Sockets/server.h
1.5 server.c
server.c implements the
server class. The object has only
one state,
Running, that uses an
oosmos_POLL event to make
thread calls.
On line 35, we wait at a
sockReceive call inside
an
oosmos_ThreadWaitCond thread call for data to be delivered by a client.
On line line 38, we send the data back to the client.
All of this is done forever inside the
for loop starting on line
33.
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 |
|
| 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 |
|
| //
|
[MIT] |
|
| // OOSMOS server Class
|
|
| //
|
|
| // Copyright (c) 2014-2026 OOSMOS, LLC
|
|
| // SPDX-License-Identifier: MIT
|
|
| //
|
|
| |
|
| #include <stdio.h>
|
#includes... |
|
| #include <stdlib.h>
|
|
| |
|
| #include "oosmos.h"
|
|
| #include "server.h"
|
|
| #include "sock.h"
|
|
| |
|
| 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.
|
|
| }
|
|
oosmos/Classes/Tests/Sockets/server.c
1.6 listener.h
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| //
|
[MIT] |
|
| // OOSMOS listener Class
|
|
| //
|
|
| // Copyright (c) 2014-2026 OOSMOS, LLC
|
|
| // SPDX-License-Identifier: MIT
|
|
| //
|
|
| |
|
| #ifndef listener_h
|
|
| #define listener_h
|
|
| |
|
| #include "sock.h"
|
|
| |
|
| typedef struct listenerTag listener;
|
|
| |
|
| extern listener * listenerNew(int Port, void (*pNewConnection)(sock *));
|
|
| |
|
| #endif
|
|
oosmos/Classes/Tests/Sockets/listener.h
1.7 listener.c
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
44) to establish the TCP port
to which clients will connect.
On line 32, we wait for connection requests
from clients. When a connection is established, variable
pNewSock will hold a pointer to a new
sock
object.
With this
sock object, we call a user
function that will create a new
server object (line
32). See
servertest.c.
| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 |
|
| 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 |
|
| //
|
[MIT] |
|
| // OOSMOS listener Class
|
|
| //
|
|
| // Copyright (c) 2014-2026 OOSMOS, LLC
|
|
| // SPDX-License-Identifier: MIT
|
|
| //
|
|
| |
|
| #include <stdio.h>
|
#includes... |
|
| #include <stdlib.h>
|
|
| |
|
| #include "oosmos.h"
|
|
| #include "listener.h"
|
|
| #include "sock.h"
|
|
| |
|
| 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;
|
|
| }
|
|
oosmos/Classes/Tests/Sockets/listener.c