File: server.cpp

package info (click to toggle)
cxxtools 3.0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 8,960 kB
  • sloc: cpp: 68,550; ansic: 15,641; sh: 4,239; makefile: 841
file content (178 lines) | stat: -rw-r--r-- 5,413 bytes parent folder | download | duplicates (2)
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <cxxtools/arg.h>
#include <cxxtools/eventloop.h>
#include <cxxtools/log.h>
#include <cxxtools/net/tcpserver.h>
#include <cxxtools/net/tcpstream.h>

#include <algorithm>
#include <iostream>
#include <vector>

log_define("server")

class Responder;

////////////////////////////////////////////////////////////////////////
// Define a server class
//
class Server : public cxxtools::Connectable
{
    cxxtools::net::TcpServer _server;
    std::vector<Responder*> _responder;

    void onNewConnection(cxxtools::net::TcpServer&);
    void onClosed(cxxtools::net::TcpStream&);

public:
    explicit Server(cxxtools::SelectorBase* selector, const std::string& ip, unsigned short port);
};

Server::Server(cxxtools::SelectorBase* selector, const std::string& ip, unsigned short port)
    : _server(ip, port)
{
    _server.setSelector(selector);
    // When a connection arrives from a client the signal 'connectionPending' is emmitted.
    // We connect that to our handler.
    cxxtools::connect(_server.connectionPending, *this, &Server::onNewConnection);
}

////////////////////////////////////////////////////////////////////////
// The Responder class handles a single client.
//
class Responder : public cxxtools::net::TcpStream
{
    void onInput(cxxtools::net::TcpStream&);
    void onOutput(cxxtools::net::TcpStream&);

public:
    explicit Responder(cxxtools::net::TcpServer& server);
};

////////////////////////////////////////////////////////////////////////

void Server::onNewConnection(cxxtools::net::TcpServer&)
{
    // When a new connection arrives create a new client responder.

    log_debug("new connection");
    Responder* responder = new Responder(_server);
    _responder.push_back(responder);

    // When the client closes the connection the signal 'closed' is emmitted.
    // Connect that to our handler onClosed.
    cxxtools::connect(responder->closed, *this, &Server::onClosed);
}

void Server::onClosed(cxxtools::net::TcpStream& responder)
{
    // When a client is closed we delete the Responder
    std::vector<Responder*>::iterator it = std::find(_responder.begin(), _responder.end(), &responder);
    if (it != _responder.end())  // should always be true
    {
        log_debug("closed");
        _responder.erase(it);
        delete *it;
    }
    else
    {
        log_warn("responder not found");
    }
}

////////////////////////////////////////////////////////////////////////
Responder::Responder(cxxtools::net::TcpServer& server)
    : cxxtools::net::TcpStream(server)
{
    setSelector(server.selector());

    // connect the signals 'inputReady' and 'outputReady' to our handlers.
    cxxtools::connect(inputReady, *this, &Responder::onInput);
    cxxtools::connect(outputReady, *this, &Responder::onOutput);

    // Start waiting for input. When data arrives, the signal 'inputReady' is
    // emmitted.
    beginRead();
}

void Responder::onInput(cxxtools::net::TcpStream&)
{
    log_debug("onInput");

    // Data has arrived. End read reads data from the socket and fills our
    // input buffer.
    endRead();

    if (in_avail() == 0)
    {
        // When 'inputReady' is emmitted without data the client has actually
        // closed the connection. We emmit the signal 'closed'.
        closed(*this);
        return;
    }

    // echo input back to the client
    char ch;
    while (get(ch), *this)
    {
        // Put the character to the output buffer
        put(ch);
    }

    // We wrote at least one character to the output buffer.
    // We signal, that we want to send the data as soon as the socket is
    // ready to accept outgoing data without blocking.
    beginWrite();

    // We called endRead. Now we want to continue reading and put the device
    // back into read mode.
    beginRead();
}

void Responder::onOutput(cxxtools::net::TcpStream&)
{
    log_debug("out avail=" << buffer().out_avail());

    // The device has signaled, that it is ready to accept data to be sent.
    // The call to endWrite copies data from our output buffer to the socket.
    endWrite();

    // The socket may have accepted only part of our output buffer so there
    // may be data left. We tell the device, that we want to transfer more
    // data when there is space in the output buffer.
    if (buffer().out_avail())
        beginWrite();
}

////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
    try
    {
        // read options from command line
        // -l <ip>   specifies the ip address of the interface to listen on
        //           a empty string tells cxxtools to listen on all interfaces.
        // -p <port> specifies to port with a default value of 7000.
        cxxtools::Arg<std::string> ip(argc, argv, 'l');
        cxxtools::Arg<unsigned short> port(argc, argv, 'p', 7000);

        // This initializes logging by reading a file 'log.xml' or
        // 'log.properties' in the local directory. Optionally a file name or a
        // object of type cxxtools::LogConfiguration could be passed to
        // log_init().
        log_init();

        // We need a event loop, which handles I/O events
        cxxtools::EventLoop loop;

        // Our server runs in the loop.
        Server server(&loop, ip, port);

        // Run the loop
        loop.run();
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}