001package jmri.jmrit.z21server; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006import java.net.DatagramPacket; 007import java.net.DatagramSocket; 008import java.net.InetAddress; 009import java.net.SocketException; 010import java.util.Arrays; 011 012public class MainServer implements Runnable { 013 014 private final static Logger log = LoggerFactory.getLogger(MainServer.class); 015 private final static int port = 21105; 016 DatagramSocket mySS; 017 @Override 018 public void run() { 019 try { 020 mySS = new DatagramSocket(port); 021 022 byte[] buf = new byte[256]; 023 DatagramPacket packet = new DatagramPacket(buf, buf.length); 024 025 log.info("Created socket, listening for connections"); 026 027 while (true) { 028 029 if (Thread.interrupted()) break; 030 boolean bReceivedData; 031 032 mySS.setSoTimeout(500); 033 try { 034 mySS.receive(packet); 035 bReceivedData = true; 036 } catch (Exception e) { 037 bReceivedData = false; 038 } 039 040 if (!bReceivedData) continue; 041 042 InetAddress clientAddress = packet.getAddress(); 043 044 ClientManager.getInstance().heartbeat(clientAddress); 045 046 byte[] rawData = packet.getData(); 047 int dataLenght = rawData[0]; 048 byte[] actualData = Arrays.copyOf(rawData, dataLenght); 049 String ident = "[" + clientAddress + "] "; 050 log.debug("{}: raw frame {} ", ident, bytesToHex(actualData)); 051 052 if (actualData.length < 3) { 053 log.debug("error, frame : {}", bytesToHex(actualData)); 054 } 055 056 byte[] response = null; 057 058 switch (actualData[2]) { 059 case 0x50: 060 byte[] maskArray = Arrays.copyOfRange(actualData, HEADER_SIZE, dataLenght); 061 int mask = fromByteArrayLittleEndian(maskArray); 062 log.debug("{} Broadcast request with mask : {}", ident, Integer.toBinaryString(mask)); 063 break; 064 case 0x30: 065 log.debug("{} Disconnect frame", ident); 066 break; 067 case 0x40: 068 byte[] payloadData = Arrays.copyOfRange(actualData, HEADER_SIZE, dataLenght); 069 response = Service40.handleService(payloadData, clientAddress); 070 break; 071 072 default: 073 log.debug("{} Service not yet implemented : 0x{}", ident, Integer.toHexString(actualData[2])); 074 } 075 076 if (response != null) { 077 DatagramPacket responsePacket = new DatagramPacket(response, response.length, clientAddress, port); 078 try { 079 mySS.send(responsePacket); 080 } catch (Exception e) { 081 log.debug("Unable to send packet to client {}", clientAddress.toString()); 082 } 083 } 084 085 ClientManager.getInstance().handleExpiredClients(); 086 } 087 088 log.info("Z21 App Server shut down."); 089 090 } catch (SocketException e) { 091 log.info("Z21 App Server encountered an error, exiting.", e); 092 } 093 094 if (mySS != null) { 095 mySS.close(); 096 } 097 098 } 099 100 private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 101 102 private static String bytesToHex(byte[] bytes) { 103 char[] hexChars = new char[bytes.length * 2]; 104 for (int j = 0; j < bytes.length; j++) { 105 int v = bytes[j] & 0xFF; 106 hexChars[j * 2] = HEX_ARRAY[v >>> 4]; 107 hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 108 } 109 return new String(hexChars); 110 } 111 112 113 private static int fromByteArrayLittleEndian(byte[] bytes) { 114 return ((bytes[2] & 0xFF) << 24) | 115 ((bytes[3] & 0xFF) << 16) | 116 ((bytes[0] & 0xFF) << 8) | 117 ((bytes[1] & 0xFF) << 0); 118 } 119 120 private static final short HEADER_SIZE = 4; 121 122 123}