udp_server.cpp
1 // Copyright 2019 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include <thread> 6 #include <boost/asio.hpp> 7 #include "common/common_types.h" 8 #include "common/logging/log.h" 9 #include "core/rpc/packet.h" 10 #include "core/rpc/udp_server.h" 11 12 namespace Core::RPC { 13 14 class UDPServer::Impl { 15 public: 16 explicit Impl(std::function<void(std::unique_ptr<Packet>)> new_request_callback) 17 // Use a random high port 18 // TODO: Make configurable or increment port number on failure 19 : socket(io_context, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 45987)), 20 new_request_callback(std::move(new_request_callback)) { 21 22 StartReceive(); 23 worker_thread = std::thread([this] { io_context.run(); }); 24 } 25 26 ~Impl() { 27 io_context.stop(); 28 worker_thread.join(); 29 } 30 31 private: 32 void StartReceive() { 33 socket.async_receive_from(boost::asio::buffer(request_buffer), remote_endpoint, 34 [this](const boost::system::error_code& error, std::size_t size) { 35 HandleReceive(error, size); 36 }); 37 } 38 39 void HandleReceive(const boost::system::error_code& error, std::size_t size) { 40 if (error) { 41 LOG_WARNING(RPC_Server, "Failed to receive data on UDP socket: {}", error.message()); 42 } else if (size >= MIN_PACKET_SIZE && size <= MAX_PACKET_SIZE) { 43 PacketHeader header; 44 std::memcpy(&header, request_buffer.data(), sizeof(header)); 45 if ((size - MIN_PACKET_SIZE) == header.packet_size) { 46 u8* data = request_buffer.data() + MIN_PACKET_SIZE; 47 std::function<void(Packet&)> send_reply_callback = 48 std::bind(&Impl::SendReply, this, remote_endpoint, std::placeholders::_1); 49 std::unique_ptr<Packet> new_packet = 50 std::make_unique<Packet>(header, data, send_reply_callback); 51 52 // Send the request to the upper layer for handling 53 new_request_callback(std::move(new_packet)); 54 } 55 } else { 56 LOG_WARNING(RPC_Server, "Received message with wrong size: {}", size); 57 } 58 StartReceive(); 59 } 60 61 void SendReply(boost::asio::ip::udp::endpoint endpoint, Packet& reply_packet) { 62 std::vector<u8> reply_buffer(MIN_PACKET_SIZE + reply_packet.GetPacketDataSize()); 63 auto reply_header = reply_packet.GetHeader(); 64 65 std::memcpy(reply_buffer.data(), &reply_header, sizeof(reply_header)); 66 std::memcpy(reply_buffer.data() + (4 * sizeof(u32)), reply_packet.GetPacketData().data(), 67 reply_packet.GetPacketDataSize()); 68 69 boost::system::error_code error; 70 socket.send_to(boost::asio::buffer(reply_buffer), endpoint, 0, error); 71 72 if (error) { 73 LOG_WARNING(RPC_Server, "Failed to send reply: {}", error.message()); 74 } else { 75 LOG_INFO(RPC_Server, "Sent reply version({}) id=({}) type=({}) size=({})", 76 reply_packet.GetVersion(), reply_packet.GetId(), reply_packet.GetPacketType(), 77 reply_packet.GetPacketDataSize()); 78 } 79 } 80 81 std::thread worker_thread; 82 83 boost::asio::io_context io_context; 84 boost::asio::ip::udp::socket socket; 85 std::array<u8, MAX_PACKET_SIZE> request_buffer; 86 boost::asio::ip::udp::endpoint remote_endpoint; 87 88 std::function<void(std::unique_ptr<Packet>)> new_request_callback; 89 }; 90 91 UDPServer::UDPServer(std::function<void(std::unique_ptr<Packet>)> new_request_callback) 92 : impl(std::make_unique<Impl>(new_request_callback)) {} 93 94 UDPServer::~UDPServer() = default; 95 96 } // namespace Core::RPC