OSDN Git Service

5c3f2799754871bbe7455dc0e7d8d9c26b2e20e2
[mmo/main.git] / server / Server.cpp
1 //\r
2 // Server.cpp\r
3 //\r
4 \r
5 #include "Server.hpp"\r
6 #include "version.hpp"\r
7 #include <algorithm>\r
8 #include <boost/make_shared.hpp>\r
9 #include <boost/foreach.hpp>\r
10 #include "../common/Logger.hpp"\r
11 #include "../common/network/Command.hpp"\r
12 #include "../common/network/Utils.hpp"\r
13 \r
14 namespace network {\r
15 \r
16     Server::Server(Config& config) :\r
17                         config_(config),\r
18             endpoint_(tcp::v4(), config.port()),\r
19             acceptor_(io_service_, endpoint_),\r
20             socket_udp_(io_service_, udp::endpoint(udp::v4(), config.port())),\r
21             udp_packet_count_(0)\r
22     {\r
23     }\r
24 \r
25     void Server::Start(CallbackFuncPtr callback)\r
26     {\r
27         callback_ = std::make_shared<CallbackFunc>(\r
28                 [&](network::Command c){\r
29 \r
30             // ログアウト\r
31             if (c.header() == network::header::FatalConnectionError) {\r
32                 if (callback) {\r
33                                         (*callback)(c);\r
34                                 }\r
35             } else if (auto session = c.session().lock()) {\r
36                                 auto read_average = session->GetReadByteAverage();\r
37                                 if (read_average > config_.receive_limit_2()) {\r
38                                         Logger::Info(_T("Banished a session: %d %dbyte/s"), session->id(), read_average);\r
39                                         session->Close();\r
40                                 } else if(read_average > config_.receive_limit_1()) {\r
41                                         Logger::Info(_T("Receive limit exceeded: %d: %d byte/s"), session->id(), read_average);\r
42                                 } else {\r
43                                         if (callback) {\r
44                                                 (*callback)(c);\r
45                                         }\r
46                 }\r
47             }\r
48 \r
49         });\r
50 \r
51         {\r
52         auto new_session = boost::make_shared<ServerSession>(io_service_);\r
53         acceptor_.async_accept(new_session->tcp_socket(),\r
54                               boost::bind(&Server::ReceiveSession, this, new_session, boost::asio::placeholders::error));\r
55         }\r
56 \r
57         {\r
58             socket_udp_.async_receive_from(\r
59                 boost::asio::buffer(receive_buf_udp_, UDP_MAX_RECEIVE_LENGTH), sender_endpoint_,\r
60                 boost::bind(&Server::ReceiveUDP, this,\r
61                   boost::asio::placeholders::error,\r
62                   boost::asio::placeholders::bytes_transferred));\r
63         }\r
64 \r
65         boost::asio::io_service::work work(io_service_);\r
66         io_service_.run();\r
67     }\r
68 \r
69     void Server::Stop()\r
70     {\r
71         io_service_.stop();\r
72                 Logger::Info("stop server");\r
73     }\r
74     void Server::Stop(int innterrupt_type)\r
75     {\r
76         io_service_.stop();\r
77         Logger::Info(_T("stop server innterrupt_type=%d"),innterrupt_type);\r
78     }\r
79 \r
80         int Server::GetUserCount() const\r
81         {\r
82                 auto count = std::count_if(sessions_.begin(), sessions_.end(),\r
83                         [](const SessionWeakPtr& s){ return !s.expired() && s.lock()->online(); });\r
84 \r
85                 return count;\r
86         }\r
87 \r
88         std::string Server::GetStatusJSON() const\r
89         {\r
90                 auto msg = (\r
91                                         boost::format("{\"nam\":\"%s\",\"ver\":\"%d.%d.%d\",\"cnt\":%d,\"cap\":%d,\"stg\":\"%s\"}")\r
92                                                 % config_.server_name()\r
93                                                 % MMO_VERSION_MAJOR % MMO_VERSION_MINOR % MMO_VERSION_REVISION\r
94                                                 % GetUserCount()\r
95                                                 % config_.capacity()\r
96                                                 % config_.stage()\r
97                                         ).str();\r
98 \r
99                 return msg;\r
100         }\r
101 \r
102         std::string Server::GetFullStatus() const\r
103         {\r
104                 using namespace boost::property_tree;\r
105                 ptree xml_ptree;\r
106 \r
107                 xml_ptree.put_child("config", config_.pt());\r
108 \r
109                 std::stringstream stream;\r
110                 write_xml(stream, xml_ptree);\r
111                 return stream.str();\r
112         }\r
113 \r
114     bool Server::Empty() const\r
115     {\r
116         return GetUserCount() == 0;\r
117     }\r
118 \r
119         bool Server::IsBlockedAddress(const boost::asio::ip::address& address)\r
120         {\r
121                 BOOST_FOREACH(const auto& pattern, config_.blocking_address_patterns()) {\r
122                         if (network::Utils::MatchWithWildcard(pattern, address.to_string())) {\r
123                                 return true;\r
124                         }\r
125                 }\r
126                 return false;\r
127         }\r
128 \r
129     void Server::ReceiveSession(const SessionPtr& session, const boost::system::error_code& error)\r
130     {\r
131                 \r
132                 config_.Reload();\r
133 \r
134                 const auto address = session->tcp_socket().remote_endpoint().address();\r
135 \r
136                 // 拒否IPでないか判定\r
137                 if(IsBlockedAddress(address)) {\r
138                         Logger::Info("Blocked IP Address: %s", address);\r
139             session->Close();\r
140 \r
141                 } else {\r
142             session->set_on_receive(callback_);\r
143             session->Start();\r
144             sessions_.push_back(SessionWeakPtr(session));\r
145 \r
146             // クライアント情報を要求\r
147             session->Send(ClientRequestedClientInfo());\r
148         }\r
149 \r
150         auto new_session = boost::make_shared<ServerSession>(io_service_);\r
151          acceptor_.async_accept(new_session->tcp_socket(),\r
152                  boost::bind(&Server::ReceiveSession, this, new_session, boost::asio::placeholders::error));\r
153 \r
154                 RefreshSession();\r
155     }\r
156 \r
157         void Server::RefreshSession()\r
158         {\r
159                 // 使用済のセッションのポインタを破棄\r
160         auto it = std::remove_if(sessions_.begin(), sessions_.end(),\r
161                 [](const SessionWeakPtr& ptr){\r
162             return ptr.expired();\r
163         });\r
164         sessions_.erase(it, sessions_.end());\r
165                 Logger::Info("Active connection: %d", GetUserCount());\r
166         }\r
167 \r
168     void Server::SendAll(const Command& command, int channel, bool limited)\r
169     {\r
170         BOOST_FOREACH(SessionWeakPtr& ptr, sessions_) {\r
171             if (auto session = ptr.lock()) {\r
172                                 if (channel < 0 || (channel >= 0 && session->channel() == channel)) {\r
173                                         if (!limited || session->write_average_limit() > session->GetWriteByteAverage()) {\r
174                                                 session->Send(command);\r
175                                         }\r
176                                 }\r
177             }\r
178         }\r
179     }\r
180 \r
181     void Server::SendOthers(const Command& command, uint32_t self_id, int channel, bool limited)\r
182     {\r
183         BOOST_FOREACH(SessionWeakPtr& ptr, sessions_) {\r
184             if (auto session = ptr.lock()) {\r
185                                 if (channel < 0 || (channel >= 0 && session->channel() == channel)) {\r
186                                         if (!limited || session->write_average_limit() > session->GetWriteByteAverage()) {\r
187                                                 if (session->id() != self_id) {\r
188                                                         session->Send(command);\r
189                                                 }\r
190                                         }\r
191                                 }\r
192             }\r
193         }\r
194     }\r
195         \r
196     void Server::SendTo(const Command& command, uint32_t user_id)\r
197         {\r
198                 auto it = std::find_if(sessions_.begin(), sessions_.end(),\r
199                         [user_id](SessionWeakPtr& ptr){\r
200                                 return ptr.lock()->id() == user_id;\r
201                         });\r
202                 \r
203                 if (it != sessions_.end()) {\r
204                         it->lock()->Send(command);\r
205                 }\r
206         }\r
207 \r
208     void Server::SendUDPTestPacket(const std::string& ip_address, uint16_t port)\r
209     {\r
210         using boost::asio::ip::udp;\r
211 \r
212         std::stringstream port_str;\r
213         port_str << (int)port;\r
214 \r
215         udp::resolver resolver(io_service_);\r
216         udp::resolver::query query(udp::v4(), ip_address.c_str(), port_str.str().c_str());\r
217         udp::resolver::iterator iterator = resolver.resolve(query);\r
218 \r
219         static char request[] = "MMO UDP Test Packet";\r
220         for (int i = 0; i < UDP_TEST_PACKET_TIME; i++) {\r
221 \r
222             io_service_.post(boost::bind(&Server::DoWriteUDP, this, request, *iterator));\r
223         }\r
224     }\r
225 \r
226     void Server::SendUDP(const std::string& message, const boost::asio::ip::udp::endpoint endpoint)\r
227     {\r
228                 io_service_.post(boost::bind(&Server::DoWriteUDP, this, message, endpoint));\r
229     }\r
230 \r
231     void Server::ReceiveUDP(const boost::system::error_code& error, size_t bytes_recvd)\r
232     {\r
233         if (bytes_recvd > 0) {\r
234             std::string buffer(receive_buf_udp_, bytes_recvd);\r
235             FetchUDP(buffer, sender_endpoint_);\r
236         }\r
237         if (!error) {\r
238           socket_udp_.async_receive_from(\r
239               boost::asio::buffer(receive_buf_udp_, UDP_MAX_RECEIVE_LENGTH), sender_endpoint_,\r
240               boost::bind(&Server::ReceiveUDP, this,\r
241                 boost::asio::placeholders::error,\r
242                 boost::asio::placeholders::bytes_transferred));\r
243         } else {\r
244             Logger::Error("%s", error.message());\r
245         }\r
246     }\r
247 \r
248     void Server::DoWriteUDP(const std::string& msg, const udp::endpoint& endpoint)\r
249     {\r
250         boost::shared_ptr<std::string> s = \r
251               boost::make_shared<std::string>(msg.data(), msg.size());\r
252 \r
253         socket_udp_.async_send_to(\r
254             boost::asio::buffer(s->data(), s->size()), endpoint,\r
255             boost::bind(&Server::WriteUDP, this,\r
256               boost::asio::placeholders::error, s));\r
257     }\r
258 \r
259     void Server::WriteUDP(const boost::system::error_code& error, boost::shared_ptr<std::string> holder)\r
260     {\r
261 //        if (!error) {\r
262 //            if (!send_queue_.empty()) {\r
263 //                  send_queue_.pop();\r
264 //                  if (!send_queue_.empty())\r
265 //                  {\r
266 //                    boost::asio::async_write(socket_tcp_,\r
267 //                        boost::asio::buffer(send_queue_.front().data(),\r
268 //                          send_queue_.front().size()),\r
269 //                        boost::bind(&Session::WriteTCP, this,\r
270 //                          boost::asio::placeholders::error));\r
271 //                  }\r
272 //            }\r
273 //        } else {\r
274 //            FatalError();\r
275 //        }\r
276     }\r
277 \r
278     void Server::FetchUDP(const std::string& buffer, const boost::asio::ip::udp::endpoint endpoint)\r
279     {\r
280         uint8_t header;\r
281         std::string body;\r
282         SessionWeakPtr session;\r
283 \r
284                 // IPアドレスとポートからセッションを特定\r
285                 auto it = std::find_if(sessions_.begin(), sessions_.end(),\r
286                         [&endpoint](const SessionWeakPtr& session) -> bool {\r
287                                 if (auto session_ptr = session.lock()) {\r
288 \r
289                                         const auto session_endpoint = session_ptr->tcp_socket().remote_endpoint();\r
290                                         const auto session_port = session_ptr->udp_port();\r
291 \r
292                                         return (session_endpoint.address() == endpoint.address() &&\r
293                                                 session_port == endpoint.port());\r
294 \r
295                                 } else {\r
296                                         return false;\r
297                                 }\r
298                         });\r
299 \r
300                 if (it != sessions_.end()) {\r
301                         session = *it;\r
302                         Logger::Debug("Receive UDP Command: %d", session.lock()->id());\r
303                 } else {\r
304                         Logger::Debug("Receive anonymous UDP Command");\r
305                 }\r
306 \r
307         if (buffer.size() > network::Utils::Deserialize(buffer, &header)) {\r
308                         body = buffer.substr(sizeof(header));\r
309                 }\r
310 \r
311         // 復号\r
312         if (session.lock() && header == header::ENCRYPT_HEADER) {\r
313             body.erase(0, sizeof(header));\r
314                         body = session.lock()->encrypter().Decrypt(body);\r
315             Utils::Deserialize(body, &header);\r
316                         body = buffer.substr(sizeof(header));\r
317         }\r
318 \r
319                 if (header == network::header::ServerRequstedStatus) {\r
320                         SendUDP(GetStatusJSON(), endpoint);\r
321                 } else {\r
322                         if (callback_) {\r
323                                 (*callback_)(Command(static_cast<network::header::CommandHeader>(header), body, session));\r
324                         }\r
325                 }\r
326 \r
327     }\r
328 \r
329     void Server::ServerSession::Start()\r
330     {\r
331         online_ = true;\r
332 \r
333         // Nagleアルゴリズムを無効化\r
334         socket_tcp_.set_option(boost::asio::ip::tcp::no_delay(true));\r
335 \r
336                 // バッファサイズを変更 1MiB\r
337                 boost::asio::socket_base::receive_buffer_size option(1048576);\r
338                 socket_tcp_.set_option(option);\r
339 \r
340         // IPアドレスを取得\r
341         global_ip_ = socket_tcp_.remote_endpoint().address().to_string();\r
342 \r
343         boost::asio::async_read_until(socket_tcp_,\r
344             receive_buf_, NETWORK_UTILS_DELIMITOR,\r
345             boost::bind(\r
346               &ServerSession::ReceiveTCP, shared_from_this(),\r
347               boost::asio::placeholders::error));\r
348     }\r
349 }\r