@ShortName("UDPSocket")
    @Permissions(values = {"android.permission.INTERNET"})
    @Events(values={"PacketArrived (Packet As UDPPacket)"})
    public static class UDPSocket {
        private UDPReader reader;
        private DatagramSocket ds;
        /**
         * Initializes the socket and starts listening for packets.
         *EventName - The name of the Sub that will handle the events.
         *Port - Local port to listen on. Passing 0 will cause the OS to choose an available port automatically.
         *ReceiveBufferSize - The size of the receiving packet. Packets larger than this value will be truncated.
         *Pass 0 if you do not want to receive any packets.
         */
        public void Initialize(BA ba, String EventName, int Port, int ReceiveBufferSize) throws SocketException {
            Close();
            if (Port == 0)
                ds = new DatagramSocket();
            else
                ds = new DatagramSocket(Port);
            if (ReceiveBufferSize > 0) {
                reader = new UDPReader();
                reader.working = true;
                reader.socket = ds;
                reader.receiveLength = ReceiveBufferSize;
                reader.ba = ba;
                reader.eventName = EventName.toLowerCase(BA.cul);
                Thread t = new Thread(reader);
                t.setDaemon(true);
                t.start();
            }
        }
        /**
         * Tests whether this object is initialized.
         */
        public boolean IsInitialized() {
            return ds != null && !ds.isClosed();
        }
        /**
         * Gets the local port that this socket listens to.
         */
        public int getPort() {
            return ds.getLocalPort();
        }
        /**
         * Sends a Packet.
         */
        public void Send(DatagramPacket Packet) throws IOException {
            ds.send(Packet);
        }
        
        /**
         * Closes the socket.
         */
        public void Close() {
            if (ds != null)
                ds.close();
            if (reader != null)
                reader.working = false;
            reader = null;
            ds = null;
        }
        @Override
        public String toString() {
            if (ds == null)
                return "Not initialized";
            return "Port=" + getPort();
        }
        private static class UDPReader implements Runnable {
            volatile boolean working;
            DatagramSocket socket;
            int receiveLength;
            BA ba;
            String eventName;
            @Override
            public void run() {
                while (working) {
                    try {
                        DatagramPacket p = new DatagramPacket(new byte[receiveLength], receiveLength);
                        socket.receive(p);
                        UDPPacket u = new UDPPacket();
                        u.setObject(p);
                        ba.raiseEventFromDifferentThread(null, null, 0, eventName + "_packetarrived", false, new Object[] {u});
                    } catch (IOException e) {
                        e.printStackTrace();
                        if (working) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e1) {
                            }
                        }
                    }
                }
            }
        }
        /**
         * A packet of data that is being sent or received.
         *To send a packet call one of the Initialize methods and then send the packet by passing it to UDPSocket.Send.
         *When a packet arrives you can get the data in the packet from the available properties.
         */
        @ShortName("UDPPacket")
        public static class UDPPacket extends AbsObjectWrapper<DatagramPacket> {
            /**
             * Initializes the packet and makes it ready for sending.
             *Data - The data that will be send.
             *Host - The target host name or IP address.
             *Port - The target port.
             */
            public void Initialize(byte[] Data, String Host, int Port) throws SocketException {
                Initialize2(Data, 0, Data.length, Host, Port);
            }
            /**
             * Similar to Initialize. The data sent is based on the Offset and Length values.
             */
            public void Initialize2(byte[] Data, int Offset, int Length, String Host, int Port) throws SocketException {
                DatagramPacket d = new DatagramPacket(Data, Offset, Length, new InetSocketAddress(Host, Port));
                setObject(d);
            }
            /**
             * Gets the length of available bytes in the data. This can be shorter than the array length.
             */
            public int getLength() {
                return getObject().getLength();
            }
            /**
             * Gets the data array received.
             */
            public byte[] getData() {
                return getObject().getData();
            }
            /**
             * Gets the offset in the data array where the available data starts.
             */
            public int getOffset() {
                return getObject().getOffset();
            }
            /**
             * Gets the port of the sending machine.
             */
            public int getPort() {
                return getObject().getPort();
            }
            /**
             * Gets the host name or IP address of the sending machine.
             */
            public String getHost() {
                return getObject().getAddress().getHostName();
            }
            @Override
            public String toString() {
                if (getObjectOrNull() == null)
                    return super.toString();
                return "Length=" + getLength() + ", Offset=" + getOffset() + ", Host=" + getHost() + ", Port=" + getPort();
            }
        }
    }