Cleaner examples
- Use ${CXX} instead of explicit clang in the Makefile.
- More explanations.
- Use socat's fork option instead of a bash loop.
- Adds a run_* script for each example.
- Remove the useless "sleep" option in service2.
This commit is contained in:
parent
8bd46d2450
commit
f74f89734c
9 changed files with 89 additions and 26 deletions
10
Makefile
10
Makefile
|
|
@ -1,11 +1,11 @@
|
||||||
all: pcat service service2
|
all: pcat service1 service2
|
||||||
|
|
||||||
pcat: pcat.cpp
|
pcat: pcat.cpp
|
||||||
clang++ pcat.cpp -o pcat
|
${CXX} pcat.cpp -o pcat
|
||||||
|
|
||||||
service: service.cpp
|
1service1: service1.cpp
|
||||||
clang++ service.cpp -o service
|
${CXX} service1.cpp -o service1
|
||||||
|
|
||||||
service2: service2.cpp
|
service2: service2.cpp
|
||||||
clang++ service2.cpp -l pthread -o service2
|
${CXX} service2.cpp -l pthread -o service2
|
||||||
|
|
||||||
|
|
|
||||||
68
README.md
68
README.md
|
|
@ -1,7 +1,8 @@
|
||||||
Named pipes services
|
Named pipes services
|
||||||
====================
|
====================
|
||||||
|
|
||||||
Examples of how to design C++ services that use Linux' named pipes FIFO as I/O.
|
Examples of how to design services that use Linux' named pipes FIFO as I/O.
|
||||||
|
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
---------
|
---------
|
||||||
|
|
@ -20,28 +21,48 @@ Once you made your service on top of named pipes,
|
||||||
it is easy to wrap it within an interface made with other languages/tools.
|
it is easy to wrap it within an interface made with other languages/tools.
|
||||||
For instance, it is very easy to expose it on the network using common Linux tools like `socat`.
|
For instance, it is very easy to expose it on the network using common Linux tools like `socat`.
|
||||||
|
|
||||||
|
Be warned that this is not secure, though, you should only use this for testing
|
||||||
|
purpose in a secure local network.
|
||||||
|
|
||||||
|
|
||||||
Build and run
|
Build and run
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./build.sh
|
./build.sh
|
||||||
./run.sh
|
./run_service2.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Two examples are provided.
|
To create the named pipes under Linux, use the `mkfifo` command, as shown in the `build.sh`
|
||||||
|
script.
|
||||||
|
|
||||||
|
### Trivial example: a `cat` service
|
||||||
|
|
||||||
|
The `pcat` executable implements a service that reads from a named pipe and
|
||||||
|
print its content on the standard output.
|
||||||
|
It's just like a `cat` command, but that would not end after the first read, but
|
||||||
|
will continue reading from the pipe instead.
|
||||||
|
|
||||||
|
This kind of service is just a simple loop that iterates over blocking I/O calls
|
||||||
|
on the named pipes, thus having zero CPU cost for polling.
|
||||||
|
|
||||||
|
The file `run_pcat.sh` shows how to run the example.
|
||||||
|
|
||||||
|
Note: if this example prints "Hello World!" multiple times, that's because you
|
||||||
|
did not created the `data` file as a named pipe, but as a regular file. Hence,
|
||||||
|
instead of emptying its content after reading, it keeps reading the same
|
||||||
|
content.
|
||||||
|
|
||||||
|
The `pcat.py` is the same example, but in Python instead of C++.
|
||||||
|
|
||||||
|
|
||||||
### Simple service
|
### Simple service
|
||||||
|
|
||||||
The first example `./service in out` implements a service
|
The first example `./service in out` implements a service
|
||||||
that reads from a named pipe `in` and writes to another one `out`.
|
that reads from a named pipe `in` and writes to another one `out`.
|
||||||
To create the named pipes, use the `mkfifo` command.
|
|
||||||
|
|
||||||
The service is just a simple loop that iterates over blocking I/O calls
|
|
||||||
on the named pipes, thus having zero CPU cost for polling.
|
|
||||||
|
|
||||||
Once launched, the service will wait for the pipes to be consummed,
|
Once launched, the service will wait for the pipes to be consummed,
|
||||||
for instance with two commands.
|
for instance with two commands.
|
||||||
|
|
@ -54,7 +75,9 @@ The second one reads the result:
|
||||||
cat out
|
cat out
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you can use the same pipe for input and output: `./service data data`.
|
The file `run_service1.sh` shows how to run this example.
|
||||||
|
|
||||||
|
Note that you can use the same pipe for input and output: `./service1 data data`.
|
||||||
|
|
||||||
|
|
||||||
### Service with two dependent inputs
|
### Service with two dependent inputs
|
||||||
|
|
@ -66,23 +89,26 @@ after which it is possible to consume some "data".
|
||||||
The service use two threads, one to poll the context and one to poll the data
|
The service use two threads, one to poll the context and one to poll the data
|
||||||
and do something with it.
|
and do something with it.
|
||||||
|
|
||||||
The script `run.sh` shows how to test it.
|
The script `run_service2.sh` shows how to test it.
|
||||||
Run it and it should show `Context: data` as a last line.
|
Run it and it should show `Context: data` as a last line.
|
||||||
|
|
||||||
|
Use `Ctrl-C` to close the remaining `cat` process.
|
||||||
|
|
||||||
|
|
||||||
Furthermore
|
Furthermore
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
If you want to expose such a service as a network server, just use socat.
|
If you want to expose such a service as a network server, just use socat.
|
||||||
|
|
||||||
For example, to get _data_ query from the network for `service2`:
|
For example, to get _data_ query from the network for `service1`:
|
||||||
```sh
|
```sh
|
||||||
socat -v -u TCP-LISTEN:8423,reuseaddr PIPE:./data
|
socat -v -u TCP-LISTEN:8423,reuseaddr PIPE:./data
|
||||||
```
|
```
|
||||||
|
(see `run_socat_server.sh` for a complete example).
|
||||||
|
|
||||||
You can test it by sending something on the connection:
|
You can test it by sending something on the connection:
|
||||||
```sh
|
```sh
|
||||||
echo "data" > /dev/tcp/127.0.0.1/8423
|
echo "Hello World!" > /dev/tcp/127.0.0.1/8423
|
||||||
```
|
```
|
||||||
|
|
||||||
Conversely, to send automatically back the answer to some server:
|
Conversely, to send automatically back the answer to some server:
|
||||||
|
|
@ -91,12 +117,28 @@ socat -v -u PIPE:./out TCP2:8424:host
|
||||||
```
|
```
|
||||||
|
|
||||||
Be aware that `socat` will terminate as soon as it receives the end of the message.
|
Be aware that `socat` will terminate as soon as it receives the end of the message.
|
||||||
Thus, if you want to establish a permanent gate, you will have to automatically restart it:
|
Thus, if you want to establish a permanent gate, you will have to use the `fork`
|
||||||
|
option:
|
||||||
```sh
|
```sh
|
||||||
while true; do socat TCP-LISTEN:8478,reuseaddr PIPE:/./data || break; done
|
socat TCP-LISTEN:8478,reuseaddr,fork PIPE:/./data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
===============
|
||||||
|
|
||||||
|
If you witness strange behavior while debugging your own services (like prints
|
||||||
|
that do not occur in the correct terminal), double check that yo don't have some
|
||||||
|
remaining detached processes that would not have been killed.
|
||||||
|
|
||||||
|
For instance, if:
|
||||||
|
```sh
|
||||||
|
ps aux | grep pcat
|
||||||
|
```
|
||||||
|
returns anything, you would need to `killall pcat`, or else several concurent
|
||||||
|
processes would read the same pipe, which leads to (almost) undefined behavior.
|
||||||
|
|
||||||
|
|
||||||
Author & license
|
Author & license
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
|
|
||||||
0
build.sh
Normal file → Executable file
0
build.sh
Normal file → Executable file
8
run_pcat.sh
Executable file
8
run_pcat.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
# Start the service and let it run in the background.
|
||||||
|
./pcat data &
|
||||||
|
PID_SERVICE=$!
|
||||||
|
|
||||||
|
echo "Hello world!" > data &
|
||||||
|
|
||||||
|
KILL $PID_SERVICE
|
||||||
11
run_service1.sh
Executable file
11
run_service1.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
./service1 data > out &
|
||||||
|
PID_SERVICE=$!
|
||||||
|
|
||||||
|
echo "Hellow World!" > data &
|
||||||
|
|
||||||
|
cat out &
|
||||||
|
|
||||||
|
echo "exit" > data
|
||||||
|
|
||||||
|
# kill $PID_SERVICE
|
||||||
7
run_socat_server.sh
Executable file
7
run_socat_server.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
./service1 data out &
|
||||||
|
PID_SERVICE=$!
|
||||||
|
|
||||||
|
socat -v -u TCP-LISTEN:8423,reuseaddr,fork PIPE:./data
|
||||||
|
|
||||||
|
kill $PID_SERVICE
|
||||||
11
service2.cpp
11
service2.cpp
|
|
@ -18,7 +18,6 @@ protected:
|
||||||
std::string _file_current_context;
|
std::string _file_current_context;
|
||||||
std::string _file_data;
|
std::string _file_data;
|
||||||
std::string _out;
|
std::string _out;
|
||||||
const unsigned int _sleep;
|
|
||||||
std::string _current_context;
|
std::string _current_context;
|
||||||
|
|
||||||
bool has_current_context()
|
bool has_current_context()
|
||||||
|
|
@ -38,14 +37,12 @@ public:
|
||||||
Service(
|
Service(
|
||||||
std::string context,
|
std::string context,
|
||||||
std::string data,
|
std::string data,
|
||||||
std::string out,
|
std::string out
|
||||||
unsigned int sleep = 100
|
|
||||||
) :
|
) :
|
||||||
_has_current_context(false),
|
_has_current_context(false),
|
||||||
_file_current_context(context),
|
_file_current_context(context),
|
||||||
_file_data(data),
|
_file_data(data),
|
||||||
_out(out),
|
_out(out)
|
||||||
_sleep(sleep)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string strip(std::string s)
|
std::string strip(std::string s)
|
||||||
|
|
@ -74,7 +71,6 @@ public:
|
||||||
this->has_current_context(true);
|
this->has_current_context(true);
|
||||||
std::clog << "\tReceived context: " << _current_context << std::endl;
|
std::clog << "\tReceived context: " << _current_context << std::endl;
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(_sleep));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +105,6 @@ public:
|
||||||
std::clog << "\tdone" << std::endl;
|
std::clog << "\tdone" << std::endl;
|
||||||
} // if not has_error
|
} // if not has_error
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(_sleep));
|
|
||||||
} // while true
|
} // while true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -137,7 +132,7 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
std::clog << "Start server" << std::endl;
|
std::clog << "Start server" << std::endl;
|
||||||
std::clog.flush();
|
std::clog.flush();
|
||||||
Service server(argv[1], argv[2], argv[3], 100);
|
Service server(argv[1], argv[2], argv[3]);
|
||||||
|
|
||||||
std::thread do_current_context(&Service::update_current_context, &server);
|
std::thread do_current_context(&Service::update_current_context, &server);
|
||||||
std::thread do_tasks(&Service::handle_data, &server);
|
std::thread do_tasks(&Service::handle_data, &server);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue