简体   繁体   English

使用boost :: asio的UDP客户端和服务器设计

[英]UDP client and server design using boost::asio

I am new to C++ boost library.I have managed to implement a UDP server and client using boost asio library. 我是C ++ Boost库的新手,我已经设法使用boost asio库实现UDP服务器和客户端。 Currently in my example program I start the UDP server and then attempt to connect using UDP client. 当前,在示例程序中,我启动UDP服务器,然后尝试使用UDP客户端进行连接。 Once the client connects and sends some data the server responds with an randomly generated string which I have converted to hex and printing it out.Once it receives the string the UDP client call the destruct-or and exits. 一旦客户端连接并发送了一些数据,服务器就会响应一个随机生成的字符串,我将其转换为十六进制并打印出来。一旦收到该字符串,UDP客户端就会调用destruct-or并退出。

My code is given below udp_server.hpp and udp_client.hpp 我的代码在udp_server.hpp和udp_client.hpp下给出

#include "udp_server.hpp"
#include <iostream>
#include <exception>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <algorithm>
#include <sstream>
#include <iomanip>
const int ARG_COUNT = 2;
const int LOWEST_PORT = 1024;
const int HIGHEST_PORT = 65000;

static char message_array[8192];

void gen_random_string(char *s, const int len) 
{
    static const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";

    for (int i = 0; i < len; ++i) {
        s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
    }
    s[len] = 0;
}


class udp_server
{
public:
    udp_server(boost::asio::io_service& io_service,int port_number)
        : socket_(io_service, boost::asio::ip::udp::udp::endpoint(boost::asio::ip::udp::udp::v4(), port_number))
    {
        std::cout << "UDP server listening on " << port_number << std::endl;
        start_receive();
    }

private:
    void start_receive()
    {
        socket_.async_receive_from(
            boost::asio::buffer(recv_buffer_), remote_endpoint_,
            boost::bind(&udp_server::handle_receive, this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
    }

    void handle_receive(const boost::system::error_code& error,
                        std::size_t /*bytes_transferred*/)
    {
        if (!error || error == boost::asio::error::message_size)
        {
            gen_random_string(message_array, 8192);
            boost::shared_ptr<std::string> message(new std::string(message_array));

            socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
                                  boost::bind(&udp_server::handle_send, this, message,
                                              boost::asio::placeholders::error,
                                              boost::asio::placeholders::bytes_transferred));

            start_receive();
        }
    }

    void handle_send(boost::shared_ptr<std::string> /*message*/,
                     const boost::system::error_code& /*error*/,
                     std::size_t /*bytes_transferred*/)
    {
    }

    boost::asio::ip::udp::udp::socket socket_;
    boost::asio::ip::udp::udp::endpoint remote_endpoint_;
    boost::array<char, 1> recv_buffer_;
};


void runUDPServer( CmdLineOpts input )
{
    try
    {
        boost::asio::io_service io_service;
        udp_server server(io_service,input.port);
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

}



class udp_client
{
public:
    udp_client(
        boost::asio::io_service& io_service,
        const std::string& host,
        const std::string& port
    ) : io_service_(io_service), socket_(io_service, boost::asio::ip::udp::udp::endpoint(boost::asio::ip::udp::udp::v4(), 0)) {
        boost::asio::ip::udp::udp::resolver resolver(io_service_);
        boost::asio::ip::udp::udp::resolver::query query(boost::asio::ip::udp::udp::v4(), host, port);
        boost::asio::ip::udp::udp::resolver::iterator iter = resolver.resolve(query);
        endpoint_ = *iter;
    }

    ~udp_client()
    {
        std::cout << "Calling UDP client destructor" << std::endl;
        socket_.close();
    }

    void send() {
        socket_.send_to(boost::asio::buffer(send_buf), endpoint_);
    }

    void recieve_from() {
        /*Initialize our endpoint*/
        boost::array<unsigned char, 8192> temp; 
       // boost::asio::buffer boost_buf(temp);
        size_t len = socket_.receive_from(
                         boost::asio::buffer(temp), sender_endpoint);

        std::ostringstream ss;
        ss << std::hex << std::uppercase << std::setfill( '0' );
        std::for_each( temp.cbegin(), temp.cend(), [&]( int c ) { ss << std::setw( 2 ) << c; } );
        std::string result = ss.str();
        std::cout << "Length of recieved message " << len << std::endl;
        std::cout << result << std::endl;

    }

private:
    boost::asio::io_service& io_service_;
    boost::asio::ip::udp::udp::socket socket_;
    boost::asio::ip::udp::udp::endpoint endpoint_;
    //boost::array<char, 2048> recv_buf;
    std::vector<unsigned char> recv_buf;
    boost::array<char, 1> send_buf  = {{ 0 }};
    boost::asio::ip::udp::endpoint sender_endpoint;

};

void runUDPClient(std::string portStr)
{
    try
    {
        boost::asio::io_service io_service;
        udp_client client(io_service, "localhost", portStr);
        client.send();
        client.recieve_from();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}

void runClient( CmdLineOpts input )
{
    runUDPClient(input.portStr);
}

void runServer( CmdLineOpts input )
{
    runUDPServer(input);
}

/**
* Usage: client_server <protocol> <port> <num of packets>
*/
bool clarg_parse ( int argc, char *argv[], CmdLineOpts *input )
{
    bool result = true;
    if (argc - 1 == ARG_COUNT)
    {
        // arg 1: server or client
        int arg1 = std::stoi(argv[1]);
        if (arg1 == 0 || arg1 == 1)
        {
            input->servOrClient = arg1;
        }
        else
        {
            std::cout << "Invalid client server choice.\nUsage: client_server <client (0) or server(1)> <port>" << std::endl;
            result = false;
        }
        // arg 2: port
        int arg2 = std::stoi(argv[3]);
        if (arg2 > LOWEST_PORT && arg2 < HIGHEST_PORT )
        {
            input->port = arg2;
            input->portStr = argv[3];
        }
        else
        {
            std::cout << "Invalid port, must be between " << LOWEST_PORT << " and " << HIGHEST_PORT << std::endl;
            std::cout << "Usage: client_server <client (0) or server(1)> <port>" << std::endl;
            result = false;
        }

    }
    else
    {
        std::cout << "Usage: client_server <client (0) or server(1)> <port>" << std::endl;
        result = false;
    }

    return result;
}



int main ( int argc, char *argv[] )
{
    CmdLineOpts input;
    if (clarg_parse(argc, argv, &input))
    {
        if(input.servOrClient == 1)
        {
            runServer(input);
        }
        else if(input.servOrClient == 0)
        {
            runClient(input);
        }
    }
    else
    {
        return 1;
    }

    return 0;
}

The header file udp_server.hpp 头文件udp_server.hpp

#ifndef UDP_SERVER_H_INCLUDED 
#define UDP_SERVER_H_INCLUDED

#include <string>

struct CmdLineOpts
{
    std::string portStr;
    int port;
    int servOrClient;
};

void runUDPServer ( CmdLineOpts input );

bool clarg_parse ( int argc, char *argv[], CmdLineOpts input );
#endif

Makefile to compile the above program Makefile编译以上程序

TARGET = udp_server
LIBS = -lboost_system -lpthread
CXX = g++
CXXFLAGS = -std=c++11 -g -Wall -pedantic

.PHONY: default all clean

default: $(TARGET)
all: default

OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
HEADERS = $(wildcard *.hpp)

%.o: %.cpp $(HEADERS)
    $(CXX) $(CXXFLAGS) -c $< -o $@

.PRECIOUS: $(TARGET) $(OBJECTS)

$(TARGET): $(OBJECTS)
    $(CXX) $(OBJECTS) $(LIBS) -o $@

clean:
        -rm -f *.o
        -rm -f $(TARGET)

My question is as follows. 我的问题如下。

1) Is the design good for sending network packets.My concern is that it seems the packet is sent(ie the server responds) when the client sends some data to the server. 1)设计是否适合发送网络数据包。我担心的是,当客户端向服务器发送一些数据时,数据包似乎已发送(即服务器响应)。 In other words the client needs to poll the server periodically to query the data. 换句话说,客户端需要定期轮询服务器以查询数据。 Is there some other model where the server informs the client that data is available ? 服务器是否还有其他模型可以通知客户端数据可用? Would that be a better design? 那会是更好的设计吗?

2) In the example I am assigning an array of 8192 bytes for both client and server. 2)在示例中,我为客户端和服务器分配了8192个字节的数组。 Is that required. 这是必需的吗? As I understand the MTU for UDP (as well as for TCP is 1500 Bytes). 据我了解,MTU适用于UDP(以及TCP的1500字节)。 Is there any reason to assign an array of more than 1500 bytes at both client and server.? 是否有任何理由在客户端和服务器上分配一个超过1500个字节的数组?

