Форум программистов, компьютерный форум, киберфорум
Наши страницы
Boost C++
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.62/13: Рейтинг темы: голосов - 13, средняя оценка - 4.62
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
1

Ip::udp::socket.async_receive_from. Как передать дополнительные аргументы в callback

14.10.2014, 00:19. Просмотров 2561. Ответов 32
Метки нет (Все метки)

Прив. Есть такой код

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdlib>
#include <iostream>
#include <boost\asio.hpp>
 
using namespace boost::asio;
 
io_service service;
ip::udp::socket sock(service);
char buff[1024];
 
 
void receive_callback(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
    std::cout << "Got " << bytes_transferred<< " bytes" <<std::endl;
    sock.async_receive(buffer(buff), receive_callback);
}
 
int main()
{
    try{
        ip::udp::endpoint endPoint(ip::udp::v4(), 9050);
        sock.open(ip::udp::v4());
        sock.bind(endPoint);
        ip::udp::endpoint senderEP;
        sock.async_receive_from(buffer(buff), senderEP, 0, receive_callback);
 
        service.run();
    }
    catch (std::exception ex)
    {
        std::cout <<"Error: " << ex.what() << std::endl; 
    }
 
    system("pause>>void");
    return 0;
}
Как видите, здесь выполняется прослушивание порта 9050 на наличие приходящих данных, или как-то так.
В общем, суть в том, что мне очень нужно передать в метод receive_callback свои данные.
В C#, с его асинхронными методами BeginИмяМетода это все можно было легко сделать, передав нужный параметр, и получив его в методе callback'e.
Но как это сделать в C++?
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
14.10.2014, 00:19
Ответы с готовыми решениями:

Как передать аргументы в консольное приложение в одну строку?
Не могу понять, как в windows можно передать аргументы cin'у с помощью командной строки в одну...

При boost::scoped_ptr<udp::socket> reset вылетает 0xC0000005
В конструктор класса подаются io_service и int port. На строчке в конструкторе ...

Boost::asio::ip::udp::socket bind а мне нужен не локальный хост
Здравствуйте. Пытаюсь наладить для своих нужд пример #include &lt;iostream&gt; #include &lt;string&gt;...

For_each и аргументы callback-функции; Как передать callback'у больше одного аргумента
Изучаю контейнеры и алгоритмы stl по Майерсу . С непривычки слегка охренел и запутался в них . В...

Как передать callback функцию?
Добрый вечер. Как в классе передать callback функцию? Хочу ее использовать в array_filter....

32
Убежденный
Ушел с форума
Эксперт С++
16143 / 7290 / 1182
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
14.10.2014, 09:39 2
Можно сделать через boost::bind.
Этот подход демонстрируется в примерах из Boost.Asio.
0
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
14.10.2014, 14:06  [ТС] 3
Угу, разобрался. А как передать boost::ip::udp::socket?
Вот простые типы данных, в том числе и буффер с полученными данными, передать можно без проблем. А с сокетом проблемка. Сначала оно даже не компилировалось, а потом я обернул сокет в shared_ptr и с компиляцией все стало нормально, но на этапе отправки данных через сокет (это происходит в receive_callback), вылазиет ошибка.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <cstdlib>
#include <iostream>
#include <boost\asio.hpp>
#include <boost\bind.hpp>
 
using namespace boost::asio;
 
void receive_callback(const boost::system::error_code& ec, std::size_t bytes_transferred, char buff[], ip::udp::endpoint senderEP ,boost::shared_ptr<ip::udp::socket> sock)
{
    buff[bytes_transferred] = '\0';
    std::cout << "Got " << bytes_transferred<< " bytes: " << buff <<std::endl;
    sock->send_to(buffer("hello"), senderEP);
}
 
int main()
{
    try{
        io_service service;
        ip::udp::socket socket(service);
        boost::shared_ptr<ip::udp::socket> sock(&socket);
        char buff[1024];
        ip::udp::endpoint endPoint(ip::udp::v4(), 9050);
        sock->open(ip::udp::v4());
        sock->bind(endPoint);
        ip::udp::endpoint senderEP;
        sock->async_receive_from(buffer(buff), senderEP, 0, boost::bind(receive_callback,_1,_2,buff,senderEP,sock));
 
        service.run();
    }
    catch (std::exception ex)
    {
        std::cout <<"Error: " << ex.what() << std::endl; 
    }
 
    system("pause>>void");
    return 0;
}
0
Миниатюры
Ip::udp::socket.async_receive_from. Как передать дополнительные аргументы в callback  
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
14.10.2014, 14:23  [ТС] 4
ойойой, у меня еще целая куча проблем. Вот этот senderEP всегда нулевой, то есть в receive_callback я не могу узнать, с какого ip и порта пришло сообщение, хотя вроде как и передаю туда senderEP.
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
14.10.2014, 15:58 5
lawliet93,
C++
1
2
ip::udp::socket socket(service);
        boost::shared_ptr<ip::udp::socket> sock(&socket);
