I try to implement a simple http server with blocking sockets on Windows. Basically, I have a simple server that just write data to a socket when a network connection occurs before exit. The problem is that the last socket.send as no effect if I don't delay the process exit. Writing to this socket is supposed to block until all the data as been written.
I have tried to use the completion condition of write, to use the non_blocking method of the socket. I still get the same problem.
Note that the problem doesn't occur on Linux.
Here is the code:
#include <boost/asio.hpp>
int main(int argc, char *argv[]) {
char *address = "0.0.0.0";
char *port = "8180";
boost::asio::io_service io_service;
boost::asio::ip::tcp::acceptor acceptor(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor.open(endpoint.protocol());
acceptor.bind(endpoint);
acceptor.listen();
boost::asio::ip::tcp::socket sock(io_service);
acceptor.accept(sock);
std::string body("Hello, World!");
sock.send(boost::asio::buffer(std::string("HTTP/1.1 200 OK\r\n")));
sock.send(boost::asio::buffer(std::string("Content-Length: ") + std::to_string(body.size()) + "\r\n\r\n"));
sock.send(boost::asio::buffer(body));
Sleep(1000); // The body would not be sent without this
return 0;
}
According to this post, on windows the send method will block only if the kernel runs out of socket buffers.
It also say that if the program is killed, the sockets are forcibly closed and the non sent data is discarded.
I wanted to add this as a comment but I don't have enough point, sorry about that.
Related
I have no knowledge in node.js.
I am sending UDP packets in C++ and I want to show them in node.js.
The problem is that both try to occupy the IP/port. Depending on which one I run, the other one fails. How should I fix this problem?
main.cpp
// g++ main.cpp -o main -std=c++11 -lboost_system -pthread
#include <iostream>
#include <stdio.h>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
int main(int argc, char* argv[])
{
const int port=1414;
const std::string ip="127.0.0.1";
boost::asio::io_service io_service;
udp::resolver resolver(io_service);
udp::endpoint client_endpoint = *resolver.resolve({udp::v4(), ip, std::to_string(port)});
udp::socket socket(io_service, udp::endpoint(udp::v4(), port));
boost::asio::socket_base::reuse_address option(true);
socket.set_option(option);
std::string data;
for(long i=0;true;i++)
{
data=std::to_string(i+1)+"th packet";
socket.send_to(boost::asio::buffer(data.c_str(), data.length()+1), client_endpoint);
usleep(10);
}
return 0;
}
app.js
var PORT = 1414;
var HOST = '127.0.0.1';
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
server.on('listening', function () {
var address = server.address();
console.log('UDP Server listening on ' + address.address + ":" + address.port);
});
server.on('message', function (message, remote) {
console.log(remote.address + ':' + remote.port +' - ' + message);
});
server.bind(PORT, HOST);
If I run the C++ file and then the js one, the error is this:
events.js:160
throw er; // Unhandled 'error' event
^
Error: bind EADDRINUSE 127.0.0.1:1414
at Object.exports._errnoException (util.js:1012:11)
at exports._exceptionWithHostPort (util.js:1035:20)
at dgram.js:221:18
at _combinedTickCallback (internal/process/next_tick.js:77:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
at Module.runMain (module.js:577:11)
at run (bootstrap_node.js:352:7)
at startup (bootstrap_node.js:144:9)
at bootstrap_node.js:467:3
While, if I run the js file first and then the C++ one, I receive:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): bind: Address already in use
Aborted (core dumped)
What #mscdex wrote is correct, but what #EJP's suggested is a more standard way of doing what you're (I believe) trying to accomplish.
That is, you normally have only the server on a well known port such as 1414 and clients usually start on any random port that is not already used.
To do that, you don't need to change your js code, and only change this part of c++ code:
udp::endpoint client_endpoint
= *resolver.resolve({udp::v4(), ip, std::to_string(port)});
udp::socket socket(io_service, udp::endpoint(udp::v4(), port));
boost::asio::socket_base::reuse_address option(true);
socket.set_option(option);
to:
udp::endpoint client_endpoint
= *resolver.resolve({udp::v4(), ip, std::to_string(port)});
// Passing 0 as the port argument will tell the system to
// pick a random port for you.
udp::socket socket(io_service, udp::endpoint(udp::v4(), 0));
You need to set reuseAddr: true when creating your socket:
var server = dgram.createSocket({ type: 'udp4', reuseAddr: true });
You will also need to change how you create your socket on the C++ side. Currently the udp::socket constructor signature you're using will cause the socket to bind immediately. Unfortunately, you need to set the reuse_address option first before binding. To do that, try this intead:
udp::socket socket(io_service, udp::v4());
socket.set_option(boost::asio::socket_base::reuse_address(true));
socket.bind(udp::endpoint(udp::v4(), port));
I'm developing a tool which is analyzing video stream.
I've worked with a file, I generate the file with this socat command (that someone's given to me):
socat -u UDP4-RECV:1234,ip-add-membership=xxx.xxx.xxx.xxx:0.0.0.0 CREATE:temp.ts
But now I'd like to work directly with the UDP stream.
With this code, I've tried to read the first received block and write it on the console, but I don#t get anything - the program gets stuck...
void Decoder::open_udp_stream(std::string ip_adress)
{
boost::asio::io_service io_service;
udp::endpoint receiver_endpoint (boost::asio::ip::address::from_string("xxx.xxx.xxx.xxx"), 1234);
udp::socket socket(io_service);
socket.open(udp::v4());
boost::array<char, 128> recv_buf;
udp::endpoint sender_endpoint;
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf.data(), len);
}
What should I do to get the blocks from this IP, as with the socat command ?
This code is working (thanks David Schwarz)
void Decoder::open_udp_stream(std::string ip_adress)
{
boost::asio::io_service io_service;
boost::asio::ip::udp::socket socket_(io_service);
boost::asio::ip::udp::endpoint sender_endpoint;
// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
boost::asio::ip::address::from_string("0.0.0.0"), 1234);
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// Join the multicast group.
socket_.set_option(
boost::asio::ip::multicast::join_group(boost::asio::ip::address::from_string("xxx.xxx.xxx.xxx")));
boost::array<char, BUF_SIZE> recv_buf;
size_t len = socket_.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf.data(), len);
}
I have a server running very heavy 3D simulations that I want to display in real time on a client machine. For now I am running my tests in localhost to get rid of the network brandwidth and latency issues, and I use boost::asio to stream my data (geometry) through the network.
I have to use tcp because I have to compress my geometry, split it into multiple packages and then send it through the network, and on the client, gather the packages to rebuild my archive, so network packages have to arrive in the good order.
This works pretty well, I can run my simulation and stream my data at ~90-120fps, depending on the quantity of data to stream, which is very good.
My problem is that sometimes, it suddenly takes ~1second for the socket to connect() on the client, and consequently as much time for the server's to accept(). This causes my simulation to stop being streamed randomly, and I can't find the problem.
I though the problem could come from some kind of buffer overflow on the socket, preventing the server to write more data as long as the client didn't read some, but it can't be that, since I have no latency between the client and the server, so the client reads the packages fast enough (as soon as they arrive, basically)
Here's a shortened piece of code for the server:
while (1)
{
//archive some data in a stringstream using boost::archive...
boost::asio::io_service ioservice;
tcp::acceptor acceptor(ioservice, tcp::endpoint(tcp::v4(), PORT));
boost::system::error_code ignored_error;
tcp::socket socket(ioservice);
acceptor.accept(socket);
gettimeofday(&m_tv, NULL);
accept += (m_tv.tv_usec - m_timer);
m_timer = m_tv.tv_usec;
size_t bytes_sent = boost::asio::write(socket, boost::asio::buffer(ss.str()), boost::asio::transfer_all(), ignored_error);
}
and on the client I get something like:
while (1)
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(IP, PORT);
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);
}
if (error)
throw boost::system::system_error(error);
while(1)
{
boost::array<char, 200000> buf;
ss.write(buf.data(), bytes_received);
boost::system::error_code error;
bytes_received = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break;
else if (error)
throw boost::system::system_error(error);
}
}
I create a socket every frame, which is probably the problem, but I couldn't find an easier way of telling my client that he finished reading the package. By closing the socket every frame, I send eof to the client who then knows that he can build the archive using the data retrieved.
Is there something I can do to avoid opening a socket every frame, without having to check the content of my packages to know the size of the data to retrieve?
Im following the tutorials at the boost official web site http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/tutorial/tutdaytime1.html.
The program is working perfectly if i connect to "localhost" or "127.0.0.1" on the same machine. But if i run the client on another computer with the same network it fails to connect to the server. Why is this happening? and what would i have to do to get the client to run on another network?
Error: connect: No connection could be made because the target machine actively refused it.
Client:
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
char* serverName = "localhost";
tcp::resolver::query query(serverName, "daytime");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
while(true)
{
boost::asio::connect(socket, endpoint_iterator);
for (;;)
{
boost::array<char, 128> buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout.write(buf.data(), len);
std::cout <<"\n";
}
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Server:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
std::string message = "This is the Server!";
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I would guess your problem might be that you return on the first error. Resolving gives you an iterator on a number of endpoints. You try the first of those and if it does not work out you give up instead of letting the iterator go on.
Again, i am by no means an expert in boost::asio and far less in its TCP world but resolve may return more than one endpoint (for example IPv4 and IPv6) and possibly only one of them does not work out here.
For testing you could create the endpoint yourself by first creating a ip::address object, using its from_string() method to give it the address of the server (works only on your local network of course) and then using it for your endpoint:
boost::asio::ip::address address;
address.from_string("the.servers.ip.here");
boost::asio::ip::tcp::endpoint endpoint(address, 13);
boost::asio::connect(socket, endpoint);
And see if that works. If not, it probably is a problem on the server side.
To run the server and client on separate networks, Make the client connect to the servers external ip address. This is obvious but external ip addresses constantly change so to solve this problem you can go to www.noip.com and create a name that links to your ip address. This way in the client all you have to do is specify a name instead of an ip address.
most likely firewall issue, if you are using windows for server check windows firewall, if you are using linux, check the iptables.
I am using boost asio to create a test server to test an http client. This will run on the same machine as the client. Now what I want to do is create a server with a randomly assigned port. I have looked at this thread here: Using boost::asio is there a portable way to find out free port number but I'm frankly still a little baffled.
My code looks something like this:
boost::asio::io_service service;
tcp::acceptor acceptor(service);
unsigned short port(0);
tcp::endpoint endPoint(tcp::endpoint(tcp::v4(), port);
acceptor.open(endPoint.protocol());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.bind(endPoint);
port = endPoint.port();
std::cout<<port<<std::endl; // prints 0
My thoughts were that by creating an endpoint with 'port 0' and then trying to bind to that port, it should cause an available port to be randomly assigned but this doesn't seem to be the case. Any idea what I'm doing wrong?
Cheers.
boost::asio::io_service service;
boost::asio::ip::tcp::acceptor acceptor(service);
unsigned short port(0);
boost::asio::ip::tcp::endpoint endPoint(tcp::endpoint(tcp::v4(), port);
acceptor.open(endPoint.protocol());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.bind(endPoint);
m_acceptor.listen();
boost::asio::ip::tcp::endpoint le = acceptor.local_endpoint(); //THIS LINE SOLVES IT
port = le.port();
// port = endPoint.port(); // This is didn't work for me
Helpful answer
Similar question
You can shorten this by using a different constructor for the acceptor:
using boost::asio;
io_service service;
ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 0));
unsigned short port = acceptor.local_endpoint().port();
This constructor calls open(), bind() and listen() on the acceptor.
Fixed. I needed to do:
boost::asio::io_service service;
tcp::acceptor acceptor(service);
unsigned short port(0);
tcp::endpoint endPoint(tcp::endpoint(tcp::v4(), port);
acceptor.open(endPoint.protocol());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.bind(endPoint);
m_acceptor.listen(); // NEEDED TO ADD THIS BIT!
port = endPoint.port();
std::cout<<port<<std::endl; // prints 0