st_asio_wrapper_udp_socket.h 7.4 KB
Newer Older
1 2 3 4 5
/*
 * st_asio_wrapper_udp_socket.h
 *
 *  Created on: 2012-3-2
 *      Author: youngwolf
6 7 8
 *		email: mail2tao@163.com
 *		QQ: 676218192
 *		Community on QQ: 198941541
9 10 11 12 13 14 15 16 17
 *
 * this class used at both client and server endpoint
 */

#ifndef ST_ASIO_WRAPPER_UDP_SOCKET_H_
#define ST_ASIO_WRAPPER_UDP_SOCKET_H_

#include <boost/array.hpp>

Y
youngwolf 已提交
18
#include "st_asio_wrapper_socket.h"
19

Y
youngowlf 已提交
20 21
//in set_local_addr, if the IP is empty, UDP_DEFAULT_IP_VERSION will define the IP version,
//or, the IP version will be deduced by the IP address.
Y
youngwolf 已提交
22
//boost::asio::ip::udp::v4() means ipv4 and boost::asio::ip::udp::v6() means ipv6.
23
#ifndef UDP_DEFAULT_IP_VERSION
Y
youngwolf 已提交
24
#define UDP_DEFAULT_IP_VERSION boost::asio::ip::udp::v4()
25 26 27 28
#endif

