#include "UDP_Server.h" #include namespace Psc::asio_socket { void UDP_Server::create() { socket = std::make_unique(io_context); socket_fd = socket_id(socket.get()); recv_buffer.init(buffer_size); send_buffer.init(buffer_size); recv_storage.assign(static_cast(read_chunk_size), 0); send_storage.assign(static_cast(read_chunk_size), 0); read_pending = false; write_pending = false; state = Not_Bind; } bool UDP_Server::bind() { if (!socket) create(); if (bind_address.ip.empty() || bind_address.port == 0) { state = Not_Set_Field; return false; } asio::error_code ec; auto address_value = asio::ip::make_address(bind_address.ip, ec); if (ec) { state = Not_Bind; return false; } asio::ip::udp::endpoint endpoint( address_value, static_cast(bind_address.port)); socket->open(endpoint.protocol(), ec); if (ec) return false; socket->set_option(asio::socket_base::reuse_address(true), ec); socket->set_option( asio::socket_base::receive_buffer_size(static_cast(buffer_size)), ec); socket->bind(endpoint, ec); if (ec) { state = Not_Bind; return false; } bound = true; state = Working; start_read(); return true; } void UDP_Server::tick() { if (!bound || !socket) return; start_read(); start_write(); io_context.restart(); io_context.poll(); start_read(); start_write(); } void UDP_Server::start_read() { if (read_pending || !bound || !socket || !socket->is_open()) return; if (recv_storage.empty()) recv_storage.assign(static_cast(read_chunk_size), 0); read_pending = true; socket->async_receive_from( asio::buffer(recv_storage.data(), recv_storage.size()), recv_endpoint, [this](const asio::error_code &ec, std::size_t n) { read_pending = false; if (!bound) return; if (ec == asio::error::operation_aborted) return; if (ec) return; last_peer = endpoint_to_sockaddr(recv_endpoint); has_last_peer = true; if (std::find(clients.begin(), clients.end(), last_peer) == clients.end()) { clients.push_back(last_peer); } if (recv_buffer.write(recv_storage.data(), n)) { recv_peers.push_back(last_peer); } start_read(); }); } void UDP_Server::start_write() { if (write_pending || !bound || !socket || !socket->is_open()) return; if (send_endpoints.empty()) return; if (send_storage.empty()) send_storage.assign(static_cast(read_chunk_size), 0); std::size_t out_len = send_storage.size(); if (!send_buffer.peek(send_storage.data(), out_len)) return; send_storage.resize(out_len); auto endpoint = send_endpoints.front(); write_pending = true; socket->async_send_to( asio::buffer(send_storage.data(), send_storage.size()), endpoint, [this](const asio::error_code &ec, std::size_t) { write_pending = false; if (!bound) return; if (ec == asio::error::operation_aborted) return; if (!ec) { send_buffer.skip_one(); if (!send_endpoints.empty()) send_endpoints.erase(send_endpoints.begin()); } send_storage.assign(static_cast(read_chunk_size), 0); start_write(); }); } void UDP_Server::close() { read_pending = false; write_pending = false; if (socket) { asio::error_code ec; socket->cancel(ec); socket->close(ec); } clients.clear(); recv_peers.clear(); send_endpoints.clear(); bound = false; has_last_peer = false; socket_fd = static_cast(-1); state = Not_Created; } std::vector UDP_Server::read() { std::vector ret; std::string data(static_cast(read_chunk_size), '\0'); while (!recv_peers.empty()) { std::size_t out_len = data.size(); if (!recv_buffer.read(data.data(), out_len)) break; ret.push_back({recv_peers.front(), std::string(data.data(), out_len)}); recv_peers.erase(recv_peers.begin()); } return ret; } void UDP_Server::reply_last_peer(const std::string &data) { if (!has_last_peer) return; send_to(last_peer.ip, static_cast(last_peer.port), data); } void UDP_Server::send_to(const std::string &ip, uint16_t port, const std::string &data) { if (data.empty()) return; if (!bound && !bind()) return; asio::error_code ec; auto endpoint = asio::ip::udp::endpoint(asio::ip::make_address(ip, ec), port); if (ec) return; if (send_buffer.write(data.data(), data.size())) { send_endpoints.push_back(endpoint); } start_write(); } void UDP_Server::write_to_all_clients(const std::string &msg) { for (const auto &client : clients) { send_to(client.ip, static_cast(client.port), msg); } } } // namespace Psc::asio_socket