175 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef HV_UDP_SERVER_HPP_
 | 
						|
#define HV_UDP_SERVER_HPP_
 | 
						|
 | 
						|
#include "hsocket.h"
 | 
						|
 | 
						|
#include "EventLoopThreadPool.h"
 | 
						|
#include "Channel.h"
 | 
						|
 | 
						|
namespace hv {
 | 
						|
 | 
						|
template<class TSocketChannel = SocketChannel>
 | 
						|
class UdpServerEventLoopTmpl {
 | 
						|
public:
 | 
						|
    typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
 | 
						|
 | 
						|
    UdpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
 | 
						|
        loop_ = loop ? loop : std::make_shared<EventLoop>();
 | 
						|
        port = 0;
 | 
						|
#if WITH_KCP
 | 
						|
        kcp_setting = NULL;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    virtual ~UdpServerEventLoopTmpl() {
 | 
						|
#if WITH_KCP
 | 
						|
        HV_FREE(kcp_setting);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    const EventLoopPtr& loop() {
 | 
						|
        return loop_;
 | 
						|
    }
 | 
						|
 | 
						|
    //@retval >=0 bindfd, <0 error
 | 
						|
    int createsocket(int port, const char* host = "0.0.0.0") {
 | 
						|
        hio_t* io = hloop_create_udp_server(loop_->loop(), host, port);
 | 
						|
        if (io == NULL) return -1;
 | 
						|
        this->host = host;
 | 
						|
        this->port = port;
 | 
						|
        channel = std::make_shared<TSocketChannel>(io);
 | 
						|
        return channel->fd();
 | 
						|
    }
 | 
						|
    // closesocket thread-safe
 | 
						|
    void closesocket() {
 | 
						|
        if (channel) {
 | 
						|
            channel->close(true);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    int startRecv() {
 | 
						|
        if (channel == NULL || channel->isClosed()) {
 | 
						|
            int bindfd = createsocket(port, host.c_str());
 | 
						|
            if (bindfd < 0) {
 | 
						|
                hloge("createsocket %s:%d return %d!\n", host.c_str(), port, bindfd);
 | 
						|
                return bindfd;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (channel == NULL || channel->isClosed()) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        channel->onread = [this](Buffer* buf) {
 | 
						|
            if (onMessage) {
 | 
						|
                onMessage(channel, buf);
 | 
						|
            }
 | 
						|
        };
 | 
						|
        channel->onwrite = [this](Buffer* buf) {
 | 
						|
            if (onWriteComplete) {
 | 
						|
                onWriteComplete(channel, buf);
 | 
						|
            }
 | 
						|
        };
 | 
						|
#if WITH_KCP
 | 
						|
        if (kcp_setting) {
 | 
						|
            hio_set_kcp(channel->io(), kcp_setting);
 | 
						|
        }
 | 
						|
#endif
 | 
						|
        return channel->startRead();
 | 
						|
    }
 | 
						|
 | 
						|
    int stopRecv() {
 | 
						|
        if (channel == NULL) return -1;
 | 
						|
        return channel->stopRead();
 | 
						|
    }
 | 
						|
 | 
						|
    // start thread-safe
 | 
						|
    void start() {
 | 
						|
        loop_->runInLoop(std::bind(&UdpServerEventLoopTmpl::startRecv, this));
 | 
						|
    }
 | 
						|
 | 
						|
    // sendto thread-safe
 | 
						|
    int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
 | 
						|
        if (channel == NULL) return -1;
 | 
						|
        std::lock_guard<std::mutex> locker(sendto_mutex);
 | 
						|
        if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
 | 
						|
        return channel->write(data, size);
 | 
						|
    }
 | 
						|
    int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
 | 
						|
        return sendto(buf->data(), buf->size(), peeraddr);
 | 
						|
    }
 | 
						|
    int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
 | 
						|
        return sendto(str.data(), str.size(), peeraddr);
 | 
						|
    }
 | 
						|
 | 
						|
#if WITH_KCP
 | 
						|
    void setKcp(kcp_setting_t* setting) {
 | 
						|
        if (setting == NULL) {
 | 
						|
            HV_FREE(kcp_setting);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (kcp_setting == NULL) {
 | 
						|
            HV_ALLOC_SIZEOF(kcp_setting);
 | 
						|
        }
 | 
						|
        *kcp_setting = *setting;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
public:
 | 
						|
    std::string             host;
 | 
						|
    int                     port;
 | 
						|
    TSocketChannelPtr       channel;
 | 
						|
#if WITH_KCP
 | 
						|
    kcp_setting_t*          kcp_setting;
 | 
						|
#endif
 | 
						|
    // Callback
 | 
						|
    std::function<void(const TSocketChannelPtr&, Buffer*)>  onMessage;
 | 
						|
    // NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
 | 
						|
    std::function<void(const TSocketChannelPtr&, Buffer*)>  onWriteComplete;
 | 
						|
 | 
						|
private:
 | 
						|
    std::mutex              sendto_mutex;
 | 
						|
    EventLoopPtr            loop_;
 | 
						|
};
 | 
						|
 | 
						|
template<class TSocketChannel = SocketChannel>
 | 
						|
class UdpServerTmpl : private EventLoopThread, public UdpServerEventLoopTmpl<TSocketChannel> {
 | 
						|
public:
 | 
						|
    UdpServerTmpl(EventLoopPtr loop = NULL)
 | 
						|
        : EventLoopThread(loop)
 | 
						|
        , UdpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
 | 
						|
        , is_loop_owner(loop == NULL)
 | 
						|
    {}
 | 
						|
    virtual ~UdpServerTmpl() {
 | 
						|
        stop(true);
 | 
						|
    }
 | 
						|
 | 
						|
    const EventLoopPtr& loop() {
 | 
						|
        return EventLoopThread::loop();
 | 
						|
    }
 | 
						|
 | 
						|
    // start thread-safe
 | 
						|
    void start(bool wait_threads_started = true) {
 | 
						|
        if (isRunning()) {
 | 
						|
            UdpServerEventLoopTmpl<TSocketChannel>::start();
 | 
						|
        } else {
 | 
						|
            EventLoopThread::start(wait_threads_started, std::bind(&UdpServerTmpl::startRecv, this));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // stop thread-safe
 | 
						|
    void stop(bool wait_threads_stopped = true) {
 | 
						|
        UdpServerEventLoopTmpl<TSocketChannel>::closesocket();
 | 
						|
        if (is_loop_owner) {
 | 
						|
            EventLoopThread::stop(wait_threads_stopped);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    bool is_loop_owner;
 | 
						|
};
 | 
						|
 | 
						|
typedef UdpServerTmpl<SocketChannel> UdpServer;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
#endif // HV_UDP_SERVER_HPP_
 |