I need to create a C++ FastCGI server and connect to it in the simplest way possible in order to test/develop incrementally.
I have both an issue conceptually and with the lack of API documentation for FastCGI. My understanding is that when I do
FCGX_Request request;
FCGX_Init();
FCGX_InitRequest(&request, 0, 0);
while (FCGX_Accept_r(&request) == 0) {
}
This opens a socket and listens on it for requests. Then, using boost::asio, I want to do something like
boost::asio::io_service io_service;
boost::system::error_code error;
using boost::asio::ip::tcp;
tcp::resolver resolver(io_service);
tcp::resolver::query query("localhost", "" /* WTF are the params here? */);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
while (true)
{
boost::array<char, 128> buf;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error)
break;
}
CPPUNIT_ASSERT_EQUAL(error, (boost::system::error_code)boost::asio::error::eof);
I.e. to programmatically send a request to the FastCGI process to test it's working.
Anyway, I believe that tcp::resolver::query is the key to setting up communication from my simple unit test.
However,
I don't fully understand what a 'socket' is in this case. I've read that it's just a file (on linux)?
Given this, how do I know what socket FastCGI is listening on?
How do I specify the socket to boost::asio?
How do I stop the FastCGI process from listening once the test is done?
I'm possibly going about this all wrong, so any hints on the proper way to approach this simple test case would be gratefully accepted. Also, if anyone knows where the hell the API documentation for fcgi/fcgi++/fastcgi++ is, that would be awesome.
Your question is really only about elementary networking.
Edit Check out this most valued internet resource on network programming (basics to advanced):
What is a socket? from Beej's network programming guide
A socket is a connection. A server is listening on a /port/ (TCP or UDP. Assuming
TCP for the rest, since you're doing CGI).
Clients connect to the port and the server accepts the connection, resulting in a socket (bidirectional stream, so to speak). The client now (usually) sends and the server receives the request. Then the server sends the response. (The connection may be closed, or kept alive depending on the protocol/preferences).
Specific answers:
query("localhost", "http");
would end up connecting to port 80 on most systems.
Of course you can also just specify the port number there:
query("localhost", "8080");
Choose the port number that the server bound it's listener to.
Related
I am a little confused about which type of resolver I should use for a side project I am working on. I am not finding the answer in the asio documentation.
I know that DNS can work with both UDP or TCP and that larger responses are generally sent over TCP.
asio offers both ip::tcp::resolver and ip::udp::resolver.
Can I use them interchangeably?
After I have resolved the name to an endpoint, I plan to connect with
a TCP socket. Does that mean I have to use a ip::tcp::resolver?
If there are in fact interchangeable:
Is there a performance benefit to using the UDP resolver?
Is there a some other benefit to using the TCP resolver?
If I use UDP resolver, do I need to deal with the response being too large for the UDP lookup and retry with TCP? (I expect to connect to a CDN that will resolve to multiple IP addresses per host)
Use the resolver that has the same protocol as the socket. For example, tcp::socket::connect() expects a tcp::endpoint, and the endpoint type provided via udp::resolver::iterator is udp::endpoint. Attempting to directly use the result of the query from a different protocol will result in a compilation error:
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::ip::udp::resolver::iterator iterator = ...;
socket.connect(iterator->endpoint());
// ~~~^~~~~~~ no matching function call to `tcp::socket::connect(udp::endpoint)`
// no known conversion from `udp::endpoint` to `tcp::endpoint`
Neither tcp::resolver nor udp::resolver dictate the transport layer protocol the name resolution will use. The DNS client will use TCP when either it become necessary or it has been explicitly configured to use TCP.
On systems where service name resolution is supported, when performing service-name resolution with a descriptive service name, the type of resolver can affect the results. For example, in the IANA Service Name and Transport Protocol Port Number Registry:
the daytime service uses port 13 on UDP and TCP
the shell service uses port 514 only on TCP
the syslog service uses port 514 only on UDP
Hence, one can use tcp::resolver to resolver the daytime and shell service, but not syslog. On the other hand, udp::resolver can resolve daytime and syslog, but not shell. The following example demonstrates this distinction:
#include <boost/asio.hpp>
int main()
{
boost::asio::io_service io_service;
using tcp = boost::asio::ip::tcp;
using udp = boost::asio::ip::udp;
boost::system::error_code error;
tcp::resolver tcp_resolver(io_service);
udp::resolver udp_resolver(io_service);
// daytime is 13/tcp and 13/udp
tcp_resolver.resolve(tcp::resolver::query("daytime"), error);
assert(!error);
udp_resolver.resolve(udp::resolver::query("daytime"), error);
assert(!error);
// shell is 514/tcp
tcp_resolver.resolve(tcp::resolver::query("shell"), error);
assert(!error);
udp_resolver.resolve(udp::resolver::query("shell"), error);
assert(error);
// syslog is 514/udp
tcp_resolver.resolve(tcp::resolver::query("syslog"), error);
assert(error);
udp_resolver.resolve(udp::resolver::query("syslog"), error);
assert(!error);
tcp_resolver.resolve(tcp::resolver::query("514"), error);
assert(!error);
udp_resolver.resolve(udp::resolver::query("514"), error);
assert(!error);
}
When I'm working on a UDP server I usually set the socket to listen on specified port and accept any IP. Keep in mind that sync receive works properly here.
std::unique_ptr<boost::asio::ip::udp::socket> socketUDP;
socketUDP.reset(new udp::socket(io_serviceUDP, udp::endpoint(udp::v4(), 9999)));
However, I would really like to have 2 different server applications listening at the same port (9999) but accepting only a single IP (I already know that IP). Each of the apps has its own client with its own IP.
But for some reason the following doesn't work (not receiving any packets in the app, while Wireshark can see it)
socketUDP.reset(new udp::socket(m_io_serviceUDP, udp::endpoint(asio::ip::address::from_string("169.254.1.2"), 9999)));
Please note :
1) According to the answer for : Issue with broadcast using Boost.Asio this should actually work. Of course my understanding isn't quite correct as I'm missing something
2) The provided IP is valid, works, sends data(confirmed by wireshark) and can be pinged
The issue is that your socketUDP's are not configured with:
set_option(boost::asio::ip::udp::socket::reuse_address(true));
However, simply calling the line above on your sockets won't work, because you must call reuse_address before the socket is bound to an endpoint... But you're constructing udp::socket with an endpoint which opens it and binds it to the endpoint, see basic_datagram_socket.
The solution is to call the constructor which just takes an io_service; open it, set the reuse_addressoption and then bind it, e.g.:
// construct the socket
boost::asio::ip::udp::socket socket_(io_service_);
// open it
boost::asio::ip::udp::endpoint rx_endpoint_(udp::v4(), 9999);
socket_.open(rx_endpoint_.protocol(), error);
if (error)
return false;
// then set it for reuse and bind it
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(rx_endpoint_, error);
if (error)
return false;
// set multicast group, etc.
socket_.set_option(boost::asio::ip::multicast::join_group(ip_address));
...
I saw the official async udp server example from boost doc.
There you create a single udp socket, bind it to a local port and do something like this:
socket(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))
socket.async_receive_from(buffer(data, max_length), senderEndpoint, boost::bind(&Request::HandleReceiveFrom, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
How can I handle multiple concurrent udp connections from clients, because if I try to create another socket using
socket(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))
i get bind errors because I already have a socket bound to the same port.
EDIT
I can send clients back responses using a different server source port but they will not recognize the response even though I sent the response back to the same client ip/client destination port.
UDP is a connectionless transport so the concept of connections is meaningless as far as UDP is concerned.
If you want to send data back to the originator of the message you'll need to keep a copy of the sender_endpoint returned in the async_receive_from callback and pass it back in the async_send_to.
This also assumes that the client is also polling/reading and expecting a reply. It need not listen on the same bound port as the server (you don't need to bind with UDP on the client side).
I recommend you have a read of Beej's guide to network programming to help you understand what's going on under the hood of boost ASIO. Boost ASIO complicates things a lot IMHO.
http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#sendtorecv
If your clients send their messages to the same port on your server then the only way to distinguish them is by the remote port or a combination of remote port and remote ip.
You create some sort of mapping from client-id (e.g. pair<remote_ip, remote_port>) to a dispatcher type (e.g map< pair<remote_ip, remote_port>, dispatcher>). This then it's up to you to make it threaded in order to support concurrent requests.
As it seems the solution can be to use the same socket for sending back responses to clients. Have a look at this question response: Use same udp socket for async receive/send
I'm trying to create an HTTP client using Boost Asio. I copied the sync client example from asio, compile, then run. Unfortunately, on my logs, it shows Service not found. When I traced the code, I found it that it is thrown from the following code:
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
//->if i removed the http, it has no error
tcp::resolver::query query("host.com", "http");
//->This part throws the service not found
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
Can anybody explain why it is throwing service not found or on how can I solve this?
This means the OS does not know which port number corresponds to the TCP service with the name "http".
On a unix-like OS, this would mean the line http 80/tcp is missing from /etc/services, I am able to reproduce the error on Linux by commenting that line out.
If the OS cannot be configured to use services, you may use any service "" in the resolver, and specify the port number explicitly when creating the endpoint object for the connect call:
tcp::endpoint connectionEndpoint(endpoint_iterator->address(), 80);
boost::system::error_code ec;
socket.connect(connectionEndpoint, ec);
it is a little bit strange to me that boost.asio doesn`t use basic concept when client app connecting to the server - using IP address and port. May be I am a little bit noobie in Boost - and I accept that - but anyway I do not understand.
So, I have code like this to get client connected to the server on the localhost:
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query("localhost", "daytime");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while(error && endpoint_iterator != end) {
socket.close();
socket.connect(*endpoint_iterator++, error);
}
Windows in its WinSock 2.0 uses two parameters - IP and port - to identify the server. So, the qurestion is - how exactly Asio finds out which port is server listening to connections on? Does it scans all ports? And, if it does, what will happen if two servers listening on different ports at the same time?
Try,
tcp::resolver::query query("localhost", boost::lexical_cast<string>(port));//assuming port is an int
To answer your question, recall that you are starting the server on port 13. This happens to be the port which runs the Linux daytime service (http://www.sorgonet.com/linux/linuxdaemons/). Hence, they are subsequently able to use query("localhost","daytime") rather than specifying the port.
You are telling it that you want to connect to localhost on the port used by the daytime service. It will look up the appropriate port number in the services file (usually C:\WINDOWS\system32\drivers\etc\services under Windows, I believe /etc/services under Unix). You could also use an explicit port number there.
open netcat listen on port 13 on the localhost
it will accept the demo's connection. type some blabla when it connects and you'll see the output on the demo program
to run the netcat, run:
nc -l -p 13
windows? no netcat? install cygwin, and add netcat