/*
* (C) Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* ohun@live.cn (夜色)
*/
package com.mpush.netty.udp;
import com.mpush.api.service.BaseService;
import com.mpush.api.service.Listener;
import com.mpush.api.service.Server;
import com.mpush.api.service.ServiceException;
import com.mpush.tools.thread.ThreadNames;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.netty.channel.socket.InternetProtocolFamily.IPv4;
/**
* Created by ohun on 16/10/20.
*
* @author ohun@live.cn (夜色)
*/
public abstract class NettyUDPConnector extends BaseService implements Server {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
protected final int port;
private EventLoopGroup eventLoopGroup;
public NettyUDPConnector(int port) {
this.port = port;
}
@Override
protected void doStart(Listener listener) throws Throwable {
createNioServer(listener);
}
@Override
protected void doStop(Listener listener) throws Throwable {
logger.info("try shutdown {}...", this.getClass().getSimpleName());
if (eventLoopGroup != null) eventLoopGroup.shutdownGracefully().syncUninterruptibly();
logger.info("{} shutdown success.", this.getClass().getSimpleName());
listener.onSuccess(port);
}
private void createServer(Listener listener, EventLoopGroup eventLoopGroup, ChannelFactory<? extends DatagramChannel> channelFactory) {
this.eventLoopGroup = eventLoopGroup;
try {
Bootstrap b = new Bootstrap();
b.group(eventLoopGroup)//默认是根据机器情况创建Channel,如果机器支持ipv6,则无法使用ipv4的地址加入组播
.channelFactory(channelFactory)
.option(ChannelOption.SO_BROADCAST, true)
.handler(getChannelHandler());
initOptions(b);
//直接绑定端口,不要指定host,不然收不到组播消息
b.bind(port).addListener(future -> {
if (future.isSuccess()) {
logger.info("udp server start success on:{}", port);
if (listener != null) listener.onSuccess(port);
} else {
logger.error("udp server start failure on:{}", port, future.cause());
if (listener != null) listener.onFailure(future.cause());
}
});
} catch (Exception e) {
logger.error("udp server start exception", e);
if (listener != null) listener.onFailure(e);
throw new ServiceException("udp server start exception, port=" + port, e);
}
}
private void createNioServer(Listener listener) {
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(
1, new DefaultThreadFactory(ThreadNames.T_GATEWAY_WORKER)
);
eventLoopGroup.setIoRatio(100);
createServer(listener, eventLoopGroup, () -> new NioDatagramChannel(IPv4));//默认是根据机器情况创建Channel,如果机器支持ipv6,则无法使用ipv4的地址加入组播
}
@SuppressWarnings("unused")
private void createEpollServer(Listener listener) {
EpollEventLoopGroup eventLoopGroup = new EpollEventLoopGroup(
1, new DefaultThreadFactory(ThreadNames.T_GATEWAY_WORKER)
);
eventLoopGroup.setIoRatio(100);
createServer(listener, eventLoopGroup, EpollDatagramChannel::new);
}
protected void initOptions(Bootstrap b) {
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b.option(ChannelOption.SO_REUSEADDR, true);
}
public abstract ChannelHandler getChannelHandler();
}