简体   繁体   English

为什么这个Boost ASIO代码不能与这个python客户端一起使用?

[英]Why doesn't this Boost ASIO code work with this python client?

This code is identical to the original udp async echo server, but with a different socket. 此代码与原始udp异步回显服务器相同,但具有不同的套接字。

The response is transmitted and showing in wireshark, but then an ICMP Port Unreachable error is sent back to the server. 响应将在wireshark中传输并显示,但随后会将ICMP Port Unreachable错误发送回服务器。 I'm trying to understand why because everything looks correct. 我试图理解为什么因为一切看起来都正确。

You can copy this code directly into a source file eg server.cpp. 您可以将此代码直接复制到源文件中,例如server.cpp。 and then compile with 然后用。编译

gcc server.cpp -lboost_system gcc server.cpp -lboost_system

Run it with a command like: ./a.out 35200 使用如下命令运行它: ./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";
      return 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. 我需要这个的原因是因为我有多个线程从输入UDP服务器的输入队列接收数据。 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 (ie socket_) in the async_send_to call then it works. 如果我在async_send_to调用中使用原始套接字(即socket_),那么它可以工作。

Ok... here is the test client that doesn't work with the code above (but works with the original version from the asio examples). 好的......这里的测试客户端不能使用上面的代码(但是使用asio示例中的原始版本)。

#!/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. 但至少我可以使用C ++ UDP客户端,它的工作原理。

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? 你在运行netfilter吗? Do you have a conntrack rule? 你有conntrack规则吗?

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. 来自同一端口的回复将与conntrack模块中的CONNECTED匹配,而来自新端口的回复似乎是新连接。 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. 如果与CONNECTED不匹配的传入UDP数据包具有REJECT操作,它将解释该行为,以及为什么完全相同的代码可以为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. 问题与我的python代码有关,这是我从其他人那里抓取的一个示例。

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. 而且,正在使用正确的API sendto recvfrom,这通常与udp数据包一起使用。

#!/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. 另一件事是,正如Ben在他的回答中指出的那样,我曾经创建了一个套接字,后来由于函数超出范围而被删除,并且它仍然有待处理的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. 我已经决定使用异步I / O在我的情况下几乎没有什么好处,因为它不必要地使代码复杂化并且不会对性能产生太大影响。

Edit 编辑

Your python client code looks suspicious, I don't think you should be doing a connect or a send using a UDP socket. 您的python客户端代码看起来很可疑,我认为您不应该使用UDP套接字进行connectsend 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 它适用于我使用您的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. 如果发送消息的客户端没有打开sender_endpoint_端口,那么host unreachable是我所期望的。 When I compiled your server.cc and use the Boost.Asio blocking udp echo client example , it works just fine 当我编译你的server.cc并使用Boost.Asio 阻塞udp echo客户端的例子时 ,它工作得很好

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 在另一个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$ 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM