This code is identical to the original udp async echo server, but with a different socket.
The response is transmitted and showing in wireshark, but then an ICMP Port Unreachable error is sent back to the server. I'm trying to understand why because everything looks correct.
You can copy this code directly into a source file e.g. server.cpp. and then compile with
gcc server.cpp -lboost_system
Run it with a command like: ./a.out 35200
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
socket_(io_service, udp::endpoint(udp::v4(), port)),
socket2_(io_service, udp::endpoint(udp::v4(),0))
{
socket_.async_receive_from(
boost::asio::buffer(data_, max_length), sender_endpoint_,
boost::bind(&server::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_receive_from(const boost::system::error_code& error,
size_t bytes_recvd)
{
if (!error && bytes_recvd > 0)
{
// use a different socket... random source port.
socket2_.async_send_to(
boost::asio::buffer(data_, bytes_recvd), sender_endpoint_,
boost::bind(&server::handle_send_to, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
socket_.async_receive_from(
boost::asio::buffer(data_, max_length), sender_endpoint_,
boost::bind(&server::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
void handle_send_to(const boost::system::error_code& /*error*/,
size_t /*bytes_sent*/)
{
// error_code shows success when checked here. But wireshark shows
// an ICMP response with destination unreachable, port unreachable when run on
// localhost. Haven't tried it across a network.
socket_.async_receive_from(
boost::asio::buffer(data_, max_length), sender_endpoint_,
boost::bind(&server::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
boost::asio::io_service& io_service_;
udp::socket socket_;
udp::socket socket2_;
udp::endpoint sender_endpoint_;
enum { max_length = 1024 };
char data_[max_length];
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_udp_echo_server <port>\n";
retu开发者_开发技巧rn 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
The reason I need this is because I have multiple threads receiving data from an input queue that is fed with a UDP server. Now I want those threads to be able to send responses directly but I can't get it working.
If I use the original socket (i.e. socket_) in the async_send_to call then it works.
Ok... here is the test client that doesn't work with the code above (but works with the original version from the asio examples).
#!/usr/bin/python
import socket, sys, time, struct
textport = "35200"
host = "localhost"
if len(sys.argv) > 1:
host = sys.argv[1]
print "Sending Data"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
port = int(textport)
s.connect((host, port))
s.sendall("Hello World")
#s.shutdown(1)
print "Looking for replies; press Ctrl-C or Ctrl-Break to stop."
while 1:
buf = s.recv(1200)
if not len(buf):
break
print "Received: %s" % buf
It's got me baffled. But at least I can use the C++ UDP client and it works.
You shouldn't pend an asynchronous send and then close the socket. The destructor for socket runs at the end of the block, closing the socket, which prevents the send from ever occurring.
Ok, a completely different possibility.
Are you running netfilter? Do you have a conntrack rule?
A reply from the same port would match CONNECTED in the conntrack module, while a reply from a new port would appear to be a new connection. If incoming UDP packets which don't match CONNECTED have a REJECT action, it would explain the behavior, as well as why the exact same code could work for Sam.
Here we go. I'm answering my own question again. The problem relates to my python code which was a sample I grabbed from someone else.
This version works a whole heap better and reads the result correctly. And, is using the correct API sendto recvfrom which is what you would normally use with udp packets.
#!/usr/bin/python
import socket, sys, time, struct
textport = "35200"
host = "localhost"
if len(sys.argv) > 1:
host = sys.argv[1]
print "Sending Data"
port = int(textport)
addr = (host, port)
buf = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto("hello World", addr)
print "Looking for replies; press Ctrl-C or Ctrl-Break to stop."
while 1:
data,addr = s.recvfrom(buf)
if not data:
print "Client has exited!"
break
else:
print "\nReceived: '", data,"'"
# Close socket
s.close()
The other thing is, that the as Ben has pointed out in his answer that at one point I was creating a socket that was later being deleted as the function went out of scope and it still had pending I/O. I have decided that there is little benefit in my case to use asynchronous I/O as it unnecessarily complicates the code and won't affect performance much.
Edit
Your python client code looks suspicious, I don't think you should be doing a connect
or a send
using a UDP socket. Try this:
#!/usr/bin/python
import socket, sys, time, struct
port = 10000
host = "localhost"
addr = (host,port)
if len(sys.argv) > 1:
host = sys.argv[1]
print "Sending Data"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto("Hello World",addr)
print "Looking for replies; press Ctrl-C or Ctrl-Break to stop."
while 1:
data,addr = s.recvfrom(1200)
if not data:
break
print "Received: %s" % data
it works for me using your server.cpp
macmini:stackoverflow samm$ ./client.py
Sending Data
Looking for replies; press Ctrl-C or Ctrl-Break to stop.
Received: Hello World
original answer below.
host unreachable
is what I would expect if the client that sent the message does not have the sender_endpoint_
port open. When I compiled your server.cc and use the Boost.Asio blocking udp echo client example, it works just fine
macmini:stackoverflow samm$ g++ server.cpp -lboost_system -o server
macmini:stackoverflow samm$ g++ client.cpp -lboost_system -o client
macmini:stackoverflow samm$ ./server 10000
in another shell
macmini:stackoverflow samm$ ./client 127.0.0.1 10000
Enter message: hello
Reply is: hello
macmini:stackoverflow samm$ ./client 127.0.0.1 10000
Enter message: goodbye
Reply is: goodbye
macmini:stackoverflow samm$
精彩评论