#include #include #include #include #include #include #include #include #include #include #include #include namespace a8 { struct UdpListenerImpl { a8::UdpListener* master = nullptr; int listen_socket = -1; std::thread* worker_thread = nullptr; volatile bool shutdown = false; bool IsActive() { return listen_socket != -1; } void SetActive(bool active) { if (active) { if (IsActive()) { return; } if (!worker_thread) { shutdown = false; worker_thread = new std::thread(&a8::UdpListenerImpl::WorkerThreadProc, this); } } else { if (IsActive()) { ActiveStop(); } } } bool ActiveStart() { listen_socket = ::socket(AF_INET, SOCK_DGRAM, 0); if (listen_socket == -1){ if (master->on_error) { master->on_error(master, errno); } return false; } // TIME_WAIT - argh int on = 1; if (::setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1 ){ //return false; } sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = inet_addr(master->bind_address.c_str()); sa.sin_port = htons(master->bind_port); if (::bind(listen_socket, (sockaddr*)&sa, sizeof(sa)) < 0) { if (master->on_error) { master->on_error(master, errno); } ::close(listen_socket); listen_socket = -1; return false; } return true; } void ActiveStop() { if(listen_socket != -1){ ::close(listen_socket); listen_socket = -1; } if (worker_thread) { shutdown = true; worker_thread->join(); delete worker_thread; worker_thread = NULL; } } void WorkerThreadProc() { if(!ActiveStart()) return; struct sockaddr_in destAddr; socklen_t addrLen = sizeof(sockaddr_in); char recv_buf[1024 * 64]; int buflen = sizeof(recv_buf); while (!shutdown) { int ret = ::recvfrom(listen_socket, recv_buf, buflen, 0, (struct sockaddr*)&destAddr, (socklen_t*)&addrLen); if(ret > 0){ master->OnSocketRead(recv_buf, ret); } } } }; UdpListener::UdpListener() { impl_ = new a8::UdpListenerImpl(); impl_->master = this; } UdpListener::~UdpListener() { delete impl_; impl_ = nullptr; } void UdpListener::OnSocketRead(char*, unsigned int) { } void UdpListener::Open() { if (!IsActive()) { impl_->SetActive(true); } } void UdpListener::Close() { if (IsActive()) { impl_->SetActive(false); } } bool UdpListener::IsActive() { return impl_->IsActive(); } }