基于udp端口转发程序设计(一)

udp端口转发

做一个udp端口转发,使用两个网卡,相当于建立了一个路由,从内外转发到外网,只作udp 程序的转发。

使用一个socket asio建立转发

使用一个socket 完全可以做到接收和发送,使用异步接收,同步发送。udp 发送的时候是非常快的,缓冲区要适当放一些量,发送的时候不使用队列,直接使用接收->发送
再接收->发送的方式,异步同步,异步同步这样做,好处和优点是特别简单,对付大量数据也不在话下。

#define _WIN32_WINNT 0x0A00
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include 
#include 
#include 
#include "boost/asio.hpp"
//#include "boost/bind.hpp"
#include "boost/bind/bind.hpp"
#include 
#include 
#include 
#include 
#include 

#include "spdlog/spdlog.h"
#include "spdlog/fmt/ostr.h"

using namespace std;
using namespace boost::asio;
using boost::asio::ip::udp;
using namespace boost::posix_time;

static const ip::udp::endpoint s_end_point(ip::address::from_string("1.82.219.249"), 6000);
//static const ip::udp::endpoint s_end_point(ip::address::from_string("192.168.0.129"), 9000);

struct param{
	//uint16_t _port;
	//udp::socket _socket;
	//recv
	uint64_t recv_bytes = 0;
	param(io_context& ctx)//:_socket(ctx, udp::v4())
	{
		//boost::asio::socket_base::send_buffer_size size_option_s(2 * 1024 * 1024);
		//_socket.set_option(size_option_s);
	}
};
typedef std::shared_ptr<param> ptr_param;
class talk_to_svr 
{
	uint64_t v_bytes = 0;
	int64_t v_packets = 0;
	io_context& v_context;
public:
	unordered_map<std::string, ptr_param> v_map_sockets;
	talk_to_svr(io_service& io_context)
		:v_context(io_context),v_socket(io_context), started_(true)
		//timer_manager_(io_context)
	{
		udp::endpoint ep(udp::v4(), 6000);
		v_socket.open(ep.protocol());
		v_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
		boost::asio::socket_base::receive_buffer_size size_option_r(4 *1024 * 1024);
		v_socket.set_option(size_option_r);
		boost::asio::socket_base::send_buffer_size size_option_s(4 * 1024 * 1024);
		v_socket.set_option(size_option_s);
		boost::system::error_code ec;
		v_socket.bind(ep, ec);
		if (ec)
		{
			spdlog::error("recv error {0}", ec.message().c_str());
		}
		//timer_manager_.Add(this, 1, &talk_to_svr::Timer1Sec);
		//timer_manager_.Run();
		start();
	}


	typedef shared_ptr<talk_to_svr> ptr;

	void start() {
		//开始接收所有数据
		do_read();
		//do_write(message_);
	}



	bool started() { return started_; }
private:
	void on_read(const error_code& err, size_t bytes) {
		if (!err) {
			//spdlog::info("recv bytes {d:0}",bytes);
			v_socket.send_to(boost::asio::buffer(read_buffer_, bytes), s_end_point);
			//v_bytes += bytes;
			//if (v_packets++ % 100000 == 0)
			//	spdlog::info("recv_packet:{0} T:{1} bytes", v_packets, v_bytes);
#if 1
			string ip = sender_ep.address().to_string();
			if (v_map_sockets.find(ip) == v_map_sockets.end())
			{
				spdlog::info("ip in {0}", ip.c_str());
				ptr_param ptr = std::make_shared<param>(v_context);
				v_map_sockets[ip] = ptr;
			}
			ptr_param& ptr = v_map_sockets[ip];
			ptr->recv_bytes += bytes;
			if(ptr->recv_bytes % 1000000 == 0)
				spdlog::info("{0} recv_packet:{1} T:{2} bytes",ip,v_packets, ptr->recv_bytes);

			//ptr->_socket.send_to(boost::asio::buffer(read_buffer_,bytes), s_end_point);
#endif
			do_read();
		}
		else
		{
			spdlog::error("recv error {0}", err.message().c_str());
		}
	}
	void on_write(const error_code& err, size_t bytes) {
		//printf("client write result:%d, bytes:%d \n", err.value(), bytes);
		//do_read();
	}
	void do_read() {
		
		v_socket.async_receive_from(buffer(read_buffer_), sender_ep,
			bind(&talk_to_svr::on_read,
				this,
				boost::placeholders::_1,
				boost::placeholders::_2));
	}
	void do_write(int size) {

		v_socket.async_send_to(buffer(write_buffer_,size), s_end_point,
			bind(&talk_to_svr::on_write,
				this,
				boost::placeholders::_1,
				boost::placeholders::_2));
	}

	void checkdata()
	{

	}
private:
	//boost::asio::timer::TimerManager timer_manager_;

private:
	udp::socket v_socket;
	ip::udp::endpoint sender_ep;
	enum { max_msg = 1500 };
	char read_buffer_[max_msg];
	char write_buffer_[max_msg];
	bool started_;
	std::string message_;
	
};
namespace spd = spdlog;
int main(int argc, char* argv[])
{

	io_context io_service;
	talk_to_svr client(io_service);
	//auto console = spd::stdout_color_mt("console");
	spdlog::info("author:qianbo");
	spdlog::info("Welcome to qb port server!");
	spdlog::info("Server run at {0}", "6000");
	//std::cout << "server run at " << 6000 << std::endl;
	io_service.run();
	return 0;
	//system("pause");
}

缺陷

这种方式的发送有一个缺陷,就是发送的端口和IP都是一样的,这样在接收的服务端是无法分辨是谁发送的,但不是不能解决,我们知道发送我们是使用RTP协议的,实时传输协议的好处就是,可以加上自己的头部,比如RTP协议里面加上CSRC头部,就可以解决这个问题。怕的是接收程序没写好,没有标准化接收!

更新和改进

改进方式为:
1 增加socket 发送,没一个对应的IP地址使用不同的socket,这样也就意味着不同的端口来发送,好处是,不用修改标准12字节头部的RTP协议。
2 增加多线程发送,使用协程发送,好处是使用同步的效果,异步的性能,当然,cpu是需要增加的。这样也就意味着更多的利用cpu。

你可能感兴趣的:(c++高级技巧,c++,udp,网络,网络协议,RTP,端口转发)