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

#include <iostream>

//configuration
#define ASCS_SERVER_PORT		9527
#define ASCS_REUSE_OBJECT //use objects pool
Y
youngwolf 已提交
7
//#define ASCS_FREE_OBJECT_INTERVAL	60 //it's useless if ASCS_REUSE_OBJECT macro been defined
Y
youngwolf 已提交
8
#define ASCS_DISPATCH_BATCH_MSG
Y
youngwolf 已提交
9
#define ASCS_ENHANCED_STABILITY
10
//#define ASCS_FULL_STATISTIC //full statistic will slightly impact efficiency
Y
youngwolf 已提交
11
//#define ASCS_USE_STEADY_TIMER
Y
youngwolf 已提交
12 13
#define ASCS_AVOID_AUTO_STOP_SERVICE
#define ASCS_DECREASE_THREAD_AT_RUNTIME
Y
youngowlf 已提交
14 15 16 17
//#define ASCS_MAX_MSG_NUM		16
//if there's a huge number of links, please reduce messge buffer via ASCS_MAX_MSG_NUM macro.
//please think about if we have 512 links, how much memory we can accupy at most with default ASCS_MAX_MSG_NUM?
//it's 2 * 1024 * 1024 * 512 = 1G
Y
youngwolf 已提交
18 19 20 21

//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
W
wolf 已提交
22 23 24
//1-replaceable packer and unpacker, head(length) + body
//2-fixed length packer and unpacker
//3-prefix and/or suffix packer and unpacker
Y
youngwolf 已提交
25 26

#if 1 == PACKER_UNPACKER_TYPE
Y
youngwolf 已提交
27 28 29
#if defined(_MSC_VER) && _MSC_VER <= 1800
#define ASCS_DEFAULT_PACKER replaceable_packer<shared_buffer<i_buffer>>
#else
Y
youngwolf 已提交
30
#define ASCS_DEFAULT_PACKER replaceable_packer<>
Y
youngwolf 已提交
31
#endif
Y
youngwolf 已提交
32
#define ASCS_DEFAULT_UNPACKER replaceable_unpacker<>
Y
youngwolf 已提交
33
#elif 2 == PACKER_UNPACKER_TYPE
Y
youngwolf 已提交
34 35
#undef ASCS_HEARTBEAT_INTERVAL
#define ASCS_HEARTBEAT_INTERVAL	0 //not support heartbeat
36
#define ASCS_DEFAULT_PACKER fixed_length_packer
Y
youngwolf 已提交
37 38
#define ASCS_DEFAULT_UNPACKER fixed_length_unpacker
#elif 3 == PACKER_UNPACKER_TYPE
Y
youngwolf 已提交
39 40
#undef ASCS_HEARTBEAT_INTERVAL
#define ASCS_HEARTBEAT_INTERVAL	0 //not support heartbeat
Y
youngwolf 已提交
41 42 43 44 45
#define ASCS_DEFAULT_PACKER prefix_suffix_packer
#define ASCS_DEFAULT_UNPACKER prefix_suffix_unpacker
#endif
//configuration

W
wolf 已提交
46
#include <ascs/ext/tcp.h>
Y
youngwolf 已提交
47
using namespace ascs;
W
wolf 已提交
48
using namespace ascs::tcp;
Y
youngwolf 已提交
49
using namespace ascs::ext;
W
wolf 已提交
50
using namespace ascs::ext::tcp;
Y
youngwolf 已提交
51 52 53 54

#define QUIT_COMMAND	"quit"
#define RESTART_COMMAND	"restart"
#define LIST_ALL_CLIENT	"list_all_client"
55 56
#define STATISTIC		"statistic"
#define STATUS			"status"
Y
youngwolf 已提交
57 58
#define INCREASE_THREAD	"increase_thread"
#define DECREASE_THREAD	"decrease_thread"
Y
youngwolf 已提交
59 60

//demonstrate how to use custom packer
Y
youngwolf 已提交
61
//under the default behavior, each tcp::socket has their own packer, and cause memory waste
Y
youngwolf 已提交
62 63 64 65
//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>());

Y
youngwolf 已提交
66
//demonstrate how to control the type of tcp::server_socket_base::server from template parameter
Y
youngwolf 已提交
67 68 69 70 71 72
class i_echo_server : public i_server
{
public:
	virtual void test() = 0;
};

