org.bidib.jbidibc.net.serialovertcp.NetBidibServerPlainTcpPort Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbidibc-net-serial-over-tcp Show documentation
Show all versions of jbidibc-net-serial-over-tcp Show documentation
jBiDiB jbidibc Net Serial over TCP POM
The newest version!
package org.bidib.jbidibc.net.serialovertcp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.HashedMap;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetBidibServerPlainTcpPort implements NetBidibPort {
private static final Logger LOGGER = LoggerFactory.getLogger(NetBidibServerPlainTcpPort.class);
private final NetMessageHandler messageReceiver;
private AtomicBoolean runEnabled = new AtomicBoolean();
private ServerSocket serverSocket;
private final ScheduledExecutorService acceptWorker = Executors.newScheduledThreadPool(1);
private Map clientMap = MapUtils.synchronizedMap(new HashedMap<>());
/**
* Creates a new instance of {@code NetBidibServerTcpPort}.
*
* @param portNumber
* the port number
* @param bindAddress
* the (optional) bind address
* @param messageReceiver
* the message receiver
* @throws IOException
*/
public NetBidibServerPlainTcpPort(int portNumber, InetAddress bindAddress, NetMessageHandler messageReceiver)
throws IOException {
this.messageReceiver = messageReceiver;
if (bindAddress != null) {
this.serverSocket = new ServerSocket(portNumber, 50, bindAddress);
LOGGER.info("Created TCP server socket for bind address: {}, port number: {}", bindAddress, portNumber);
}
else {
this.serverSocket = new ServerSocket(portNumber);
LOGGER.info("Created TCP server socket on port number: {}", portNumber);
}
}
@Override
public void run() {
LOGGER.info("Start the TCP socket.");
runEnabled.set(true);
// add a task to the worker to let the node process the send queue
acceptWorker.submit(new Runnable() {
@Override
public void run() {
// we must receive from multiple clients
while (runEnabled.get()) {
try {
LOGGER.info("Wait for client to connect.");
Socket socket = serverSocket.accept();
LOGGER.info("The client connection was accepted byte the server socket, socket: {}", socket);
InetAddress remoteAddress = socket.getInetAddress();
if (remoteAddress != null) {
String remoteIpAddress = remoteAddress.getHostAddress();
try {
messageReceiver.acceptClient(remoteIpAddress);
registerClient(socket);
NetBidibPlainTcpServerSocketHandler handler =
new NetBidibPlainTcpServerSocketHandler(socket, messageReceiver,
clientSocket -> unregisterClient(clientSocket));
handler.start();
}
catch (Exception e) {
LOGGER.warn("Client was not accepted.", e);
unregisterClient(socket);
try {
socket.close();
}
catch (Exception e1) {
LOGGER.warn("Close socket of unaccepted client.", e1);
}
}
}
else {
LOGGER.warn("No remote address available for socket.");
try {
socket.close();
}
catch (Exception e) {
LOGGER.warn("Close socket without remote address available failed.", e);
}
}
}
catch (IOException ex) {
if (runEnabled.get()) {
LOGGER.error("Start listen on server socket failed.", ex);
runEnabled.set(false);
}
else {
LOGGER.info("The server socket listener is stopped.");
}
return;
}
}
LOGGER.info("The server socket acceptor has finished.");
}
});
LOGGER.info("Start server port has passed.");
}
@Override
public void stop() {
LOGGER.info("Stop the TCP packet receiver, serverSocket: {}", serverSocket);
runEnabled.set(false);
if (serverSocket != null) {
LOGGER.info("Close the server socket.");
try {
serverSocket.close();
}
catch (IOException ex) {
LOGGER.warn("Close serverSocket failed.", ex);
}
try {
LOGGER.info("Shutdown acceptWorker.");
acceptWorker.shutdown();
acceptWorker.awaitTermination(2000, TimeUnit.MILLISECONDS);
}
catch (Exception ex) {
LOGGER.warn("Wait for shutdown of acceptWorker failed.", ex);
}
serverSocket = null;
}
// cleanup the clientmap
synchronized (clientMap) {
clientMap.clear();
}
}
private void registerClient(final Socket socket) {
LOGGER.info("Register client: {}", socket);
if (socket == null) {
throw new IllegalArgumentException("Mandatory socket is not available.");
}
synchronized (clientMap) {
clientMap.put(socket.getPort(), socket);
}
}
private void unregisterClient(final Socket socket) {
LOGGER.info("Unregister client: {}", socket);
if (socket == null) {
throw new IllegalArgumentException("Mandatory socket is not available.");
}
synchronized (clientMap) {
clientMap.remove(socket.getPort(), socket);
}
}
@Override
public void send(byte[] sendData, InetAddress address, int portNumber) throws IOException {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Send data, port: {}, bytes: {}", portNumber, ByteUtils.bytesToHex(sendData));
}
final Collection connectedClients;
synchronized (clientMap) {
connectedClients = Collections.unmodifiableCollection(clientMap.values());
}
for (Socket socket : connectedClients) {
if (socket != null) {
LOGGER.info("Send data to socket: {}", socket, ByteUtils.bytesToHex(sendData));
socket.getOutputStream().write(sendData);
socket.getOutputStream().flush();
}
else {
LOGGER
.warn("Send data is discarded because no socket registered for port: {}, address: {}", portNumber,
address);
}
}
}
}