OOSMOS Sockets Example
1 Introduction
1.1 Object Diagram
1.2 client.h
1.3 client.c
1.4 server.h
1.5 server.c
1.6 listener.h
1.7 listener.c
2 Sockets on Windows and Linux
2.1 clienttest.c
2.2 servertest.c
1 Introduction
1.1 Object Diagram
1.2 client.h
1.3 client.c
1.4 server.h
1.5 server.c
1.6 listener.h
1.7 listener.c
2 Sockets on Windows and...
2.1 clienttest.c
2.2 servertest.c
1 Introduction
1.1 Object Diagram
1.2 client.h
1.3 client.c
1.4 server.h
1.5 server.c
1.6 listener.h
1.7 listener.c
2 Sockets on Windows a...
2.1 clienttest.c
2.2 servertest.c
1 Introduction
1.1 Object Diagram
1.2 client.h
1.3 client.c
1.4 server.h
1.5 server.c
1.6 listener.h
1.7 listener.c
2 Sockets on Windows...
2.1 clienttest.c
2.2 servertest.c

1 Introduction

Combining OOSMOS's set of Async 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 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 send it back to the client.

1.1 Object Diagram

Sockets Example Object Diagram

1.2 client.h

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, int 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_INSTATE event to make asynchronous calls.
On lines 56-58, we call sockConnect inside an oosmos_AsyncWaitCond_TimeoutMS_Event Async call to connect to the server. We use oosmos_AsyncWaitCond_TimeoutMS_Event because if we timeout while waiting for the connection to be made, event ConnectionTimeoutEvent will be sent to ourselves, handled on line 86. This technique is handy because we can deal with exceptions out of the nominal sequence of instructions inside our oosmos_AsyncBegin and oosmos_AsyncEnd markers, making the mainstream code more readable.
Once the connection is established, we drop down into the while loop and immediately send the string 123456 to the server. OOSMOS "waits" on lines 66-68 until the data is delivered.
Then, we "wait" on lines 72-74 as we receive the echoed data back from the server.
We do this forever inside the while loop beginning on line 62.
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
[GPLv2]
 
#includes...
 
typedef enum
{
ConnectionTimeoutEvent = 1,
ClosedEvent,
} eEvents;
 
struct clientTag
{
oosmos_sStateMachine(StateMachine, oosmos_sEvent, 1);
oosmos_sLeaf Running_State;
 
const char * m_pHost;
int m_Port;
sock * m_pSock;
char m_Buffer[100];
};
 
static bool Running_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
client * pClient = (client *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE: {
const uint32_t IP_HostByteOrder = sockDotToIP_HostByteOrder(pClient->m_pHost);
 
oosmos_AsyncBegin(pRegion);
oosmos_AsyncWaitCond_TimeoutMS_Event(pRegion, 2000, ConnectionTimeoutEvent,
sockConnect(pClient->m_pSock, IP_HostByteOrder, pClient->m_Port)
);
 
printf("%p: CONNECTED\n", (void *) pClient->m_pSock);
 
while (true) {
size_t BytesReceived;
 
printf("%p: Sending...\n", (void *) pClient->m_pSock);
oosmos_AsyncWaitCond(pRegion,
sockSend(pClient->m_pSock, "123456", sizeof("123456"))
);
 
printf("%p: Waiting for incoming data...\n", (void *) pClient->m_pSock);
 
oosmos_AsyncWaitCond(pRegion,
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_AsyncEnd(pRegion);
return true;
}
 
case ClosedEvent:
printf("Server closed. Terminating...\n");
exit(1);
 
case ConnectionTimeoutEvent:
printf("%p: Unable to connect to server: Timed out. Terminating.\n", (void *) pClient->m_pSock);
exit(1);
}
 
return false;
}
 
extern client * clientNew(const char * pHost, int Port)
{
client * pClient = (client *) malloc(sizeof(client));
 
// StateName Parent Default
// ======================================================
oosmos_StateMachineInit(pClient, StateMachine, NULL, Running_State);
oosmos_LeafInit (pClient, Running_State, StateMachine );
 
pClient->m_pSock = sockNew();
pClient->m_pHost = pHost;
pClient->m_Port = Port;
 
sockSubscribeClosedEvent(pClient->m_pSock, &pClient->EventQueue, 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
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
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_INSTATE event to make asynchronous calls.
On lines 54-56, we "wait" at a sockReceive call inside an oosmos_AsyncWaitCond async call for data to be delivered by a client.
On lines 59-61, we send the data back to the client.
All of this is done forever inside the while loop starting on line 52.
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
[GPLv2]
 
#includes...
 
typedef enum
{
ClosedEvent = 1,
} eEvents;
 
struct serverTag
{
oosmos_sStateMachine(StateMachine, oosmos_sEvent, 1);
oosmos_sLeaf Running_State;
 
sock * m_pSock;
size_t m_BytesReceived;
char m_Buffer[100];
};
 
static bool Running_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
server * pServer = (server *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE:
oosmos_AsyncBegin(pRegion);
while (true) {
printf("Waiting for incoming data...\n");
oosmos_AsyncWaitCond(pRegion,
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_AsyncWaitCond(pRegion,
sockSend(pServer->m_pSock, pServer->m_Buffer, pServer->m_BytesReceived)
);
}
oosmos_AsyncEnd(pRegion);
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 Default
// ======================================================
oosmos_StateMachineInit(pServer, StateMachine, NULL, Running_State);
oosmos_LeafInit (pServer, Running_State, StateMachine );
 
pServer->m_pSock = pSock;
sockSubscribeClosedEvent(pSock, &pServer->EventQueue, ClosedEvent, NULL);
 
return pServer;
}
 
extern void serverDelete(server * pServer)
{
oosmos_StateMachineDetach(pServer, StateMachine);
 
printf("Delete Server %p.\n", (void *) pServer);
sockDelete(pServer->m_pSock);
free(pServer);
}
oosmos/Classes/Tests/Sockets/server.c

1.6 listener.h

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
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 45) to establish the TCP port to which clients will connect.
On lines 54-56, 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 57). 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
[GPLv2]
 
#includes...
 
struct listenerTag
{
oosmos_sStateMachineNoQueue(StateMachine);
oosmos_sLeaf Running_State;
 
sock * m_pSock;
void (*m_pAcceptedFunc)(sock *);
};
 
static bool Running_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
listener * pListener = (listener *) pObject;
 
switch (pEvent->Code) {
case oosmos_ENTER:
sockListen(pListener->m_pSock, 60009, 50);
return true;
 
case oosmos_INSTATE:
oosmos_AsyncBegin(pRegion);
while (true) {
sock * pNewSock;
 
printf("Waiting for incoming connections...\n");
 
oosmos_AsyncWaitCond(pRegion,
sockAccepted(pListener->m_pSock, &pNewSock)
);
pListener->m_pAcceptedFunc(pNewSock);
}
oosmos_AsyncEnd(pRegion);
return true;
}
 
return false;
}
 
extern listener * listenerNew(int Port, void (*pAcceptedFunc)(sock *))
{
oosmos_Allocate(pListener, listener, 1, NULL);
 
// StateName Parent Default
// ========================================================
oosmos_StateMachineInitNoQueue(pListener, StateMachine, NULL, Running_State);
oosmos_LeafInit (pListener, Running_State, StateMachine );
 
pListener->m_pSock = sockNew();
pListener->m_pAcceptedFunc = pAcceptedFunc;
 
return pListener;
}
oosmos/Classes/Tests/Sockets/listener.c

2 Sockets on Windows and Linux

This example has been tested on Windows (Microsoft's Visual C++ 2008 Express Edition command line compiler), Linux (Raspberry Pi, Red Hat and Mac OS X).

2.1 clienttest.c

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).
Once the 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
39
40
41
[GPLv2]
 
#includes...
 
extern int main(int argc, char *argv[])
{
int Count;
 
for (Count = 1; Count <= 5; Count += 1)
clientNew("127.0.0.1", 60009);
 
while (true) {
oosmos_RunStateMachines();
oosmos_DelayMS(50);
}
 
return 0;
}
oosmos/Classes/Tests/Sockets/clienttest.c

2.2 servertest.c

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).
We supply the address of a local function, 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
44
45
[GPLv2]
 
#includes...
 
static void SpawnServer(sock * pSock)
{
serverNew(pSock);
}
 
extern int main(int argc, char *argv[])
{
listenerNew(60009, SpawnServer);
 
while (true) {
oosmos_RunStateMachines();
oosmos_DelayMS(50);
}
 
return 0;
}
oosmos/Classes/Tests/Sockets/servertest.c
Copyright © 2014-2016  OOSMOS, LLC