W
wolf 已提交
73
class echo_socket : public server_socket_base<ASCS_DEFAULT_PACKER, ASCS_DEFAULT_UNPACKER, i_echo_server>
Y
youngwolf 已提交
74 75 76 77
{
public:
	echo_socket(i_echo_server& server_) : server_socket_base(server_)
	{
Y
youngwolf 已提交
78
		packer(global_packer);
Y
youngwolf 已提交
79 80

#if 2 == PACKER_UNPACKER_TYPE
Y
youngwolf 已提交
81
		std::dynamic_pointer_cast<ASCS_DEFAULT_UNPACKER>(unpacker())->fixed_length(1024);
Y
youngwolf 已提交
82
#elif 3 == PACKER_UNPACKER_TYPE
Y
youngwolf 已提交
83
		std::dynamic_pointer_cast<ASCS_DEFAULT_UNPACKER>(unpacker())->prefix_suffix("begin", "end");
Y
youngwolf 已提交
84 85 86 87 88 89 90 91 92 93 94 95
#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)
	{
Y
youngwolf 已提交
96
		//the type of tcp::server_socket_base::server now can be controlled by derived class(echo_socket),
Y
youngwolf 已提交
97
		//which is actually i_echo_server, so, we can invoke i_echo_server::test virtual function.
Y
youngwolf 已提交
98
		get_server().test();
Y
youngwolf 已提交
99 100 101 102
		server_socket_base::on_recv_error(ec);
	}

	//msg handling: send the original msg back(echo server)
Y
youngwolf 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115
#ifdef ASCS_DISPATCH_BATCH_MSG
	virtual size_t on_msg_handle(out_queue_type& can)
	{
		if (!is_send_buffer_available())
			return 0;

		out_container_type tmp_can;
		can.swap(tmp_can);

		ascs::do_something_to_all(tmp_can, [this](out_msg_type& msg) {this->send_msg(msg.data(), msg.size(), true);});
		return tmp_can.size();
	}
#else
Y
youngwolf 已提交
116
	virtual bool on_msg_handle(out_msg_type& msg) {return send_msg(msg.data(), msg.size());}
Y
youngwolf 已提交
117
#endif
Y
youngwolf 已提交
118 119 120
	//msg handling end
};

W
wolf 已提交
121
class echo_server : public server_base<echo_socket, object_pool<echo_socket>, i_echo_server>
Y
youngwolf 已提交
122 123 124 125 126 127 128 129
{
public:
	echo_server(service_pump& service_pump_) : server_base(service_pump_) {}

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

Y
youngwolf 已提交
130 131 132 133 134 135 136 137 138 139
#if ASCS_HEARTBEAT_INTERVAL > 0
typedef server_socket_base<packer, unpacker> normal_socket;
#else
//demonstrate how to open heartbeat function without defining macro ASCS_HEARTBEAT_INTERVAL
class normal_socket : public server_socket_base<packer, unpacker>
{
public:
	normal_socket(i_server& server_) : server_socket_base(server_) {}

protected:
Y
youngwolf 已提交
140 141 142
	//demo client needs heartbeat (macro ASCS_HEARTBEAT_INTERVAL been defined), pleae note that the interval (here is 5) must be equal to
	//macro ASCS_HEARTBEAT_INTERVAL defined in demo client, and macro ASCS_HEARTBEAT_MAX_ABSENCE must has the same value as demo client's.
	virtual void on_connect() {start_heartbeat(5);}
Y
youngwolf 已提交
143 144 145
};
#endif

Y
youngwolf 已提交
146 147
int main(int argc, const char* argv[])
{
148
	printf("usage: %s [<service thread number=1> [<port=%d> [ip=0.0.0.0]]]\n", argv[0], ASCS_SERVER_PORT);
Y
youngwolf 已提交
149 150 151 152 153 154 155
	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;
Y
youngwolf 已提交
156
	//only need a simple server? you can directly use server or tcp::server_base, because of normal_socket,
Y
youngwolf 已提交
157 158 159 160
	//this server cannot support fixed_length_packer/fixed_length_unpacker and prefix_suffix_packer/prefix_suffix_unpacker,
	//the reason is these packer and unpacker need additional initializations that normal_socket not implemented,
	//see echo_socket's constructor for more details.
	server_base<normal_socket> server_(sp);
Y
youngwolf 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
	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
Y
youngwolf 已提交
181
	global_packer->prefix_suffix("begin", "end");
Y
youngwolf 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195
#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);
		}
196
		else if (STATISTIC == str)
Y
youngwolf 已提交
197 198 199 200 201 202
		{
			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());
		}
203 204 205 206 207
		else if (STATUS == str)
		{
			server_.list_all_status();
			echo_server_.list_all_status();
		}
Y
youngwolf 已提交
208 209 210 211 212 213 214
		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();
		}
Y
youngwolf 已提交
215 216 217 218
		else if (INCREASE_THREAD == str)
			sp.add_service_thread(1);
		else if (DECREASE_THREAD == str)
			sp.del_service_thread(1);
Y
youngwolf 已提交
219 220
		else
		{
Y
youngwolf 已提交
221 222 223 224 225
//			/*
			//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 demo 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.
Y
youngwolf 已提交
226
//			*/
Y
youngwolf 已提交
227
			/*
Y
youngwolf 已提交
228 229 230
			//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);
231
			//send \0 character too, because demo client used basic_buffer as its msg type, it will not append \0 character automatically as std::string does,
Y
youngwolf 已提交
232 233
			//so need \0 character when printing it.
			if (!msg.empty())
Y
youngwolf 已提交
234 235 236
				server_.do_something_to_all([&msg](server_base<normal_server_socket>::object_ctype& item) {item->direct_send_msg(msg);});
			*/
			/*
237
			//if demo client is using stream_unpacker
Y
youngwolf 已提交
238 239 240
			if (!str.empty())
				server_.do_something_to_all([&str](server_base<normal_server_socket>::object_ctype& item) {item->direct_send_msg(str);});
			*/
Y
youngwolf 已提交
241 242 243 244 245
		}
	}

	return 0;
}