You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
9.5 KiB
295 lines
9.5 KiB
7 years ago
|
/**
|
||
6 years ago
|
* Copyright (c) 2012-2019 Nikita Koksharov
|
||
|
*
|
||
7 years ago
|
* 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
|
||
6 years ago
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
7 years ago
|
* 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.
|
||
|
*/
|
||
|
package com.fr.third.socketio;
|
||
|
|
||
|
import com.fr.third.socketio.listener.ClientListeners;
|
||
|
import com.fr.third.socketio.listener.ConnectListener;
|
||
|
import com.fr.third.socketio.listener.DataListener;
|
||
|
import com.fr.third.socketio.listener.DisconnectListener;
|
||
|
import com.fr.third.socketio.listener.MultiTypeEventListener;
|
||
|
import com.fr.third.socketio.listener.PingListener;
|
||
|
import io.netty.bootstrap.ServerBootstrap;
|
||
|
import io.netty.channel.ChannelOption;
|
||
|
import io.netty.channel.EventLoopGroup;
|
||
|
import io.netty.channel.FixedRecvByteBufAllocator;
|
||
|
import io.netty.channel.ServerChannel;
|
||
|
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||
|
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||
|
import io.netty.util.concurrent.Future;
|
||
|
import io.netty.util.concurrent.FutureListener;
|
||
|
|
||
|
import java.net.InetSocketAddress;
|
||
|
import java.util.Collection;
|
||
|
import java.util.UUID;
|
||
7 years ago
|
import java.util.concurrent.TimeUnit;
|
||
7 years ago
|
|
||
6 years ago
|
import io.netty.util.concurrent.GenericFutureListener;
|
||
7 years ago
|
import org.slf4j.Logger;
|
||
|
import org.slf4j.LoggerFactory;
|
||
|
|
||
|
import com.fr.third.socketio.namespace.Namespace;
|
||
|
import com.fr.third.socketio.namespace.NamespacesHub;
|
||
|
|
||
|
/**
|
||
|
* Fully thread-safe.
|
||
6 years ago
|
*
|
||
7 years ago
|
*/
|
||
|
public class SocketIOServer implements ClientListeners {
|
||
6 years ago
|
|
||
7 years ago
|
private static final Logger log = LoggerFactory.getLogger(SocketIOServer.class);
|
||
6 years ago
|
|
||
7 years ago
|
private final Configuration configCopy;
|
||
|
private final Configuration configuration;
|
||
6 years ago
|
|
||
7 years ago
|
private final NamespacesHub namespacesHub;
|
||
|
private final SocketIONamespace mainNamespace;
|
||
6 years ago
|
|
||
7 years ago
|
private SocketIOChannelInitializer pipelineFactory = new SocketIOChannelInitializer();
|
||
6 years ago
|
|
||
7 years ago
|
private EventLoopGroup bossGroup;
|
||
|
private EventLoopGroup workerGroup;
|
||
6 years ago
|
|
||
7 years ago
|
public SocketIOServer(Configuration configuration) {
|
||
|
this.configuration = configuration;
|
||
|
this.configCopy = new Configuration(configuration);
|
||
|
namespacesHub = new NamespacesHub(configCopy);
|
||
|
mainNamespace = addNamespace(Namespace.DEFAULT_NAME);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
public void setPipelineFactory(SocketIOChannelInitializer pipelineFactory) {
|
||
|
this.pipelineFactory = pipelineFactory;
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Get all clients connected to default namespace
|
||
|
*
|
||
|
* @return clients collection
|
||
|
*/
|
||
|
public Collection<SocketIOClient> getAllClients() {
|
||
|
return namespacesHub.get(Namespace.DEFAULT_NAME).getAllClients();
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Get client by uuid from default namespace
|
||
|
*
|
||
|
* @param uuid - id of client
|
||
|
* @return client
|
||
|
*/
|
||
|
public SocketIOClient getClient(UUID uuid) {
|
||
|
return namespacesHub.get(Namespace.DEFAULT_NAME).getClient(uuid);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Get all namespaces
|
||
|
*
|
||
|
* @return namespaces collection
|
||
|
*/
|
||
|
public Collection<SocketIONamespace> getAllNamespaces() {
|
||
|
return namespacesHub.getAllNamespaces();
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
public BroadcastOperations getBroadcastOperations() {
|
||
|
return new BroadcastOperations(getAllClients(), configCopy.getStoreFactory());
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Get broadcast operations for clients within
|
||
|
* room by <code>room</code> name
|
||
|
*
|
||
|
* @param room - name of room
|
||
|
* @return broadcast operations
|
||
|
*/
|
||
|
public BroadcastOperations getRoomOperations(String room) {
|
||
|
Iterable<SocketIOClient> clients = namespacesHub.getRoomClients(room);
|
||
|
return new BroadcastOperations(clients, configCopy.getStoreFactory());
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Start server
|
||
|
*/
|
||
|
public void start() {
|
||
|
startAsync().syncUninterruptibly();
|
||
|
}
|
||
6 years ago
|
|
||
6 years ago
|
public void start(GenericFutureListener<? extends Future<? super Void>> listener) {
|
||
|
startAsync(listener).syncUninterruptibly();
|
||
|
}
|
||
|
|
||
7 years ago
|
/**
|
||
|
* Start server asynchronously
|
||
6 years ago
|
*
|
||
7 years ago
|
* @return void
|
||
|
*/
|
||
|
public Future<Void> startAsync() {
|
||
6 years ago
|
return startAsync(new FutureListener<Void>() {
|
||
|
|
||
|
@Override
|
||
|
public void operationComplete(Future<Void> future) throws Exception {
|
||
|
|
||
|
if (future.isSuccess()) {
|
||
|
log.info("SocketIO server started at port: {}", configCopy.getPort());
|
||
|
} else {
|
||
|
log.error("SocketIO server start failed at port: {}!", configCopy.getPort());
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start server asynchronously
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public Future<Void> startAsync(GenericFutureListener<? extends Future<? super Void>> listener) {
|
||
|
|
||
7 years ago
|
log.info("Session store / pubsub factory used: {}", configCopy.getStoreFactory());
|
||
|
initGroups();
|
||
6 years ago
|
|
||
7 years ago
|
pipelineFactory.start(configCopy, namespacesHub);
|
||
6 years ago
|
|
||
7 years ago
|
Class<? extends ServerChannel> channelClass = NioServerSocketChannel.class;
|
||
|
if (configCopy.isUseLinuxNativeEpoll()) {
|
||
|
channelClass = EpollServerSocketChannel.class;
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
ServerBootstrap b = new ServerBootstrap();
|
||
|
b.group(bossGroup, workerGroup)
|
||
6 years ago
|
.channel(channelClass)
|
||
|
.childHandler(pipelineFactory);
|
||
7 years ago
|
applyConnectionOptions(b);
|
||
6 years ago
|
|
||
7 years ago
|
InetSocketAddress addr = new InetSocketAddress(configCopy.getPort());
|
||
|
if (configCopy.getHostname() != null) {
|
||
|
addr = new InetSocketAddress(configCopy.getHostname(), configCopy.getPort());
|
||
|
}
|
||
6 years ago
|
|
||
6 years ago
|
return b.bind(addr).addListener(listener);
|
||
7 years ago
|
}
|
||
6 years ago
|
|
||
7 years ago
|
protected void applyConnectionOptions(ServerBootstrap bootstrap) {
|
||
|
SocketConfig config = configCopy.getSocketConfig();
|
||
|
bootstrap.childOption(ChannelOption.TCP_NODELAY, config.isTcpNoDelay());
|
||
|
if (config.getTcpSendBufferSize() != -1) {
|
||
|
bootstrap.childOption(ChannelOption.SO_SNDBUF, config.getTcpSendBufferSize());
|
||
|
}
|
||
|
if (config.getTcpReceiveBufferSize() != -1) {
|
||
|
bootstrap.childOption(ChannelOption.SO_RCVBUF, config.getTcpReceiveBufferSize());
|
||
|
bootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(config.getTcpReceiveBufferSize()));
|
||
|
}
|
||
|
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, config.isTcpKeepAlive());
|
||
|
bootstrap.childOption(ChannelOption.SO_LINGER, config.getSoLinger());
|
||
6 years ago
|
|
||
7 years ago
|
bootstrap.option(ChannelOption.SO_REUSEADDR, config.isReuseAddress());
|
||
|
bootstrap.option(ChannelOption.SO_BACKLOG, config.getAcceptBackLog());
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
protected void initGroups() {
|
||
|
if (configCopy.isUseLinuxNativeEpoll()) {
|
||
|
bossGroup = new EpollEventLoopGroup(configCopy.getBossThreads());
|
||
|
workerGroup = new EpollEventLoopGroup(configCopy.getWorkerThreads());
|
||
|
} else {
|
||
|
bossGroup = new NioEventLoopGroup(configCopy.getBossThreads());
|
||
|
workerGroup = new NioEventLoopGroup(configCopy.getWorkerThreads());
|
||
|
}
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Stop server
|
||
|
*/
|
||
|
public void stop() {
|
||
|
bossGroup.shutdownGracefully().syncUninterruptibly();
|
||
|
workerGroup.shutdownGracefully().syncUninterruptibly();
|
||
6 years ago
|
|
||
7 years ago
|
pipelineFactory.stop();
|
||
|
log.info("SocketIO server stopped");
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
public void stopImmediately() {
|
||
6 years ago
|
this.bossGroup.shutdownGracefully(0L, 1L, TimeUnit.SECONDS).syncUninterruptibly();
|
||
|
this.workerGroup.shutdownGracefully(0L, 1L, TimeUnit.SECONDS).syncUninterruptibly();
|
||
|
this.pipelineFactory.stop();
|
||
7 years ago
|
log.info("SocketIO server stopped");
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
public SocketIONamespace addNamespace(String name) {
|
||
|
return namespacesHub.create(name);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
public SocketIONamespace getNamespace(String name) {
|
||
|
return namespacesHub.get(name);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
public void removeNamespace(String name) {
|
||
|
namespacesHub.remove(name);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
/**
|
||
|
* Allows to get configuration provided
|
||
|
* during server creation. Further changes on
|
||
|
* this object not affect server.
|
||
|
*
|
||
|
* @return Configuration object
|
||
|
*/
|
||
|
public Configuration getConfiguration() {
|
||
|
return configuration;
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public void addMultiTypeEventListener(String eventName, MultiTypeEventListener listener, Class<?>... eventClass) {
|
||
|
mainNamespace.addMultiTypeEventListener(eventName, listener, eventClass);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public <T> void addEventListener(String eventName, Class<T> eventClass, DataListener<T> listener) {
|
||
|
mainNamespace.addEventListener(eventName, eventClass, listener);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public void removeAllListeners(String eventName) {
|
||
|
mainNamespace.removeAllListeners(eventName);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public void addDisconnectListener(DisconnectListener listener) {
|
||
|
mainNamespace.addDisconnectListener(listener);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public void addConnectListener(ConnectListener listener) {
|
||
|
mainNamespace.addConnectListener(listener);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public void addPingListener(PingListener listener) {
|
||
|
mainNamespace.addPingListener(listener);
|
||
|
}
|
||
6 years ago
|
|
||
7 years ago
|
@Override
|
||
|
public void addListeners(Object listeners) {
|
||
|
mainNamespace.addListeners(listeners);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void addListeners(Object listeners, Class<?> listenersClass) {
|
||
|
mainNamespace.addListeners(listeners, listenersClass);
|
||
|
}
|
||
6 years ago
|
|
||
|
|
||
7 years ago
|
}
|
||
6 years ago
|
|