2010-08-26 106 views
5

此代碼與原始的udp異步回顯服務器相同,但使用不同的套接字。爲什麼Boost ASIO代碼不能用於這個python客戶端?

響應被傳輸並顯示在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客戶端,它的工作原理。

+0

某些端口受OS保護。其他端口已被使用。如果您強行殺死​​了服務器,操作系統可能會在60秒內未注意到該端口已基本死亡,因此該端口在OS清理之前將無法使用。您嘗試使用哪個端口號。 – 2010-08-26 00:06:32

+0

35200那不是原因。如果我將handle_receive_from從套接字更改爲成員變量socket_,那麼它將起作用。 – Matt 2010-08-26 00:11:00

+0

我已經根據您的python客戶端代碼更新了我的答案。 – 2010-08-26 12:34:44

回答

0

在這裏,我們走了。我正在回答我自己的問題。這個問題涉及我的python代碼 ,這是我從其他人那裏獲取的示例。

此版本更好地工作整個堆並正確讀取結果。並且,正在使用正確的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() 

的另一件事是,將作爲本已經指出了他的答案,在一個點,我創建作爲函數出去的範圍,它仍然有懸而未決我後來被刪除的插座/ O.我決定在我的情況下使用異步I/O幾乎沒有什麼好處,因爲它不必要地使代碼複雜化,並且不會影響性能。

3

您不應該掛起異步發送,然後關閉套接字。套接字的析構函數在塊的末尾運行,關閉套接字,從而防止發送。

+0

謝謝你指出。我修改了仍然不起作用的代碼。 ASIO是否壞了? – Matt 2010-08-26 00:31:38

+0

如果使用同步send_to調用,則結果相同。 – Matt 2010-08-26 00:36:01

-1

編輯

你的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爲我工作。

host unreachable是我所期望的,如果發送消息的客戶端沒有打開sender_endpoint_端口。當我編譯你的server.cc並使用Boost.Asio的blocking udp echo client example,它工作得很好

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 

在另一個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$ 
+0

嗯,這是奇怪的。我也試過用UDP客戶端。我認爲這可能與我使用的python測試腳本有關。 – Matt 2010-08-26 04:04:03

+0

用另一個解決方案更新他的答案並不是很有禮貌! – schlamar 2012-12-20 15:53:26

0

好了,一個完全不同的可能性。

你在運行netfilter嗎?你有conntrack規則?

來自同一端口的回覆將與conntrack模塊中的CONNECTED相匹配,而來自新端口的回覆將顯示爲新連接。如果與CONNECTED不匹配的傳入UDP數據包有REJECT操作,它將解釋行爲,以及爲什麼Sam完全可以使用完全相同的代碼。