Очень-очень плохая затея. socket создался на стеке, а при уничтожении shared_ptr будет вызван delete на указатель, в итоге краш, т.к. указатель не был выделен через new. Конструируйте сразу shared_ptr через new или через make_shared.

senderEP просто конструируется, больше ему ничего не присваивается. Его стоит получать из socket в хендлере.

Добавлено через 8 минут
и использовать async_receive, т.к. вы не знаете откуда придет запрос.

В конце хендлера, если хотите чтобы сервер продолжал работать, стоит поставить задачу в очередь:
C++
1
2
3
4
// Обнулили буфер
memset(buff, 0, sizeof(buff));
// Вызвали async_receive
sock->async_receive(boost::bind(&receive_callback, _1, _2, buff, boost::ref(sock)));
1
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
14.10.2014, 18:11  [ТС] 6
ForEveR, а как это я должен получать в handler'е тот endpoint?
Я вот решил аж целый класс написать, вернее, аж два. И вот при вызове
C++
1
Client client(socket1.remote_endpoint(ec));
Вылазиет ошибка с кодом 10057 и сообщением Unknown error
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <cstdlib>
#include <iostream>
#include <boost\asio.hpp>
#include <boost\bind.hpp>
#include <boost\array.hpp>
 
using namespace boost::asio;
 
class Client{
    
public:
    Client(ip::udp::endpoint endP):endPoint(endP.address(),endP.port()){}
    ip::udp::endpoint endPoint;
    bool Compare(Client &other)
    {
        if (other.endPoint.address() == endPoint.address() && other.endPoint.port() == endPoint.port())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
};
 
class UdpSimpleServer{
private:
    ip::udp::socket socket1;
    std::array<char, 1024> buff;
    std::list<Client> clients;
    int port;
 
