基于udp端口转发程序设计

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于udp端口转发程序设计相关的知识,希望对你有一定的参考价值。

udp端口转发

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

使用一个socket asio建立转发

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

#define _WIN32_WINNT 0x0A00
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <string.h>
#include <iostream>
#include <string>
#include "boost/asio.hpp"
//#include "boost/bind.hpp"
#include "boost/bind/bind.hpp"
#include <boost/lockfree/queue.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <mutex>
#include <string>
#include <unordered_map>

#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<std::chrono::seconds> 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。

以上是关于基于udp端口转发程序设计的主要内容,如果未能解决你的问题,请参考以下文章

怎么用路由器转发UDP端口27015

UDP/TCP 发送和接收,无需端口转发

winsocket udp和tcp编程,缓冲区应该设置多大?

基于Java开发客户端音频采集播放UDP协议转发程序

基于Java开发客户端音频采集播放UDP协议转发程序

Azure Load Balancer : 简介