5 #include "Server.hpp"
\r
6 #include "version.hpp"
\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
16 Server::Server(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
25 void Server::Start(CallbackFuncPtr callback)
\r
27 callback_ = std::make_shared<CallbackFunc>(
\r
28 [&](network::Command c){
\r
31 if (c.header() == network::header::FatalConnectionError) {
\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
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
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
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
65 boost::asio::io_service::work work(io_service_);
\r
72 Logger::Info("stop server");
\r
74 void Server::Stop(int innterrupt_type)
\r
77 Logger::Info(_T("stop server innterrupt_type=%d"),innterrupt_type);
\r
80 int Server::GetUserCount() const
\r
82 auto count = std::count_if(sessions_.begin(), sessions_.end(),
\r
83 [](const SessionWeakPtr& s){ return !s.expired() && s.lock()->online(); });
\r
88 std::string Server::GetStatusJSON() const
\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
95 % config_.capacity()
\r
102 std::string Server::GetFullStatus() const
\r
104 using namespace boost::property_tree;
\r
107 xml_ptree.put_child("config", config_.pt());
\r
109 std::stringstream stream;
\r
110 write_xml(stream, xml_ptree);
\r
111 return stream.str();
\r
114 bool Server::Empty() const
\r
116 return GetUserCount() == 0;
\r
119 bool Server::IsBlockedAddress(const boost::asio::ip::address& address)
\r
121 BOOST_FOREACH(const auto& pattern, config_.blocking_address_patterns()) {
\r
122 if (network::Utils::MatchWithWildcard(pattern, address.to_string())) {
\r
129 void Server::ReceiveSession(const SessionPtr& session, const boost::system::error_code& error)
\r
134 const auto address = session->tcp_socket().remote_endpoint().address();
\r
137 if(IsBlockedAddress(address)) {
\r
138 Logger::Info("Blocked IP Address: %s", address);
\r
142 session->set_on_receive(callback_);
\r
144 sessions_.push_back(SessionWeakPtr(session));
\r
147 session->Send(ClientRequestedClientInfo());
\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
157 void Server::RefreshSession()
\r
159 // 使用済のセッションのポインタを破棄
\r
160 auto it = std::remove_if(sessions_.begin(), sessions_.end(),
\r
161 [](const SessionWeakPtr& ptr){
\r
162 return ptr.expired();
\r
164 sessions_.erase(it, sessions_.end());
\r
165 Logger::Info("Active connection: %d", GetUserCount());
\r
168 void Server::SendAll(const Command& command, int channel, bool limited)
\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
181 void Server::SendOthers(const Command& command, uint32_t self_id, int channel, bool limited)
\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
196 void Server::SendTo(const Command& command, uint32_t user_id)
\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
203 if (it != sessions_.end()) {
\r
204 it->lock()->Send(command);
\r
208 void Server::SendUDPTestPacket(const std::string& ip_address, uint16_t port)
\r
210 using boost::asio::ip::udp;
\r
212 std::stringstream port_str;
\r
213 port_str << (int)port;
\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
219 static char request[] = "MMO UDP Test Packet";
\r
220 for (int i = 0; i < UDP_TEST_PACKET_TIME; i++) {
\r
222 io_service_.post(boost::bind(&Server::DoWriteUDP, this, request, *iterator));
\r
226 void Server::SendUDP(const std::string& message, const boost::asio::ip::udp::endpoint endpoint)
\r
228 io_service_.post(boost::bind(&Server::DoWriteUDP, this, message, endpoint));
\r
231 void Server::ReceiveUDP(const boost::system::error_code& error, size_t bytes_recvd)
\r
233 if (bytes_recvd > 0) {
\r
234 std::string buffer(receive_buf_udp_, bytes_recvd);
\r
235 FetchUDP(buffer, sender_endpoint_);
\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
244 Logger::Error("%s", error.message());
\r
248 void Server::DoWriteUDP(const std::string& msg, const udp::endpoint& endpoint)
\r
250 boost::shared_ptr<std::string> s =
\r
251 boost::make_shared<std::string>(msg.data(), msg.size());
\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
259 void Server::WriteUDP(const boost::system::error_code& error, boost::shared_ptr<std::string> holder)
\r
262 // if (!send_queue_.empty()) {
\r
263 // send_queue_.pop();
\r
264 // if (!send_queue_.empty())
\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
278 void Server::FetchUDP(const std::string& buffer, const boost::asio::ip::udp::endpoint endpoint)
\r
282 SessionWeakPtr session;
\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
289 const auto session_endpoint = session_ptr->tcp_socket().remote_endpoint();
\r
290 const auto session_port = session_ptr->udp_port();
\r
292 return (session_endpoint.address() == endpoint.address() &&
\r
293 session_port == endpoint.port());
\r
300 if (it != sessions_.end()) {
\r
302 Logger::Debug("Receive UDP Command: %d", session.lock()->id());
\r
304 Logger::Debug("Receive anonymous UDP Command");
\r
307 if (buffer.size() > network::Utils::Deserialize(buffer, &header)) {
\r
308 body = buffer.substr(sizeof(header));
\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
319 if (header == network::header::ServerRequstedStatus) {
\r
320 SendUDP(GetStatusJSON(), endpoint);
\r
323 (*callback_)(Command(static_cast<network::header::CommandHeader>(header), body, session));
\r
329 void Server::ServerSession::Start()
\r
334 socket_tcp_.set_option(boost::asio::ip::tcp::no_delay(true));
\r
337 boost::asio::socket_base::receive_buffer_size option(1048576);
\r
338 socket_tcp_.set_option(option);
\r
341 global_ip_ = socket_tcp_.remote_endpoint().address().to_string();
\r
343 boost::asio::async_read_until(socket_tcp_,
\r
344 receive_buf_, NETWORK_UTILS_DELIMITOR,
\r
346 &ServerSession::ReceiveTCP, shared_from_this(),
\r
347 boost::asio::placeholders::error));
\r