All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openmuc.jdlms.transportlayer.client.UdpLayer Maven / Gradle / Ivy

/**
 * Copyright 2012-18 Fraunhofer ISE
 *
 * This file is part of jDLMS.
 * For more information visit http://www.openmuc.org
 *
 * jDLMS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jDLMS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jDLMS.  If not, see .
 */
package org.openmuc.jdlms.transportlayer.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import org.openmuc.jdlms.settings.client.TcpSettings;

/**
 * The transport layer used to communicate via the User Datagram Protocol (UDP).
 */
public class UdpLayer implements TransportLayer {

    public static final int MAX_UDP_PAYLOAD_SIZE = 65507;

    private final TcpSettings settings;
    private DatagramSocket socket;
    private boolean closed;

    private IStream inputStream;
    private OStream outputStream;

    public UdpLayer(TcpSettings settings) {
        this.settings = settings;
        this.closed = true;
    }

    @Override
    public void open() throws IOException {
        this.socket = new DatagramSocket();
        this.closed = false;
        this.inputStream = new IStream();
        this.outputStream = new OStream();
    }

    @Override
    public void setTimeout(int timeout) throws IOException {
        this.socket.setSoTimeout(timeout);
    }

    @Override
    public DataInputStream getInputStream() throws IOException {
        return new DataInputStream(inputStream);
    }

    @Override
    public DataOutputStream getOutpuStream() throws IOException {
        return new DataOutputStream(outputStream);
    }

    @Override
    public void close() throws IOException {
        try {
            this.socket.close();

            this.inputStream.closeStream();
            this.outputStream.closeStream();
        } finally {
            this.closed = true;
        }
    }

    @Override
    public boolean isClosed() {
        return this.closed;
    }

    private class OStream extends OutputStream {

        private final ByteArrayOutputStream os;

        public OStream() {
            this.os = new ByteArrayOutputStream(MAX_UDP_PAYLOAD_SIZE);
        }

        public void closeStream() throws IOException {
            this.os.close();
        }

        @Override
        public void close() throws IOException {
            UdpLayer.this.close();
        }

        @Override
        public void write(int b) throws IOException {
            this.os.write(b);

            if (this.os.size() == MAX_UDP_PAYLOAD_SIZE) {
                flush();
            }
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            int remaining = len;
            while (true) {
                int newLen = Math.min(len, MAX_UDP_PAYLOAD_SIZE - this.os.size());

                if (newLen != 0) {
                    this.os.write(b, off + (len - remaining), newLen);
                    remaining -= newLen;
                }

                if (remaining == 0) {
                    break;
                }

                flush();
            }

        }

        @Override
        public void write(byte[] b) throws IOException {
            write(b, 0, b.length);
        }

        @Override
        public synchronized void flush() throws IOException {
            if (this.os.size() == 0) {
                return;
            }

            byte[] data = this.os.toByteArray();
            this.os.reset();

            DatagramPacket packet = new DatagramPacket(data, 0, data.length, settings.inetAddress(), settings.port());
            socket.send(packet);
        }

    }

    private class IStream extends InputStream {

        private InputStream is;
        private final Object lock;

        public IStream() {
            this.is = new ByteArrayInputStream(new byte[0]);
            this.lock = new Object();
        }

        public void closeStream() throws IOException {
            this.is.close();
        }

        @Override
        public void close() throws IOException {
            UdpLayer.this.close();
        }

        @Override
        public int read() throws IOException {
            readIfEmpty();
            synchronized (lock) {
                return is.read();
            }

        }

        private void readIfEmpty() throws IOException {
            synchronized (lock) {
                if (is.available() == 0) {
                    readNextPacket();
                }
            }
        }

        @Override
        public int available() throws IOException {
            readIfEmpty();

            return is.available();
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            try {
                readIfEmpty();
            } catch (IOException e) {
                return 0;
            }

            int remaining = len;
            while (true) {
                synchronized (socket) {
                    remaining -= is.read(b, len - remaining, Math.min(is.available(), remaining));
                }

                if (remaining == 0) {
                    return len;
                }

                try {
                    readIfEmpty();
                } catch (IOException e) {
                    return len - remaining;
                }
            }
        }

        @Override
        public int read(byte[] b) throws IOException {
            return read(b, 0, b.length);
        }

        private synchronized void readNextPacket() throws IOException {

            byte[] buf = new byte[MAX_UDP_PAYLOAD_SIZE];
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            socket.receive(packet);

            InetAddress address = packet.getAddress();
            int port = packet.getPort();
            if (!address.equals(settings.inetAddress()) || port != settings.port()) {
                // TODO check this again

                readNextPacket();
                return;
            }

            int len = packet.getLength();
            byte[] data = packet.getData();

            synchronized (lock) {
                this.is = new ByteArrayInputStream(data, 0, len);
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy