// // Created by bspeice on 10/5/24. // #include #include #include #include #include #include #include #include #include #define CHECK_SYSCALL(expr) [&](){ const auto ret = expr; if (ret < 0) { throw std::runtime_error(#expr); } else { return ret; } }() int main(int argc, char** argv) { if (argc != 2) { throw std::runtime_error("Usage: ./0_smoketest "); } // Get the address tied to our intended interface const auto interface = std::string_view(argv[1]); ifaddrs* interface_addrs{}; CHECK_SYSCALL(getifaddrs(&interface_addrs)); while (interface_addrs) { if (interface == interface_addrs->ifa_name && interface_addrs->ifa_addr->sa_family == AF_INET) { break; } interface_addrs = interface_addrs->ifa_next; } if (!interface_addrs) { throw std::runtime_error("Expected interface not found"); } std::array name_buffer{}; getnameinfo(interface_addrs->ifa_addr, sizeof(sockaddr_in), name_buffer.data(), name_buffer.size(), nullptr, 0, NI_NUMERICHOST); auto* address = reinterpret_cast(interface_addrs->ifa_addr); address->sin_port = htons(3490); // Port must be in network byte order std::cout << "Address: " << std::string(name_buffer.data()) << ' ' << ntohs(address->sin_port) << '\n'; // Set up a socket const auto socket_fd = CHECK_SYSCALL(socket(AF_INET, SOCK_STREAM, 0)); int enable = 1; setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); CHECK_SYSCALL(bind(socket_fd, reinterpret_cast(address), sizeof(sockaddr_in))); CHECK_SYSCALL(listen(socket_fd, 10)); // Set up epoll const auto epoll_fd = CHECK_SYSCALL(epoll_create(10)); epoll_event event{EPOLLIN}; event.data.fd = socket_fd; CHECK_SYSCALL(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event)); std::vector events{10}; std::vector buffer(1024); while (true) { std::cout << "Waiting for events..." << '\n'; const auto num_fds = CHECK_SYSCALL(epoll_wait(epoll_fd, events.data(), events.size(), -1)); std::cout << "Received events=" << num_fds << '\n'; for (auto i = 0; i < num_fds; i++) { if (events[i].data.fd == socket_fd) { // Connection attempt; add to the interest list and continue const auto connection_fd = CHECK_SYSCALL(accept(socket_fd, nullptr, nullptr)); event.data.fd = connection_fd; CHECK_SYSCALL(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connection_fd, &event)); std::cout << "Adding connection=" << connection_fd << '\n'; continue; } // Received data on a connection socket, read it and send it back const auto bytes_read = CHECK_SYSCALL(read(events[i].data.fd, buffer.data(), buffer.size())); std::cout << "Received bytes=" << bytes_read << '\n'; if (bytes_read == 0) { // Connection closed std::cout << "Closing fd=" << events[i].data.fd << '\n'; CHECK_SYSCALL(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &event)); CHECK_SYSCALL(close(events[i].data.fd)); continue; } // Blocking I/O, I know, it's bad std::cout << "Sending bytes=" << bytes_read << '\n'; CHECK_SYSCALL(write(events[i].data.fd, buffer.data(), bytes_read)); } } }