summaryrefslogtreecommitdiff
path: root/examples/Reactor/TP_Reactor/ReadHandler.cpp
blob: 06c6c9530467a4458ecf2b60c31981cce5c8f8ae (plain)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * ACE reactor demonstration
 *
 * $Id$
 * Date: 26-Jan-2006
 */

#include <ace/streams.h>
#include <ace/Time_Value.h>

#include "common.h"
#include "ReadHandler.h"

/**
 * This macro is used to increase the invocation counter by one when entering
 * handle_input(). It also checks wether the counter is greater than zero
 * indicating, that handle_input() has been called before.
 */
#define INVOCATION_ENTER() do { if (mInvocationCounter > 0) \
        ACE_ERROR((LM_ERROR, ACE_TEXT("Multiple invocations detected.\n"))); \
        mInvocationCounter++; } while (0)

/**
 * THis macro is the counter part to INVOCATION_ENTER(). It decreases the
 * invocation counter and then returns the given value. This macro is
 * here for convenience to decrease the invocation counter also when returning
 * due to errors.
 */
#define INVOCATION_RETURN(retval) do { mInvocationCounter--; \
                                    return retval; } while(0)

ReadHandler::ReadHandler() : ACE_Event_Handler(), mStream(), mDataSize(0),
        mData(0), mCallCounter(0), mInvocationCounter(0) {
    ACE_TRACE(ACE_TEXT("ReadHandler::ReadHandler()"));
}

ReadHandler::~ReadHandler() {
    ACE_TRACE(ACE_TEXT("ReadHandler::~ReadHandler()"));

    if (mStream.close() == -1)
      ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to close socket. ")
                 ACE_TEXT ("(errno = %i: %m)\n"), errno));

    delete[] mData;
}

ACE_SOCK_Stream &ReadHandler::getStream(void) {
    ACE_TRACE(ACE_TEXT("ReadHandler::getStream(void)"));
    return mStream;
}

ACE_HANDLE ReadHandler::get_handle(void) const {
    ACE_TRACE(ACE_TEXT("ReadHandler::get_handle(void)"));
    return mStream.get_handle();
}

int ReadHandler::handle_input(ACE_HANDLE) {
    ACE_TRACE(ACE_TEXT("ReadHandler::handle_input(ACE_HANDLE)"));

    INVOCATION_ENTER();

    // the response sent to the client
    char response = 0;

    if (mCallCounter == 0) {

        /*
         * This is the first request from the client.
         */

        // increase the call counter so the next client request goes to else-if
        mCallCounter++;

        // get the desired size from the client
        // Note: only use the sizeof and pointer to int on compatible
        //       platforms (i.e. little-endian/big-endian, data type size)
        if (mStream.recv_n(&mDataSize, sizeof(mDataSize),
                &connTimeout) != sizeof(mDataSize)) {
          ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to receive ")
                     ACE_TEXT ("request. (errno = %i: %m)\n"), errno));
            INVOCATION_RETURN(-1);
        }

        // The verbose debug output is replaced with some unintrusive dots.
        // This increases visibility of the desired effect.
        // ACE_DEBUG((LM_DEBUG, ACE_TEXT("%@: Data size: %i\n"), this, mDataSize));
        ACE_DEBUG((LM_DEBUG, ACE_TEXT(".")));

        // check mDataSize for plausability then allocate memory
        if (mDataSize > 0) {
            mData = new (std::nothrow) char[mDataSize];
            if (mData == 0)
              ACE_DEBUG((LM_DEBUG, ACE_TEXT("%N:%l: Failed to allocate ")
                         ACE_TEXT ("data buffer.\n")));
            else
                response = 'K';
        }

        // send the response to the client (which is still 0, if the
        // allocation did not succeed)
        if (mStream.send_n(&response, sizeof(response), &connTimeout) != 1) {
          ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to send ")
                     ACE_TEXT ("response. (errno = %i: %m)\n"), errno));
            INVOCATION_RETURN(-1);
        }

        if (response == 'K')
            INVOCATION_RETURN(0); // get another request from the same client
        else
            INVOCATION_RETURN(-1); // the client will not send data if response != 'K'

    } else if (mCallCounter == 1) {

        /*
         * This is the second request from the client.
         */

        // increase the call counter, this read handler should not be called
        // again
        mCallCounter++;

        // receive the data from the client
        if (mStream.recv_n(mData, mDataSize, &connTimeout) != mDataSize) {
          ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to receive data.")
                     ACE_TEXT ("(errno = %i: %m)\n"), errno));
            INVOCATION_RETURN(-1);
        }

        response = 'K';

        if (mStream.send_n(&response, 1, &connTimeout) != 1) {
          ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to send ")
                     ACE_TEXT ("confirmation. (errno = %i: %m)\n"), errno));
            INVOCATION_RETURN(-1);
        }

        INVOCATION_RETURN(-1); // ask for removal, since client does not send any more data
    }

    // this is to find strange actions with the call counter
    ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: We should not get here.")));
    INVOCATION_RETURN(-1);
}

int ReadHandler::handle_close(ACE_HANDLE, ACE_Reactor_Mask) {
  ACE_TRACE("ReadHandler::handle_close(ACE_HANDLE, ACE_Reactor_Mask)");

    delete this;
    return 0;
}