echo_server.cpp 8.6 KB
Newer Older
Y
youngwolf 已提交
1 2 3 4 5 6 7

#include <iostream>

//configuration
#define ASCS_SERVER_PORT		9527
#define ASCS_ASYNC_ACCEPT_NUM	5
#define ASCS_REUSE_OBJECT //use objects pool
Y
youngwolf 已提交
8
//#define ST_ASIO_FREE_OBJECT_INTERVAL	60 //it's useless if ST_ASIO_REUSE_OBJECT macro been defined
Y
youngwolf 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
//#define ASCS_FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer
#define ASCS_ENHANCED_STABILITY
//#define ASCS_FULL_STATISTIC //full statistic will slightly impact efficiency.
//#define ASCS_USE_STEADY_TIMER

//use the following macro to control the type of packer and unpacker
#define PACKER_UNPACKER_TYPE	0
//0-default packer and unpacker, head(length) + body
//1-default replaceable_packer and replaceable_unpacker, head(length) + body
//2-fixed length unpacker
//3-prefix and suffix packer and unpacker

#if 1 == PACKER_UNPACKER_TYPE
#define ASCS_DEFAULT_PACKER replaceable_packer
#define ASCS_DEFAULT_UNPACKER replaceable_unpacker
#elif 2 == PACKER_UNPACKER_TYPE
#define ASCS_DEFAULT_UNPACKER fixed_length_unpacker
#elif 3 == PACKER_UNPACKER_TYPE
#define ASCS_DEFAULT_PACKER prefix_suffix_packer
#define ASCS_DEFAULT_UNPACKER prefix_suffix_unpacker
#endif
//configuration

W
wolf 已提交
32
#include <ascs/ext/tcp.h>
Y
youngwolf 已提交
33
using namespace ascs;
W
wolf 已提交
34
using namespace ascs::tcp;
Y
youngwolf 已提交
35
using namespace ascs::ext;
W
wolf 已提交
36
using namespace ascs::ext::tcp;
Y
youngwolf 已提交
37 38 39 40 41 42 43 44 45

#define QUIT_COMMAND	"quit"
#define RESTART_COMMAND	"restart"
#define LIST_ALL_CLIENT	"list_all_client"
#define LIST_STATUS		"status"
#define SUSPEND_COMMAND	"suspend"
#define RESUME_COMMAND	"resume"

//demonstrate how to use custom packer
W
wolf 已提交
46
//under the default behavior, each ascs::tcp::socket has their own packer, and cause memory waste
Y
youngwolf 已提交
47 48 49 50
//at here, we make each echo_socket use the same global packer for memory saving
//notice: do not do this for unpacker, because unpacker has member variables and can't share each other
auto global_packer(std::make_shared<ASCS_DEFAULT_PACKER>());

W
wolf 已提交
51
//demonstrate how to control the type of ascs::tcp::server_socket_base::server from template parameter
Y
youngwolf 已提交
52 53 54 55 56 57
class i_echo_server : public i_server
{
public:
	virtual void test() = 0;
};

W
wolf 已提交
58
class echo_socket : public server_socket_base<ASCS_DEFAULT_PACKER, ASCS_DEFAULT_UNPACKER, i_echo_server>
Y
youngwolf 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
{
public:
	echo_socket(i_echo_server& server_) : server_socket_base(server_)
	{
		inner_packer(global_packer);

#if 2 == PACKER_UNPACKER_TYPE
		std::dynamic_pointer_cast<ASCS_DEFAULT_UNPACKER>(inner_unpacker())->fixed_length(1024);
#elif 3 == PACKER_UNPACKER_TYPE
		std::dynamic_pointer_cast<ASCS_DEFAULT_UNPACKER>(inner_unpacker())->prefix_suffix("begin", "end");
#endif
	}

public:
	//because we use objects pool(REUSE_OBJECT been defined), so, strictly speaking, this virtual
	//function must be rewrote, but we don't have member variables to initialize but invoke father's
	//reset() directly, so, it can be omitted, but we keep it for possibly future using
	virtual void reset() {server_socket_base::reset();}

protected:
	virtual void on_recv_error(const asio::error_code& ec)
	{
W
wolf 已提交
81
		//the type of ascs::tcp::server_socket_base::server now can be controlled by derived class(echo_socket),
Y
youngwolf 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
		//which is actually i_echo_server, so, we can invoke i_echo_server::test virtual function.
		server.test();
		server_socket_base::on_recv_error(ec);
	}

	//msg handling: send the original msg back(echo server)
#ifndef ASCS_FORCE_TO_USE_MSG_RECV_BUFFER
	//this virtual function doesn't exists if ASCS_FORCE_TO_USE_MSG_RECV_BUFFER been defined
	virtual bool on_msg(out_msg_type& msg)
	{
	#if 2 == PACKER_UNPACKER_TYPE
		//we don't have fixed_length_packer, so use packer instead, but need to pack msgs with native manner.
		return post_native_msg(msg.data(), msg.size());
	#else
		return post_msg(msg.data(), msg.size());
	#endif
	}
#endif
	//we should handle msg in on_msg_handle for time-consuming task like this:
	virtual bool on_msg_handle(out_msg_type& msg, bool link_down)
	{
	#if 2 == PACKER_UNPACKER_TYPE
		//we don't have fixed_length_packer, so use packer instead, but need to pack msgs with native manner.
		return send_native_msg(msg.data(), msg.size());
	#else
		return send_msg(msg.data(), msg.size());
	#endif
	}
W
wolf 已提交
110
	//please remember that we have defined ASCS_FORCE_TO_USE_MSG_RECV_BUFFER, so, ascs::tcp::socket will directly
Y
youngwolf 已提交
111 112 113 114
	//use msg recv buffer, and we need not rewrite on_msg(), which doesn't exists any more
	//msg handling end
};

