blob: b21865dd18c8ec9c7f780f63a3852089fca027bf [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001/**
2 *
3 * This class demonstrates use of the CommonC++ SocketPort class.
4 *
5 * Copyright 2001 - Nick Liebmann <nick@ukmail.org>
6 *
7 * The SampleSocketPort is an implementation of the CommonC++ SocketPort class
8 * that irons out some problems that I found with disconnection, and also demonstrates
9 * a way of using the SocketPort to reliably extract and send data from/to a TCP/IP socket.
10 *
11 * In addition to this the SampleSocketPort includes some additional functionality
12 * to determine whether the data stream has become corrupted
13 * (missing terminator / incorrect formatting). For this feature a timer is used which if it is
14 * allowed to expire, indicates that a 'packet' took too long to arrive, and as such the data
15 * in the buffer is 'corrupt'.
16 *
17 * The SampleSocketPort can be used as-is...Modify the contents of the pending()
18 * function if your data is formatted differently to the default (i.e. not terminated with \r\n)
19 *
20 *
21 * This sample code is distributed under the same terms and conditions of the CommonC++ library.
22 *
23 * CHANGE HISTORY:
24 *
25 *
26 * 07/01/02 NL There have been slight changes to the way CommonC++ starts threads,
27 * a possible bug in InetHostAddress constructor, and a bug fix for SocketService
28 * #496276. The following changes address those issues
29 *
30 * New thread start semantics. SocketService Thread is now explicitly started
31 * by the SampleSocketServiceServer.
32 *
33 * Added SampleSocketServiceServer::StartServer() to start the server and wait
34 * for the thread to get up and running.
35 *
36 * Added SampleSocketServiceServer::StopServer() to cleanly stop the server, and
37 * ensure that there are no partially constructed SocketPorts left lying around
38 *
39 * Removed setDetectOutput(true)...this does not seem to be required anymore
40 * as the SocketService functions correctly now.
41 *
42 * InetHostAddress constructor does not treat INADDR_ANY as it used to.
43 *
44 * 07/01/02 NL main() - now waits for a 'quit' command, and deletes the Server object.
45 */
46
47
48#include "SampleSocketPort.h"
49
50SampleSocketPort::SampleSocketPort(SocketService *pService, TCPSocket & tcpSocket) :
51SocketPort(pService, tcpSocket)
52{
53 tpport_t port;
54 InetHostAddress ia = getPeer( & port );
55 cerr << "connecting from " << ia.getHostname() << ":" << port << endl;
56
57 // Set up non-blocking reads
58 setCompletion( false );
59
60 //1.9.3 THIS LINE DOES NOT SEEM TO BE REQUIRED ANYMORE!
61 //This sorts out a bug which prevents connections after a disconnect
62 //setDetectOutput(true);
63
64 m_bOpen = true;
65 m_bDoDisconnect = false;
66 m_bTimedOut = false;
67 m_bReceptionStarted = false;
68 m_nLastBytesAvail = 0;
69 m_pBuf = new char[MAX_RXBUF];
70}
71
72
73SampleSocketPort::~SampleSocketPort()
74{
75 endSocket();
76 delete [] m_pBuf;
77}
78
79void SampleSocketPort::pending(void)
80{
81//cerr << "Pending called " << endl;
82 if(!m_bOpen)
83 return;
84
85 // Read all available bytes into our buffer
86 int nBytesAvail = peek(m_pBuf, MAX_RXBUF);
87//cerr << "Pending .. " << nBytesAvail << endl;
88
89 if(!m_bReceptionStarted)
90 { //Start the receive timer
91 ResetReadTimeout(MAX_RXTIMEOUT); //Got 'n' seconds to get all the data else we timeout
92 m_bReceptionStarted = true;
93 }
94 else {
95 if(m_bTimedOut) //The receive timer has expired...this is a timeout condition
96 {
97 ResetReadTimeout(MAX_RXTIMEOUT); //Clear the timeout flag
98 m_nLastBytesAvail = 0; //Reset the flags
99 m_bReceptionStarted = false;
100 OnRxTimeout(); //Do whatever 'we' do for a timeout (probably a flush or disconnect)...
101 return;
102 }
103 }
104
105 if(m_nLastBytesAvail == nBytesAvail) //Check if any more data has been received since last time
106 { //No point in parsing unless this has changed!
107 //Maybe yield in here!
108 //Thread::yield();
109 if(nBytesAvail == 0) //If we have been called with 0 bytes available (twice now)
110 { //a disconnection has occurred
111 if(!m_bDoDisconnect) {
112 CloseSocket(); //Force the close
113 }
114 }
115 return;
116 }
117
118 //Depending on your application you may want to attempt to process the extra data
119 //(or change your MAX_RXBUF).
120 //
121 //Here I just flush the whole lot, because I assume a 'legal' client wont send more than
122 //we can receive....maybe someone is trying to flood / overrun us!
123 if(nBytesAvail > MAX_RXBUF) {
124 cerr << "TCP/IP overflow..." << endl;
125 FlushRxData();
126 m_nLastBytesAvail = 0;
127 m_bReceptionStarted = false;
128 return;
129 }
130 m_nLastBytesAvail = nBytesAvail;
131
132 //In this loop you may parse the received data to determine whether a whole
133 //'packet' has arrived. What you do in here depends on what data you are sending.
134 //Here we will just look for a /r/n terminator sequence.
135 for(int i=0; i < nBytesAvail; i++) {
136
137/***************************SHOULD BE CUSTOMISED*******************/
138
139 if(m_pBuf[i] == '\r') {
140 if(i+1 < nBytesAvail) {
141 if(m_pBuf[i+1] == '\n')
142 { //Terminator sequence found
143
144 /**************************************************************/
145 // COMPULSORY ... Clear the flag and count..
146 // do this when you have received a good packet
147 m_nLastBytesAvail = 0;
148 m_bReceptionStarted = false;
149 /**************************************************************/
150
151 // Now receive the data into a buffer and call our receive function
152 int nLen = i+2;
153 char *pszRxData = new char[nLen+1]; //Allow space for terminator
154 receive(pszRxData, nLen); //Receive the data
155 pszRxData[nLen] = '\0'; //Terminate it
156 OnDataReceived(pszRxData, nLen);
157 delete [] pszRxData;
158 return;
159 }
160 }
161 }
162/***************************END CUSTOMISATION*******************/
163
164 }
165}
166
167void SampleSocketPort::disconnect(void)
168{
169 if(m_bOpen) {
170 m_bDoDisconnect = true;
171 CloseSocket();
172 }
173}
174
175void SampleSocketPort::expired(void)
176{
177 if(m_bDoDisconnect && m_bOpen) {
178 CloseSocket();
179 }
180 else if(m_bOpen && m_bReceptionStarted) {
181 //Timer must have expired because the rx data has not all been received
182 m_bTimedOut = true;
183 }
184}
185
186
187bool SampleSocketPort::CloseSocket(void)
188{
189 if(m_bOpen && m_bDoDisconnect)
190 { //This is where the disconnection really occurs
191 m_bOpen = false; //If m_bDoDisconnect == true we know this has been called
192 OnConnectionClosed(); //through the timer, so 'delete this' is safe!
193 delete this;
194 }
195 else if(m_bOpen) {
196 m_bDoDisconnect = true; //Just set the timer and the flag so we can
197 setTimer(DISCONNECT_MS); //disconnect safely, in DISCONNECT_MS
198 }
199 return(true);
200}
201
202ssize_t SampleSocketPort::DoSend(void *buf, size_t len)
203{
204 //If we are disconnecting, just pretend all the bytes were sent
205 if(m_bDoDisconnect)
206 return((ssize_t)len);
207
208 ssize_t nSent = send(buf, len);
209 while(!isPending(Socket::pendingOutput, 0)) //Wait for output to complete
210 {
211 if(m_bDoDisconnect || !m_bOpen) {
212 //If we are disconnecting, just pretend all the bytes were sent
213 return((ssize_t)len);
214 }
215 //I like to yield whenever waiting for things...
216 //this is optional and may not suit your implementation!
217 Thread::yield();
218 }
219 return(nSent);
220}
221
222bool SampleSocketPort::WriteData(const char *szTxData, const size_t nByteCount)
223{
224 //First calculate how many bytes we are to send
225 ssize_t nLen = nByteCount;
226
227 if(nLen == -1)
228 nLen = (ssize_t)strlen(szTxData);
229
230 size_t nBytesToSend = nLen;
231
232 while(m_bOpen && nLen) {
233 nLen -= DoSend((void *)&(szTxData[nBytesToSend - nLen]), nLen);
234 }
235
236// If we are sending a terminator.....uncomment the following lines
237// char chTerminator = '\n';
238// while(DoSend((void *)&chTerminator, 1) != 1);
239
240 return(true);
241}
242
243#define WITH_EXAMPLE
244
245#ifdef WITH_EXAMPLE
246
247
248/************ THE FOLLOWING CODE DEMONSTRATES THE USE OF THE ABOVE CLASS ********************
249 ****
250 **** To test it, compile with:
251 ****
252 **** g++ SampleSocketPort.cpp -lccgnu -lpthread -ldl -oSampleSocketPort -ggdb -I/usr/local/include/cc++/
253 **** Run the program.
254 ****
255 **** From another terminal telnet to port 3999 of the server
256 ****
257 **** 'telnet localhost 3999'
258 ****
259 **** Anything you type should be sent back to you in reverse!
260 ****
261 **** To test the corrupt data detection, send a control code (like ^D),
262 **** if the terminating charcters are not detected within the specified time
263 **** the receive timeout will occur.
264 ****
265 ****/
266
267
268//define the following to include the example classes and functions
269
270int g_nOpenPorts = 0; //Dirty global to allow us to quit simply
271
272class ReverserPort : public SampleSocketPort
273{
274public:
275 ReverserPort(SocketService *pService, TCPSocket & tcpSocket) :
276 SampleSocketPort(pService, tcpSocket) {
277 g_nOpenPorts++;
278 }
279 virtual ~ReverserPort() {
280 g_nOpenPorts--;
281 }
282 virtual void OnConnectionClosed(void)
283 { cerr << "Connection Closed!" << endl; }
284
285 /**
286 * Called when a 'packet' of data has been received.
287 * This implementation simply reverses all the data and sends it back
288 */
289 virtual void OnDataReceived(char *pszData, unsigned int nByteCount) {
290 //Reverse the data and send it back
291
292 size_t nLen = strlen(pszData);
293 char *szToSend = new char[nLen+1];
294
295 //No need to reverse the \r\n or \0
296 size_t nIndex = nLen-3;
297
298 size_t i;
299 for(i=0; i < nLen - 2; i++) {
300 szToSend[i] = pszData[nIndex - i];
301 }
302 szToSend[i++] = '\r';
303 szToSend[i++] = '\n';
304 szToSend[nLen] = '\0';
305
306 WriteData(szToSend, nLen);
307 delete [] szToSend;
308 }
309
310};
311
312class ReverserServer : public SampleSocketServiceServer
313{
314public:
315 ReverserServer(InetHostAddress & machine, int port) :
316 TCPSocket(machine, port), Thread(), SampleSocketServiceServer(machine, port) {}
317
318 virtual ~ReverserServer() {}
319
320 virtual SocketPort *CreateSocketPort(SocketService *pService, TCPSocket & Socket) {
321 return(new ReverserPort(pService, Socket));
322 }
323};
324
325
326int main(void)
327{
328 InetHostAddress LocalHost;
329 LocalHost = htonl(INADDR_ANY);
330 ReverserServer *Server = NULL;
331 try {
332 Server = new ReverserServer(LocalHost, 3999);
333 Server->StartServer();
334 }
335 catch(...) {
336 cerr << "Failed to start server" << endl;
337 return(false);
338 }
339 cerr << "Waiting for connections...type \"quit\" to exit." << endl;
340
341 char cmd[255];
342
343 cin.getline(cmd, 255);
344
345
346 while(strcmp(cmd, "quit") != 0) {
347 cin.getline(cmd, 255);
348 }
349
350 Server->StopServer();
351 delete Server;
352 return 0;
353}
354
355#endif //WITH_EXAMPLE
356