1 #include "dvlnet/tcp_server.h"
2 
3 #include <functional>
4 #include <chrono>
5 
6 #include "dvlnet/base.h"
7 
8 namespace dvl {
9 namespace net {
10 
tcp_server(asio::io_context & ioc,std::string bindaddr,unsigned short port,std::string pw)11 tcp_server::tcp_server(asio::io_context &ioc, std::string bindaddr,
12     unsigned short port, std::string pw)
13     : ioc(ioc)
14     , pktfty(pw)
15 {
16 	auto addr = asio::ip::address::from_string(bindaddr);
17 	auto ep = asio::ip::tcp::endpoint(addr, port);
18 	acceptor.reset(new asio::ip::tcp::acceptor(ioc, ep, true));
19 	start_accept();
20 }
21 
localhost_self()22 std::string tcp_server::localhost_self()
23 {
24 	auto addr = acceptor->local_endpoint().address();
25 	if (addr.is_unspecified()) {
26 		if (addr.is_v4()) {
27 			return asio::ip::address_v4::loopback().to_string();
28 		} else if (addr.is_v6()) {
29 			return asio::ip::address_v6::loopback().to_string();
30 		}
31 		ABORT();
32 	}
33 	return addr.to_string();
34 }
35 
make_connection()36 tcp_server::scc tcp_server::make_connection()
37 {
38 	return std::make_shared<client_connection>(ioc);
39 }
40 
next_free()41 plr_t tcp_server::next_free()
42 {
43 	for (plr_t i = 0; i < MAX_PLRS; ++i)
44 		if (!connections[i])
45 			return i;
46 	return PLR_BROADCAST;
47 }
48 
empty()49 bool tcp_server::empty()
50 {
51 	for (plr_t i = 0; i < MAX_PLRS; ++i)
52 		if (connections[i])
53 			return false;
54 	return true;
55 }
56 
start_recv(scc con)57 void tcp_server::start_recv(scc con)
58 {
59 	con->socket.async_receive(asio::buffer(con->recv_buffer),
60 	    std::bind(&tcp_server::handle_recv, this, con,
61 	        std::placeholders::_1,
62 	        std::placeholders::_2));
63 }
64 
handle_recv(scc con,const asio::error_code & ec,size_t bytes_read)65 void tcp_server::handle_recv(scc con, const asio::error_code &ec,
66     size_t bytes_read)
67 {
68 	if (ec || bytes_read == 0) {
69 		drop_connection(con);
70 		return;
71 	}
72 	con->recv_buffer.resize(bytes_read);
73 	con->recv_queue.write(std::move(con->recv_buffer));
74 	con->recv_buffer.resize(frame_queue::max_frame_size);
75 	while (con->recv_queue.packet_ready()) {
76 		try {
77 			auto pkt = pktfty.make_packet(con->recv_queue.read_packet());
78 			if (con->plr == PLR_BROADCAST) {
79 				handle_recv_newplr(con, *pkt);
80 			} else {
81 				con->timeout = timeout_active;
82 				handle_recv_packet(*pkt);
83 			}
84 		} catch (dvlnet_exception &e) {
85 			SDL_Log("Network error: %s", e.what());
86 			drop_connection(con);
87 			return;
88 		}
89 	}
90 	start_recv(con);
91 }
92 
send_connect(scc con)93 void tcp_server::send_connect(scc con)
94 {
95 	auto pkt = pktfty.make_packet<PT_CONNECT>(PLR_MASTER, PLR_BROADCAST,
96 	    con->plr);
97 	send_packet(*pkt);
98 }
99 
handle_recv_newplr(scc con,packet & pkt)100 void tcp_server::handle_recv_newplr(scc con, packet &pkt)
101 {
102 	auto newplr = next_free();
103 	if (newplr == PLR_BROADCAST)
104 		throw server_exception();
105 	if (empty())
106 		game_init_info = pkt.info();
107 	auto reply = pktfty.make_packet<PT_JOIN_ACCEPT>(PLR_MASTER, PLR_BROADCAST,
108 	    pkt.cookie(), newplr,
109 	    game_init_info);
110 	start_send(con, *reply);
111 	con->plr = newplr;
112 	connections[newplr] = con;
113 	con->timeout = timeout_active;
114 	send_connect(con);
115 }
116 
handle_recv_packet(packet & pkt)117 void tcp_server::handle_recv_packet(packet &pkt)
118 {
119 	send_packet(pkt);
120 }
121 
send_packet(packet & pkt)122 void tcp_server::send_packet(packet &pkt)
123 {
124 	if (pkt.dest() == PLR_BROADCAST) {
125 		for (auto i = 0; i < MAX_PLRS; ++i)
126 			if (i != pkt.src() && connections[i])
127 				start_send(connections[i], pkt);
128 	} else {
129 		if (pkt.dest() >= MAX_PLRS)
130 			throw server_exception();
131 		if ((pkt.dest() != pkt.src()) && connections[pkt.dest()])
132 			start_send(connections[pkt.dest()], pkt);
133 	}
134 }
135 
start_send(scc con,packet & pkt)136 void tcp_server::start_send(scc con, packet &pkt)
137 {
138 	const auto *frame = new buffer_t(frame_queue::make_frame(pkt.data()));
139 	auto buf = asio::buffer(*frame);
140 	asio::async_write(con->socket, buf,
141 	    [this, con, frame](const asio::error_code &ec, size_t bytes_sent) {
142 		    handle_send(con, ec, bytes_sent);
143 		    delete frame;
144 	    });
145 }
146 
handle_send(scc con,const asio::error_code & ec,size_t bytes_sent)147 void tcp_server::handle_send(scc con, const asio::error_code &ec,
148     size_t bytes_sent)
149 {
150 	// empty for now
151 }
152 
start_accept()153 void tcp_server::start_accept()
154 {
155 	auto nextcon = make_connection();
156 	acceptor->async_accept(nextcon->socket,
157 	    std::bind(&tcp_server::handle_accept,
158 	        this, nextcon,
159 	        std::placeholders::_1));
160 }
161 
handle_accept(scc con,const asio::error_code & ec)162 void tcp_server::handle_accept(scc con, const asio::error_code &ec)
163 {
164 	if (ec)
165 		return;
166 	if (next_free() == PLR_BROADCAST) {
167 		drop_connection(con);
168 	} else {
169 		asio::ip::tcp::no_delay option(true);
170 		con->socket.set_option(option);
171 		con->timeout = timeout_connect;
172 		start_recv(con);
173 		start_timeout(con);
174 	}
175 	start_accept();
176 }
177 
start_timeout(scc con)178 void tcp_server::start_timeout(scc con)
179 {
180 	con->timer.expires_after(std::chrono::seconds(1));
181 	con->timer.async_wait(std::bind(&tcp_server::handle_timeout, this, con,
182 	    std::placeholders::_1));
183 }
184 
handle_timeout(scc con,const asio::error_code & ec)185 void tcp_server::handle_timeout(scc con, const asio::error_code &ec)
186 {
187 	if (ec) {
188 		drop_connection(con);
189 		return;
190 	}
191 	if (con->timeout > 0)
192 		con->timeout -= 1;
193 	if (con->timeout < 0)
194 		con->timeout = 0;
195 	if (!con->timeout) {
196 		drop_connection(con);
197 		return;
198 	}
199 	start_timeout(con);
200 }
201 
drop_connection(scc con)202 void tcp_server::drop_connection(scc con)
203 {
204 	if (con->plr != PLR_BROADCAST) {
205 		auto pkt = pktfty.make_packet<PT_DISCONNECT>(PLR_MASTER, PLR_BROADCAST,
206 		    con->plr, LEAVE_DROP);
207 		connections[con->plr] = NULL;
208 		send_packet(*pkt);
209 		// TODO: investigate if it is really ok for the server to
210 		//       drop a client directly.
211 	}
212 	con->timer.cancel();
213 	con->socket.close();
214 }
215 
close()216 void tcp_server::close()
217 {
218 	acceptor->close();
219 }
220 
~tcp_server()221 tcp_server::~tcp_server()
222 {
223 }
224 
225 } // namespace net
226 } // namespace dvl
227