    void receive_callback(const boost::system::error_code &err, std::size_t bytes_transfered)
    {
        buff[bytes_transfered] = '\0';
        std::cout<< "got: " << buff.data() << std::endl;
 
        boost::system::error_code ec;
 
        Client client(socket1.remote_endpoint(ec));
        
        if (ec)
        {
            char er[256];
            strerror_s(er, sizeof(er), ec.value());
            std::cout << ec << "   " << er << std::endl;
            return;
        }
 
        bool add = true;
 
        if (clients.size() > 1)
        {
            for each (Client cl in clients)
            {
                if (client.Compare(cl))
                {
                    add = false;
                    break;
                }
            }
        }
 
        if (add)
        {
            clients.push_back(client);
        }
 
        if (clients.size() > 1)
        {
            for each (Client cl in clients)
            {
                if (!client.Compare(cl))
                {
                    socket1.send_to(buffer(buff), cl.endPoint);
                }
            }
        }
 
        socket1.async_receive(buffer(buff), 0, boost::bind(&UdpSimpleServer::receive_callback, this,
            boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
    }
 
public:
    UdpSimpleServer(io_service &service,int port) :socket1(service){
        UdpSimpleServer::port = port;
    }
    void Start()
    {
        try{
            socket1.open(ip::udp::v4());
            socket1.bind(ip::udp::endpoint(ip::address_v4::from_string("127.0.0.1"), port));
            
            socket1.async_receive(buffer(buff), 0, boost::bind(&UdpSimpleServer::receive_callback, this,
                boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
        catch (std::exception ex)
        {
            std::cout << "Error: " << ex.what() << std::endl;
        }
    }
};
 
int main()
{
    io_service service;
    UdpSimpleServer server(service, 9050);
    server.Start();
    service.run();
 
    std::cin.get();
    return 0;
}
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
14.10.2014, 19:41 7
lawliet93, Это system_error, не надо получать через strerror. У него есть метод message.
1
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 11:13  [ТС] 8
ForEveR,
(when sending on a datagram socket using a sendto call) no address was supplied
вот такое показало

Добавлено через 15 часов 12 минут
ForEveR, ну шо, вам там нечего сказать, ага? Ведь именно async_receive_from позволяет узнать конечную точку отправителя, а с async_receive, по всей видимости, ничего мы не узнаем.
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
15.10.2014, 11:34 9
lawliet93, Да, я видимо слегка забыл, что это udp, а не tcp. Работайте через async_receive_from.
1
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 11:47  [ТС] 10
ForEveR, слушайте, а может вы знаете, почему, если я отправляю на сервер данные, которые разделенные пробелом, то они приходят не одним куском, а по частям.
Например, я отправляю "прив чувак", а на сервер сначала приходит 4 байта и сообщение "прив", а потом еще 5 байт и сообщение "чувак"?
Код клиента
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <boost\asio.hpp>
#include <cstdio>
#include <iostream>
 
using namespace boost::asio;
 
io_service service;
ip::udp::socket sock(service);
std::array<char, 256> buff;
 
void receive_callback(const boost::system::error_code &ec, size_t bytes_transferred)
{
    buff[bytes_transferred] = '/0';
    std::cout << buff.data() << std::endl;
    sock.async_receive(buffer(buff), 0, receive_callback);
}
 
int main()
{
    ip::udp::endpoint endPoint(ip::address_v4::from_string("127.0.0.1"), 9050);
    sock.open(ip::udp::v4());
    sock.async_receive(buffer(buff), 0, receive_callback);
    std::string str;
    while (true)
    {
        std::cin >> str;
        sock.send_to(buffer(str), endPoint);
    }
 
    system("pause>>void");
    return 0;
}
Код сервера
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    #include <cstdlib>
    #include <iostream>
    #include <boost\asio.hpp>
    #include <boost\bind.hpp>
    #include <boost\array.hpp>
 
    using namespace boost::asio;
 
    class Client{
 
    public:
        Client(ip::udp::endpoint endP) :endPoint(endP.address(), endP.port()){}
        ip::udp::endpoint endPoint;
        bool Compare(Client &other)
        {
            if (other.endPoint.address() == endPoint.address() && other.endPoint.port() == endPoint.port())
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
    };
 
    class UdpSimpleServer{
    private:
        ip::udp::socket socket1;
        std::array<char, 1024> buff;
        std::list<Client> clients;
        ip::udp::endpoint endpoint;
        int port;
 
        void receive_callback(const boost::system::error_code &err, std::size_t bytes_transfered)
        {
            std::cout << "We got " << bytes_transfered << " bytes" << std::endl;
            if (bytes_transfered > 0){
                buff[bytes_transfered] = '\0';
                std::cout << buff.data() << std::endl;
 
                Client client(endpoint);
 
                bool add = false;
 
                if (clients.size() > 0)
                {
                    for each (Client cl in clients)
                    {
                        if (client.Compare(cl))
                        {
                            add = true;
                            break;
                        }
                    }
                }
 
                if (!add)
                {
                    std::cout << "Add new client" << std::endl;
                    clients.push_back(client);
                }
                std::cout << "We have " << clients.size() << " clients now" << std::endl;
                if (clients.size() > 1)
                {
                    for each (Client cl in clients)
                    {
                        if (!client.Compare(cl))
                        {
                            socket1.send_to(buffer(buff), cl.endPoint);
                        }
                    }
                }
            }
 
            socket1.async_receive_from(buffer(buff), endpoint, boost::bind(&UdpSimpleServer::receive_callback, this,
                boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
 
    public:
        UdpSimpleServer(io_service &service, int port) :socket1(service){
            UdpSimpleServer::port = port;
        }
        void Start()
        {
            try{
                socket1.open(ip::udp::v4());
                socket1.bind(ip::udp::endpoint(ip::address_v4::from_string("127.0.0.1"), port));
 
                socket1.async_receive_from(buffer(buff), endpoint, boost::bind(&UdpSimpleServer::receive_callback, this,
                    boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
            }
            catch (std::exception ex)
            {
                std::cout << "Error: " << ex.what() << std::endl;
            }
        }
    };
 
    int main()
    {
        io_service service;
        UdpSimpleServer server(service, 9050);
        server.Start();
        service.run();
 
        std::cin.get();
        return 0;
    }
0
Миниатюры
Ip::udp::socket.async_receive_from. Как передать дополнительные аргументы в callback  
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
15.10.2014, 12:02 11
lawliet93, Потому что стоит поучить С++, если хотите его использовать.
C++
1
std::cin >> str;
Считывает до разделителя (пробела). Чтобы считать строку до \n нужно использовать std::getline.
1
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 12:14  [ТС] 12
ForEveR, еще во прос, по поводу клиента.
Прошлый код был недоделанным, этот тоже недоделанный, но не так сильно, ведь здесь я запускаю service.run(), что дает возможность использовать async_receive
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <boost\asio.hpp>
#include <cstdio>
#include <iostream>
 
using namespace boost::asio;
 
io_service service;
ip::udp::socket sock(service);
std::array<char, 256> buff;
 
void receive_callback(const boost::system::error_code &ec, size_t bytes_transferred)
{
    std::cout << "We got " << bytes_transferred << " bytes" << std::endl;
    buff[bytes_transferred] = '/0';
    std::cout << buff.data() << std::endl;
    sock.async_receive(buffer(buff), 0, receive_callback);
}
 
int main()
{
    ip::udp::endpoint endPoint(ip::address_v4::from_string("127.0.0.1"), 9050);
    sock.open(ip::udp::v4());
    sock.async_receive(buffer(buff), receive_callback);
    service.run();
    std::string str;
    while (true)
    {
        std::getline(std::cin, str);
        sock.send_to(buffer(str,str.size()), endPoint);
    }
 
    system("pause>>void");
    return 0;
}
Проблема в том, что у меня постоянно вызывается receive_callback, который говорит, что пришло 0 байтов.
Очевидно, что я не знаю, как на клиенте принимать данные
То ли запутался, то ли просто не знаю, кароче.
Чего посоветуете?
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
15.10.2014, 12:40 13
lawliet93, Я все же советую использовать async_receive_from при работе с udp...
П.С. service.run() используется не там где надо, он блокирует поток исполнения, пока есть задания.
0
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 13:14  [ТС] 14
ForEveR, ну окей, сделал с async_receive_from, но все равно постоянно вызывается receive_callback.
А где же использовать service.run()? новый поток для него создавать или как?
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
15.10.2014, 14:31 15
lawliet93, Скорее поток логичнее создавать для ввода и посылки данных...
Или же

C++
1
2
3
4
5
sock.async_receive(buffer(buff), receive_callback);
std::string str;
std::getline(std::cin, str);
sock.send_to(buffer(str,str.size()), endPoint);
service.run();
В хендлере async_receive делаем новый send_to. Но это как-то бредово.

Можно так же юзать async_send и в его хендлере звать async_receive.
0
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 14:37  [ТС] 16
ForEveR, тогда поток лучше, а то если привязывать send к receive, то там же если не отправить ничего, то и не сможем получить ниче, и наоборот, ага?
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
15.10.2014, 15:02 17
lawliet93, Да. Так что либо поток на взаимодействие с юзером, либо поток на io_service. Либо погуглить как правильно сие делать в С++.
0
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 15:35  [ТС] 18
ForEveR, да я уже сделал, но клиент все равно не может принять данные.
Вот смотрите. Есть клиент, и есть сервер. Для того, чтобы отправить какие-то данные, нужно же занять порт, ага? То есть у клиента и у сервера есть свой порт, с которого они отправляют сообщение. А так же должен быть еще один порт, который клиент и сервер должны слушать, чтобы принимать пришедшие данные, ага?
То есть у клиента тоже нужно вызывать socket.bind(endpoint)? Но какой порт слушать этому клиенту?
Я ж не могу объявить явно, что мол отправляет на такой порт, а принимает на такой. Потому что тогда я не смогу запустить два клиента на одном компе т.к. будет попытка забайндить уже забайндненный порт, ага?
0
ForEveR
В астрале
Эксперт С++
8003 / 4761 / 653
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
15.10.2014, 17:18 19
lawliet93, Клиент получает данные с сервера, сервер получает данные от клиента, не? Зачем какой-то третий порт? Один у сервера, один у клиента, сервер получает откуда угодно и отвечает тому откуда пришло, клиент шлет строго на сервер и получает строго с сервера.
0
lawliet93
17 / 5 / 3
Регистрация: 22.03.2011
Сообщений: 329
15.10.2014, 17:26  [ТС] 20
ForEveR, эээ, суть в том, что для того, чтобы смогти что-то получить от сервера, на клиенте нужно вызвать socket.bind(endPoint), и вот именно на этот endPoint сервер должен отправлять данные, чтобы клиент их принял.
И каким же должен быть этот endPoint, спрашивается?
0
15.10.2014, 17:26
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
15.10.2014, 17:26

Как в Qt передать указатель на CallBack функцию WinAPI ? :-)
Есть ли в Qt такое понятие как CallBack или нет? Конечно же тут проскакивает аналогия со слотом,...

WCF Callback for UDP
Как в WCF, получая и отправляя данные по UDP использовать Callback? IScreenCallback callback =...

addEventListener - дополнительные аргументы
Здравствуйте. ActionScript знаю довольно плохо, поэтому требуется помощь) Конечно, мог бы...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru