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

发布于 2024-09-16 04:48:29 字数 3675 浏览 2 评论 0原文

此代码与原始 udp 异步回显服务器相同,但具有不同的套接字。

响应已传输并显示在wireshark 中,但随后会向服务器发送回ICMP 端口不可达错误。我试图理解为什么,因为一切看起来都是正确的。

您可以将此代码直接复制到源文件中,例如 server.cpp。然后编译

gcc server.cpp -lboost_system

使用如下命令运行它: ./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;
}

我需要这个的原因是因为我有多个线程从由 UDP 服务器提供的输入队列接收数据。现在我希望这些线程能够直接发送响应,但我无法让它工作。

如果我在 async_send_to 调用中使用原始套接字(即 socket_),那么它就可以工作。

好的...这是测试客户端,它不适用于上面的代码(但适用于 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

让我很困惑。但至少我可以使用 C++ UDP 客户端并且它可以工作。

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";
      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. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绻影浮沉 2024-09-23 04:48:29

您不应该挂起异步发送然后关闭套接字。套接字的析构函数在块的末尾运行,关闭套接字,从而防止发送发生。

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.

许你一世情深 2024-09-23 04:48:29

好吧,完全不同的可能性。

你正在运行netfilter吗?你有 conntrack 规则吗?

来自同一端口的回复将与 conntrack 模块中的 CONNECTED 匹配,而来自新端口的回复将显示为新连接。如果与 CONNECTED 不匹配的传入 UDP 数据包有 REJECT 操作,它将解释该行为,以及为什么完全相同的代码可以为 Sam 工作。

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.

调妓 2024-09-23 04:48:29

开始了。我再次回答我自己的问题。该问题与我的 python 代码有关
这是我从别人那里拿来的样本。

这个版本可以更好地工作整个堆并正确读取结果。并且,正在使用正确的 API sendto recvfrom,这是您通常与 udp 数据包一起使用的 API。

#!/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()

另一件事是,正如 Ben 在他的回答中指出的那样,有一次我正在创建一个套接字,后来由于函数超出范围而被删除,并且它仍然有挂起的 I/O。我认为在我的例子中使用异步 I/O 没有什么好处,因为它不必要地使代码复杂化并且不会对性能产生太大影响。

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.

送君千里 2024-09-23 04:48:29

编辑

您的Python客户端代码看起来很可疑,我认为您不应该使用UDP套接字执行connectsend。试试这个:

#!/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

它对我有用,使用

macmini:stackoverflow samm$ ./client.py 
Sending Data
Looking for replies; press Ctrl-C or Ctrl-Break to stop.
Received: Hello World

下面的 server.cpp 原始答案。

如果发送消息的客户端没有打开 sender_endpoint_ 端口,我会期望 host unreachable 。当我编译你的 server.cc 并使用 Boost.Asio

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

"nofollow noreferrer">阻止 udp echo 客户端示例,它在另一个 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$ 

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$ 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文