It would be really great if someone could answer the above questions. 如果有人能够回答上述问题,那将是非常不错的。

When designing the I/O layer for an application protocol, consider the expected network topography, the application protocol requirements, any service-level agreements, hardware requirements, etc. Design around satisfying those constraints, rather than putting too much effort in optimizing for small efficiencies. 在为应用程序协议设计I / O层时,请考虑预期的网络拓扑结构,应用程序协议要求,任何服务级别协议,硬件要求等。围绕满足这些约束条件进行设计,而不要花费太多精力进行优化以优化小型应用程序。效率。 Without knowing many more details, it will be subjective to answer what design is good or if other designs are better. 在不了解更多细节的情况下,主观回答哪种设计是好的,或者其他设计是否更好。 Nevertheless: 不过:

  • if having the server respond to a client's periodic request meets the application protocol's requirements, then it may be a good design. 如果让服务器响应客户端的定期请求符合应用程序协议的要求,那么这可能是一个不错的设计。
  • if the latency for informing the client needs to be reduced, then having the server initiate a send when data becomes available may be a good design. 如果需要减少通知客户端的延迟,那么在数据可用时让服务器启动发送可能是一个不错的设计。 Be aware that the network topography may affect this due to NAT traversal . 请注意,由于NAT遍历 ,网络拓扑可能会影响这一点。

For a given layer, the maximum transmission unit (MTU) defines the maximum size of the protocol unit which may be passed onwards to the next layer. 对于给定的层, 最大传输单元 (MTU)定义了协议单元的最大大小,可以继续传递给下一层。 Higher-level layers and protocols may introduce fragmentation handling, allowing the max size of a given layer's protocol unit to exceed the lower layer's MTU. 较高层和协议可能会引入碎片处理,从而使给定层的协议单元的最大大小超过较低层的MTU。 For instance, while the Ethernet frame 's MTU is 1500 bytes, the max size for UDP datagram 's (Layer 4: Transport) payload is 65507 bytes. 例如,当以太网的MTU为1500字节时,UDP 数据报 (第4层:传输)有效负载的最大大小为65507字节。 This is possible because IP Layer packets (Layer 3: Network) may be constructed from one or more Ethernet frames (Layer 2: Data Link). 这是可能的,因为IP层数据包 (第3层:网络)可能是由一个或多个以太网 (第2层:数据链路)构成的。

The size of the buffer one should use is often dependent on the application protocol. 一个缓冲区应该使用的大小通常取决于应用程序协议。 For example, the Asio Chat example uses buffers that are 516 bytes because the application protocol has a max length of 516 bytes. 例如对于Asio Chat示例使用516字节的缓冲区,因为应用程序协议的最大长度为516字节。 The fragmentation and reassemble of lower layer protocol units will be transparent to the application. 下层协议单元的分段和重组对应用程序是透明的。 However, as UDP provides neither acknowledgment nor retransmission, and the loss of part of a datagram will cause the entire datagram to be discarded, larger datagrams have a greater chance of being loss due to greater fragmentation. 但是,由于UDP既不提供确认也不进行重传,并且丢失一部分数据报将导致整个数据报被丢弃,因此,较大的数据报由于更大的碎片而更有可能丢失。

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

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