namespace st_asio_wrapper
{
Y
youngwolf 已提交
29 30
namespace st_udp
{
31

Y
youngwolf 已提交
32
template<typename MsgType>
Y
youngwolf 已提交
33
struct udp_msg
34
{
Y
youngwolf 已提交
35
	boost::asio::ip::udp::endpoint peer_addr;
Y
youngwolf 已提交
36
	MsgType str;
37

Y
youngwolf 已提交
38
	void swap(udp_msg& other) {std::swap(peer_addr, other.peer_addr); str.swap(other.str);}
Y
youngwolf 已提交
39
	void swap(const boost::asio::ip::udp::endpoint& addr, MsgType& tmp_str) {peer_addr = addr; str.swap(tmp_str);}
Y
youngwolf 已提交
40
	void clear() {peer_addr = boost::asio::ip::udp::endpoint(); str.clear();}
Y
youngwolf 已提交
41
	bool empty() const {return str.empty();}
Y
youngwolf 已提交
42 43
	bool operator==(const udp_msg& other) const {return this == &other;}
};
44

Y
youngwolf 已提交
45 46
template <typename MsgType = std::string, typename Socket = boost::asio::ip::udp::socket>
class st_udp_socket_base : public st_socket<udp_msg<MsgType>, Socket, MsgType>
Y
youngwolf 已提交
47
{
48
public:
Y
youngwolf 已提交
49
	st_udp_socket_base(boost::asio::io_service& io_service_) :
Y
youngwolf 已提交
50
		st_socket<udp_msg<MsgType>, Socket, MsgType>(io_service_) {ST_THIS reset_state();}
51 52

	//reset all, be ensure that there's no any operations performed on this st_udp_socket when invoke it
Y
youngowlf 已提交
53 54
	//notice, when reuse this st_udp_socket, st_object_pool will invoke reset(), child must re-write this to initialize
	//all member variables, and then do not forget to invoke st_udp_socket::reset() to initialize father's
55
	//member variables
56 57
	virtual void reset()
	{
Y
youngwolf 已提交
58 59
		ST_THIS reset_state();
		ST_THIS clear_buffer();
60

Y
youngwolf 已提交
61
		boost::system::error_code ec;
Y
youngwolf 已提交
62 63
		ST_THIS lowest_layer().close(ec);
		ST_THIS lowest_layer().open(local_addr.protocol(), ec); assert(!ec);
64
#ifndef NOT_REUSE_ADDRESS
Y
youngwolf 已提交
65
		ST_THIS lowest_layer().set_option(boost::asio::socket_base::reuse_address(true), ec); assert(!ec);
66
#endif
Y
youngwolf 已提交
67
		ST_THIS lowest_layer().bind(local_addr, ec); assert(!ec);
68 69 70
		if (ec) {unified_out::error_out("bind failed.");}
	}

71
	void set_local_addr(unsigned short port, const std::string& ip = std::string())
72
	{
Y
youngwolf 已提交
73
		boost::system::error_code ec;
74
		if (ip.empty())
Y
youngwolf 已提交
75
			local_addr = boost::asio::ip::udp::endpoint(UDP_DEFAULT_IP_VERSION, port);
76 77
		else
		{
Y
youngwolf 已提交
78
			local_addr = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(ip, ec), port);
79 80
			assert(!ec);
		}
81
	}
Y
youngwolf 已提交
82
	const boost::asio::ip::udp::endpoint& get_local_addr() const {return local_addr;}
83

84 85 86 87
	void disconnect() {force_close();}
	void force_close() {clean_up();}
	void graceful_close() {clean_up();}

Y
youngowlf 已提交
88
	//UDP does not need a unpacker
89

Y
youngwolf 已提交
90
	using st_socket<udp_msg<MsgType>, Socket, MsgType>::send_msg;
91 92
	///////////////////////////////////////////////////
	//msg sending interface
Y
youngwolf 已提交
93 94 95 96 97 98
	UDP_SEND_MSG(send_msg, false) //use the packer with native = false to pack the msgs
	UDP_SEND_MSG(send_native_msg, true) //use the packer with native = true to pack the msgs
	//guarantee send msg successfully even if can_overflow equal to false
	//success at here just means put the msg into st_udp_socket's send buffer
	UDP_SAFE_SEND_MSG(safe_send_msg, send_msg)
	UDP_SAFE_SEND_MSG(safe_send_native_msg, send_native_msg)
Y
youngwolf 已提交
99 100 101
	//like safe_send_msg and safe_send_native_msg, but non-block
	UDP_POST_MSG(post_msg, false)
	UDP_POST_MSG(post_native_msg, true)
102 103 104
	//msg sending interface
	///////////////////////////////////////////////////

105 106
	void show_info(const char* head, const char* tail)
	{
Y
youngwolf 已提交
107
		boost::system::error_code ec;
Y
youngwolf 已提交
108
		auto ep = ST_THIS lowest_layer().local_endpoint(ec);
109 110 111 112
		if (!ec)
			unified_out::info_out("%s %s:%hu %s", head, ep.address().to_string().c_str(), ep.port(), tail);
	}

113
protected:
114 115
	virtual bool do_start()
	{
Y
youngwolf 已提交
116
		if (!ST_THIS get_io_service().stopped())
117
		{
Y
youngwolf 已提交
118 119
			ST_THIS next_layer().async_receive_from(boost::asio::buffer(raw_buff), peer_addr,
				boost::bind(&st_udp_socket_base::recv_handler, this,
Y
youngwolf 已提交
120
				boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
121 122 123 124 125 126 127 128 129 130

			return true;
		}

		return false;
	}

	//must mutex send_msg_buffer before invoke this function
	virtual bool do_send_msg()
	{
Y
youngwolf 已提交
131 132 133
		if (!is_send_allowed() || ST_THIS get_io_service().stopped())
			ST_THIS sending = false;
		else if (!ST_THIS sending && !send_msg_buffer.empty())
134
		{
Y
youngwolf 已提交
135 136
			ST_THIS sending = true;
			ST_THIS last_send_msg.swap(send_msg_buffer.front());
Y
youngwolf 已提交
137 138 139 140
			ST_THIS next_layer().async_send_to(
				boost::asio::buffer(ST_THIS last_send_msg.str.data(), ST_THIS last_send_msg.str.size()),
				ST_THIS last_send_msg.peer_addr,
				boost::bind(&st_udp_socket_base::send_handler, this,
Y
youngwolf 已提交
141
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
142 143 144
			send_msg_buffer.pop_front();
		}

Y
youngwolf 已提交
145
		return ST_THIS sending;
146 147
	}

Y
youngwolf 已提交
148
	virtual bool is_send_allowed() const
Y
youngwolf 已提交
149
		{return ST_THIS lowest_layer().is_open() && st_socket<udp_msg<MsgType>, Socket, MsgType>::is_send_allowed();}
150 151
	//can send data or not(just put into send buffer)

Y
youngwolf 已提交
152
	virtual void on_recv_error(const boost::system::error_code& ec)
Y
youngwolf 已提交
153
		{unified_out::error_out("recv msg error: %d %s", ec.value(), ec.message().data());}
154 155

#ifndef FORCE_TO_USE_MSG_RECV_BUFFER
Y
youngwolf 已提交
156
	virtual bool on_msg(udp_msg<MsgType>& msg)
157
		{unified_out::debug_out("recv(" size_t_format "): %s", msg.str.size(), msg.str.data()); return true;}
158 159
#endif

Y
youngwolf 已提交
160
	virtual bool on_msg_handle(udp_msg<MsgType>& msg, bool link_down)
161
		{unified_out::debug_out("recv(" size_t_format "): %s", msg.str.size(), msg.str.data()); return true;}
162 163 164

	void clean_up()
	{
Y
youngwolf 已提交
165
		if (ST_THIS lowest_layer().is_open())
166
		{
Y
youngwolf 已提交
167
			boost::system::error_code ec;
Y
youngwolf 已提交
168 169
			ST_THIS lowest_layer().shutdown(boost::asio::ip::udp::socket::shutdown_both, ec);
			ST_THIS lowest_layer().close(ec);
170 171
		}

Y
youngwolf 已提交
172 173
		ST_THIS stop_all_timer();
		ST_THIS reset_state();
174 175
	}

Y
youngwolf 已提交
176
	void recv_handler(const boost::system::error_code& ec, size_t bytes_transferred)
177 178 179
	{
		if (!ec && bytes_transferred > 0)
		{
Y
youngwolf 已提交
180
			MsgType tmp_str(raw_buff.data(), bytes_transferred);
Y
youngwolf 已提交
181
			temp_msg_buffer.resize(temp_msg_buffer.size() + 1);
182
			temp_msg_buffer.back().swap(peer_addr, tmp_str);
Y
youngwolf 已提交
183
			ST_THIS dispatch_msg();
184 185 186
		}
#ifdef _MSC_VER
		else if (WSAECONNREFUSED == ec.value())
187
			do_start();
188 189 190 191 192
#endif
		else
			on_recv_error(ec);
	}

Y
youngwolf 已提交
193
	void send_handler(const boost::system::error_code& ec, size_t bytes_transferred)
194 195 196 197 198
	{
		if (!ec)
		{
			assert(bytes_transferred > 0);
#ifdef WANT_MSG_SEND_NOTIFY
Y
youngwolf 已提交
199
			ST_THIS on_msg_send(last_send_msg);
200 201 202
#endif
		}
		else
Y
youngwolf 已提交
203
			ST_THIS on_send_error(ec);
204

Y
youngwolf 已提交
205
		boost::mutex::scoped_lock lock(send_msg_buffer_mutex);
Y
youngwolf 已提交
206
		ST_THIS sending = false;
Y
youngwolf 已提交
207

208
		//send msg sequentially, that means second send only after first send success
209
		//under windows, send a msg to addr_any may cause sending errors, please note
Y
youngowlf 已提交
210
		//for UDP in st_asio_wrapper, sending error will not stop the following sending.
211
		if (!do_send_msg())
Y
youngwolf 已提交
212
		{
213
#ifdef WANT_ALL_MSG_SEND_NOTIFY
Y
youngwolf 已提交
214 215
			lock.unlock();
			on_all_msg_send(last_send_msg);
216
#endif
Y
youngwolf 已提交
217
		}
218 219 220
	}

protected:
Y
youngwolf 已提交
221 222
	boost::array<char, MAX_MSG_LEN> raw_buff;
	boost::asio::ip::udp::endpoint peer_addr, local_addr;
223
};
Y
youngwolf 已提交
224
typedef st_udp_socket_base<> st_udp_socket;
225

Y
youngwolf 已提交
226 227 228
} //namespace st_udp
} //namespace st_asio_wrapper

229
using namespace st_asio_wrapper::st_udp; //compatible with old version which doesn't have st_udp namespace.
230 231

#endif /* ST_ASIO_WRAPPER_UDP_SOCKET_H_ */