W
wolf 已提交
115
class echo_server : public server_base<echo_socket, object_pool<echo_socket>, i_echo_server>
Y
youngwolf 已提交
116 117 118 119 120 121 122
{
public:
	echo_server(service_pump& service_pump_) : server_base(service_pump_) {}

	echo_socket::statistic get_statistic()
	{
		echo_socket::statistic stat;
Y
youngwolf 已提交
123
		do_something_to_all([&stat](const auto& item) {stat += item->get_statistic();});
Y
youngwolf 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

		return stat;
	}

	//from i_echo_server, pure virtual function, we must implement it.
	virtual void test() {/*puts("in echo_server::test()");*/}
};

int main(int argc, const char* argv[])
{
	printf("usage: asio_server [<service thread number=1> [<port=%d> [ip=0.0.0.0]]]\n", ASCS_SERVER_PORT);
	puts("normal server's port will be 100 larger.");
	if (argc >= 2 && (0 == strcmp(argv[1], "--help") || 0 == strcmp(argv[1], "-h")))
		return 0;
	else
		puts("type " QUIT_COMMAND " to end.");

	service_pump sp;
W
wolf 已提交
142 143 144 145 146
	//only need a simple server? you can directly use server or ascs::tcp::server_base.
	//because we use ascs::tcp::server_socket_base directly, so this server cannot support fixed_length_unpacker and prefix_suffix_packer/prefix_suffix_unpacker,
	//the reason is these packer and unpacker need additional initializations that ascs::tcp::server_socket_base not implemented, see echo_socket's constructor for more details.
	typedef server_socket_base<packer, unpacker> normal_server_socket;
	server_base<normal_server_socket> server_(sp);
Y
youngwolf 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	echo_server echo_server_(sp); //echo server

	if (argc > 3)
	{
		server_.set_server_addr(atoi(argv[2]) + 100, argv[3]);
		echo_server_.set_server_addr(atoi(argv[2]), argv[3]);
	}
	else if (argc > 2)
	{
		server_.set_server_addr(atoi(argv[2]) + 100);
		echo_server_.set_server_addr(atoi(argv[2]));
	}
	else
		server_.set_server_addr(ASCS_SERVER_PORT + 100);

	auto thread_num = 1;
	if (argc > 1)
		thread_num = std::min(16, std::max(thread_num, atoi(argv[1])));

#if 3 == PACKER_UNPACKER_TYPE
		global_packer->prefix_suffix("begin", "end");
#endif

	sp.start_service(thread_num);
	while(sp.is_running())
	{
		std::string str;
		std::cin >> str;
		if (QUIT_COMMAND == str)
			sp.stop_service();
		else if (RESTART_COMMAND == str)
		{
			sp.stop_service();
			sp.start_service(thread_num);
		}
		else if (LIST_STATUS == str)
		{
			printf("normal server, link #: " ASCS_SF ", invalid links: " ASCS_SF "\n", server_.size(), server_.invalid_object_size());
			printf("echo server, link #: " ASCS_SF ", invalid links: " ASCS_SF "\n", echo_server_.size(), echo_server_.invalid_object_size());
			puts("");
			puts(echo_server_.get_statistic().to_string().data());
		}
		//the following two commands demonstrate how to suspend msg dispatching, no matter recv buffer been used or not
		else if (SUSPEND_COMMAND == str)
Y
youngwolf 已提交
191
			echo_server_.do_something_to_all([](const auto& item) {item->suspend_dispatch_msg(true);});
Y
youngwolf 已提交
192
		else if (RESUME_COMMAND == str)
Y
youngwolf 已提交
193
			echo_server_.do_something_to_all([](const auto& item) {item->suspend_dispatch_msg(false);});
Y
youngwolf 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
		else if (LIST_ALL_CLIENT == str)
		{
			puts("clients from normal server:");
			server_.list_all_object();
			puts("clients from echo server:");
			echo_server_.list_all_object();
		}
		else
		{
			//broadcast series functions call pack_msg for each client respectively, because clients may used different protocols(so different type of packers, of course)
//			server_.broadcast_msg(str.data(), str.size() + 1);
			//send \0 character too, because asio_client used basic_buffer as its msg type, it will not append \0 character automatically as std::string does,
			//so need \0 character when printing it.

			//if all clients used the same protocol, we can pack msg one time, and send it repeatedly like this:
			packer p;
			auto msg = p.pack_msg(str.data(), str.size() + 1);
			//send \0 character too, because asio_client used basic_buffer as its msg type, it will not append \0 character automatically as std::string does,
			//so need \0 character when printing it.
			if (!msg.empty())
Y
youngwolf 已提交
214
				server_.do_something_to_all([&msg](const auto& item) {item->direct_send_msg(msg);});
Y
youngwolf 已提交
215 216 217

			//if asio_client is using stream_unpacker
//			if (!str.empty())
Y
youngwolf 已提交
218
//				server_.do_something_to_all([&str](const auto& item) {item->direct_send_msg(str);});
Y
youngwolf 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
		}
	}

	return 0;
}

//restore configuration
#undef ASCS_SERVER_PORT
#undef ASCS_ASYNC_ACCEPT_NUM
#undef ASCS_REUSE_OBJECT
#undef ASCS_FREE_OBJECT_INTERVAL
#undef ASCS_FORCE_TO_USE_MSG_RECV_BUFFER
#undef ASCS_ENHANCED_STABILITY
#undef ASCS_FULL_STATISTIC
#undef ASCS_DEFAULT_PACKER
#undef ASCS_DEFAULT_UNPACKER
#undef ASCS_USE_STEADY_TIMER
//restore configuration