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); | |
} | |
} |