gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-28 05:49:37 27 4
gpt4 key购买 nike

我是 C++ boost 库的新手。我已经设法使用 boost asio 库实现了 UDP 服务器和客户端。目前在我的示例程序中,我启动了 UDP 服务器,然后尝试使用 UDP 客户端进行连接。客户端连接并发送一些数据后,服务器会使用随机生成的字符串进行响应,我已将其转换为十六进制并将其打印出来。一旦它收到该字符串,UDP 客户端就会调用 destruct-or 并退出。

我的代码在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;
}

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

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)

我的问题如下。

1)设计是否适合发送网络数据包。我担心的是,当客户端向服务器发送一些数据时,数据包似乎已发送(即服务器响应)。也就是说客户端需要周期性的轮询服务器来查询数据。服务器是否有其他模型通知客户端数据可用?那会是更好的设计吗?

2) 在示例中,我为客户端和服务器分配了一个 8192 字节的数组。那是必需的吗。据我了解,UDP 的 MTU(以及 TCP 是 1500 字节)。有什么理由在客户端和服务器上分配一个超过 1500 字节的数组吗?

如果有人能回答上面的问题就太好了。

最佳答案

在为应用程序协议(protocol)设计 I/O 层时,请考虑预期的网络拓扑、应用程序协议(protocol)要求、任何服务级别协议(protocol)、硬件要求等。围绕满足这些约束进行设计,而不是在其中投入太多精力优化小效率。在不知道更多细节的情况下,回答哪种设计好或其他设计是否更好将是主观的。尽管如此:

  • 如果让服务器响应客户端的周期性请求满足应用程序协议(protocol)的要求,那么它可能是一个不错的设计。
  • 如果需要减少通知客户端的延迟,那么让服务器在数据可用时发起发送可能是一个不错的设计。请注意,由于 NAT traversal,网络拓扑可能会对此产生影响。 .

对于给定层,maximum transmission unit (MTU) 定义了可以向前传递到下一层的协议(protocol)单元的最大大小。更高级别的层和协议(protocol)可能会引入分段处理,从而允许给定层的协议(protocol)单元的最大大小超过较低层的 MTU。例如,以太网 的 MTU 为 1500 字节,而 UDP 数据报(第 4 层:传输)有效载荷的最大大小为 65507 字节。这是可能的,因为 IP 层数据包(第 3 层:网络)可能由一个或多个以太网(第 2 层:数据链路)构成。

应该使用的缓冲区大小通常取决于应用程序协议(protocol)。例如,Asio Chat example使用 516 字节的缓冲区,因为应用程序协议(protocol)的最大长度为 516 字节。下层协议(protocol)单元的分片和重组对应用程序来说是透明的。然而,由于 UDP 既不提供确认也不提供重传,并且数据报的一部分丢失将导致整个数据报被丢弃,因此较大的数据报由于更大的碎片而丢失的可能性更大。

关于c++ - 使用 boost::asio 的 UDP 客户端和服务器设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35530761/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com