2010-08-26 5 views
5

Ce code est identique au serveur original async echo d'udp, mais avec un socket différent.Pourquoi ce code Boost ASIO ne fonctionne-t-il pas avec ce client python?

La réponse est transmise et affichée dans wireshark, mais une erreur ICMP Port Unreachable est renvoyée au serveur. J'essaie de comprendre pourquoi parce que tout semble correct.

Vous pouvez copier ce code directement dans un fichier source, par ex. server.cpp. puis compiler avec

gcc server.cpp -lboost_system

Exécuter avec une commande comme: ./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; 
} 

La raison pour laquelle j'ai besoin c'est parce que j'ai plusieurs threads de réception de données à partir d'une file d'entrée alimentée par un serveur UDP. Maintenant, je veux que ces threads puissent envoyer des réponses directement, mais je ne peux pas le faire fonctionner.

Si j'utilise le socket d'origine (c'est-à-dire socket_) dans l'appel async_send_to alors cela fonctionne.

Ok ... voici le client de test qui ne fonctionne pas avec le code ci-dessus (mais qui fonctionne avec la version originale des exemples 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 

Cela me déroute. Mais au moins, je peux utiliser le client C++ UDP et cela fonctionne.

+0

Certains ports sont protégés par le système d'exploitation. D'autres ports sont déjà utilisés. Si vous avez tué le serveur de force, le système d'exploitation peut ne pas remarquer pendant soixante secondes que le port est pratiquement mort et que le port sera inutilisable jusqu'à ce qu'il soit nettoyé par le système d'exploitation. Quel numéro de port essayez-vous d'utiliser? –

+0

35200 ce n'est pas la raison. Si je change de handle_receive_from de socket vers la variable membre socket_ alors cela fonctionne. – Matt

+0

J'ai mis à jour ma réponse en fonction de votre code client python. –

Répondre

0

C'est parti. Je répète ma propre question à nouveau. Le problème se rapporte à mon code python qui était un échantillon que j'ai attrapé de quelqu'un d'autre.

Cette version fonctionne mieux et lit le résultat correctement. Et, utilise l'API correcte sendto recvfrom qui est ce que vous utiliseriez normalement avec les paquets 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() 

L'autre chose est que le Ben a souligné dans sa réponse que, à un moment donné, je créais un socket qui a été plus tard être supprimé que la fonction est allé hors de portée et il y avait encore en suspens I/O. J'ai décidé qu'il y a peu d'avantages dans mon cas à utiliser des E/S asynchrones car cela complique inutilement le code et n'affectera pas beaucoup les performances.

3

Vous ne devez pas suspendre un envoi asynchrone, puis fermez le socket. Le destructeur de socket s'exécute à la fin du bloc, fermant le socket, ce qui empêche l'envoi de se produire.

+0

Merci d'avoir signalé cela. J'ai modifié le code qui ne fonctionne toujours pas. L'ASIO est-elle brisée? – Matt

+0

BTW même résultat si vous utilisez send_to synchrone. – Matt

-1

Modifier

Votre code client python semble suspect, je ne pense pas que vous devriez faire un connect ou un send en utilisant un socket UDP. Essayez ceci:

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

cela fonctionne pour moi en utilisant votre server.cpp

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

originale réponse ci-dessous.

host unreachable est ce que je m'attendrais si le port sender_endpoint_ n'est pas ouvert sur le client ayant envoyé le message. Quand je compilé votre server.cc et utiliser la Boost.Asio blocking udp echo client example, il fonctionne très bien

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 

dans une autre coquille

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

hmmm, c'est bizarre. Je l'ai essayé avec le client UDP aussi. Je pense que cela pourrait être quelque chose à voir avec le script de test python que j'utilise. – Matt

+0

Mettre à jour sa réponse avec la solution d'un autre n'est pas vraiment poli! – schlamar

0

Ok, une possibilité tout à fait différente.

Exécutez-vous netfilter? Avez-vous une règle de conntrack?

Une réponse du même port correspond à CONNECTED dans le module conntrack, tandis qu'une réponse provenant d'un nouveau port apparaît comme une nouvelle connexion. Si les paquets UDP entrants qui ne correspondent pas à CONNECTED ont une action REJECT, cela expliquerait le comportement, ainsi que la raison pour laquelle le même code pourrait fonctionner pour Sam.

Questions connexes