Robert Koifman
Published © Apache-2.0

Softnet Free: Empowering IoT with TCP/IP Connectivity

Focus on your application's logic, leave the networking issues to Softnet Free.

IntermediateFull instructions provided1 hour283
Softnet Free: Empowering IoT with TCP/IP Connectivity

Things used in this project

Software apps and online services

Software resources and tutorials on GitHub
Demo server with free registrations

Story

Read more

Code

Test service application

Java
The application has a dependency on two libraries: softnet.jar and asncodec.jar.
The library repos are published at:
https://github.com/Softnet-Free/softnet-java
https://github.com/Softnet-Free/asn1codec-java
package serviceTestApp;

import softnet.*;
import softnet.exceptions.*;
import softnet.service.*;
import softnet.asn.*;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.TimeZone;
import java.util.UUID;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

class MyTCPAcceptHandler implements TCPAcceptHandler {
    @Override
    public void accept(RequestContext context, SocketChannel socketChannel, ConnectionMode mode) {
        System.out.println(String.format("The TCP connection is established in '%s' mode", mode));
				
        try {
            int buffer_size = 100;
            ByteBuffer buffer = ByteBuffer.allocate(buffer_size);

            while(socketChannel.read(buffer) != -1) {
                if(buffer.position() == buffer_size)
                    break;
            }															
			
            SequenceDecoder asnInput = ASNDecoder.Sequence(buffer.array());									
            System.out.println(String.format("The client message: '%s'", asnInput.UTF8String()));
			
            ASNEncoder asnEncoder = new ASNEncoder();
            SequenceEncoder asnOutput = asnEncoder.Sequence();
            asnOutput.UTF8String("Hello! I'm a service.");
			
            buffer.clear();
            buffer.put(asnEncoder.getEncoding());
            buffer.flip();
			
            socketChannel.write(buffer);
            socketChannel.shutdownOutput();			
        }
        catch(IOException e) {
            System.out.println(String.format("TCP socket error: %s", e.getMessage()));
        }
        catch(AsnException e) {
            System.out.println(String.format("Input data format error: %s", e.getMessage()));
        }	
        finally {
            try {
                socketChannel.close();
            } catch (IOException e) {}
			
            context.serviceEndpoint.tcpAccept(5, new MyTCPAcceptHandler()); /* The tcpAccept method is called again right after closing the current connection */
        }
    }				
}

class MyUDPAcceptHandler implements UDPAcceptHandler {
    @Override
    public void accept(RequestContext context, DatagramSocket datagramSocket, InetSocketAddress remoteSocketAddress, ConnectionMode mode) {
        System.out.println(String.format("The UDP connection established in '%s' mode", mode));
				 
        try {			
            byte[] buffer = new byte[15];  
            DatagramPacket packet = new DatagramPacket(buffer, 15);  
		    
            datagramSocket.setSoTimeout(5000);
            int rawNumber = 0;
            System.out.println("Sequence numbers of received packets:");
            for(int i=0; i<100; i++) {
                packet.setLength(15);
                datagramSocket.receive(packet);  				
                SequenceDecoder asnInput = ASNDecoder.Sequence(packet.getData());
                int number = asnInput.Int32();
                if(number / 10 != rawNumber) {
                    rawNumber = number / 10;
                    System.out.println();
                }
                System.out.print(String.format("%d,\t", number));
            }
            System.out.println();
        }		
        catch(SocketTimeoutException e) {
            System.out.println();
        }
        catch(IOException e) {
            System.out.println(String.format("UDP socket error: %s", e.getMessage()));
        }
        catch(AsnException e) {
            System.out.println(String.format("Input data format error: %s", e.getMessage()));
        }
        finally {
            datagramSocket.close();
            context.serviceEndpoint.udpAccept(10, new MyUDPAcceptHandler());
        }
    }
}

class GetBookListByAuthor implements RPCRequestHandler {	
    private ArrayList<Book> bookList;
	
    public GetBookListByAuthor(ArrayList<Book> bookList) {
        this.bookList = bookList;
    }
	
    @Override
    public int execute(RequestContext context, SequenceDecoder parameters, SequenceEncoder result, SequenceEncoder error) {
        try {
            String author = parameters.UTF8String();
            for (Book book : bookList) {
                if(book.author.equalsIgnoreCase(author)) {
                    SequenceEncoder asnBook = result.Sequence();
                    asnBook.UTF8String(book.title);
                    asnBook.UTF8String(book.author);
                    asnBook.GndTime(book.pubDate);
                    asnBook.Int32(book.pages);
                    asnBook.Real32(book.price);
                }				
            }			
            return 0;
        }
        catch(AsnException e) {
            return -1;
        }
    }
}

class SetTemperature implements RPCRequestHandler {
    @Override
    public int execute(final RequestContext context, SequenceDecoder parameters, SequenceEncoder result, SequenceEncoder error) {
        final UUID taskID = UUID.randomUUID();
		
        try {
            System.out.println(String.format("The 'Set Temperature' task is created. Target temp: %s", parameters.Int32()));		
		
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);					
                        PrivateEvent notification = new PrivateEvent("TemperatureSet", context.clientId);
                        notification.arguments.OctetString(taskID);
                        context.serviceEndpoint.raiseEvent(notification);					
                    } 
                    catch (InterruptedException e) {}
            }});
            thread.start();
			
            result.OctetString(taskID);
            return 0;
        } 
        catch (AsnException e) {
            e.printStackTrace();
            return -1;
        }
    }
}

class Book {
    public String title;
    public String author;
    public Date pubDate;
    public int pages;
    public float price;
    public Book(String title, String author, Date pubDate, int pages, float price) {
        this.title = title;
        this.author = author;
        this.pubDate = pubDate;
        this.pages = pages;
        this.price = price;
    }
}

public class ServiceApp {	
    public static final String apiVersion = "1.0";	
    private static ServiceEndpoint serviceEndpoint = null;
    private static ArrayList<Book> bookList = new ArrayList<Book>();	
	
    private static void loadBookList() throws ParseException {		
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
        dateFormat.setTimeZone(utcTimeZone);		
        bookList.add(new Book("The Secrets of Time", "Jane Smith", dateFormat.parse("2018-05-25"), 320, 19.99f));
        bookList.add(new Book("Beyond the Stars", "Jane Smith", dateFormat.parse("2019-02-23"), 288, 15.99f));
        bookList.add(new Book("Echoes of the Past", "John Doe", dateFormat.parse("2021-03-10"), 246, 14.99f));
        bookList.add(new Book("The Mystery of the Hidden Treasure", "Sarah Johnson", dateFormat.parse("2020-11-05"), 368, 17.99f));
        bookList.add(new Book("Lost in the Wilderness", "David Wilson", dateFormat.parse("2019-06-20"), 412, 21.99f));
    }
	
    static void executeEventRaisingDemo() 
    {
        try {			
            ReplacingEvent replacingEvent = new ReplacingEvent("CurrentTemperature");
            replacingEvent.arguments.Int32(1);
            serviceEndpoint.raiseEvent(replacingEvent);
	
            QueueingEvent queueingEvent = new QueueingEvent("CriticalPressure");
            queueingEvent.arguments.Int32(1);
            serviceEndpoint.raiseEvent(queueingEvent);
				
            Thread.sleep(1000);
		
            replacingEvent = new ReplacingEvent("CurrentTemperature");
            replacingEvent.arguments.Int32(2);
            serviceEndpoint.raiseEvent(replacingEvent);
	
            queueingEvent = new QueueingEvent("CriticalPressure");
            queueingEvent.arguments.Int32(2);
            serviceEndpoint.raiseEvent(queueingEvent);
	
            Thread.sleep(1000);
			
            replacingEvent = new ReplacingEvent("CurrentTemperature");
            replacingEvent.arguments.Int32(3);
            serviceEndpoint.raiseEvent(replacingEvent);
			
            queueingEvent = new QueueingEvent("CriticalPressure");
            queueingEvent.arguments.Int32(3);
            serviceEndpoint.raiseEvent(queueingEvent);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
	
    public static void main(String[] args) {
        // Instead of this account details, specify the details of your service
        String service_uri = "softnet-srv://3cd69efa-70a5-4c4f-9dec-50c47afd5db0@ts.softnet-iot.org";
        String password = "5Ur3pLBXSw";
				
        try {							
            SiteStructure siteStructure = ServiceEndpoint.createStructure(					
                "Test Service",
                "John Doe");
			
            siteStructure.addReplacingEvent("CurrentTemperature");
            siteStructure.addQueueingEvent("CriticalPressure", 600, 3);
            siteStructure.addPrivateEvent("TemperatureSet", TimeSpan.fromMinutes(30));
			            
            ServiceURI serviceURI = new ServiceURI(service_uri);
            serviceEndpoint = ServiceEndpoint.create(siteStructure, apiVersion, serviceURI, password);
            serviceEndpoint.setPersistenceL1();
			
            serviceEndpoint.addEventListener(new ServiceEventAdapter()
            {
                @Override
                public void onConnectivityChanged(ServiceEndpointEvent e)
                {
                    ServiceEndpoint serviceEndpoint = e.getEndpoint();
                    EndpointConnectivity connectivity = serviceEndpoint.getConnectivity();
                    System.out.println(String.format("Endpoint connectivity status: %s, Error: %s, Message: %s", connectivity.status, connectivity.error, connectivity.message));
                }

                @Override
                public void onStatusChanged(ServiceEndpointEvent e)
                {
                    ServiceEndpoint serviceEndpoint = e.getEndpoint();
                    ServiceStatus serviceStatus = serviceEndpoint.getStatus();
                    System.out.println(String.format("Service status: %s", serviceStatus));
                    
                    if(serviceStatus == ServiceStatus.Online) {
                        executeEventRaisingDemo();
                    }
                }
            });
						
            serviceEndpoint.tcpListen(5, null, 2);
            serviceEndpoint.tcpAccept(5, new MyTCPAcceptHandler());			
			
            /*
            serviceEndpoint.udpListen(10, 2);			
            serviceEndpoint.udpAccept(10, new MyUDPAcceptHandler());
            */
            
            /*
            loadBookList();
            serviceEndpoint.registerProcedure("getBookListByAuthor", new GetBookListByAuthor(bookList), 2);
            */
            
            /*
            serviceEndpoint.registerProcedure("setTemperature", new SetTemperature(), 1);            
            */
            
            serviceEndpoint.connect();
            System.in.read(); /* waits until pressing the Enter key */
        }		
        catch(java.lang.Throwable e) {
            e.printStackTrace();
        }
        finally {
            if(serviceEndpoint != null)
                serviceEndpoint.close();
            System.out.println("The service app is closed!");
        }
    }
}

Test client application

Java
The application has a dependency on two libraries: softnet.jar and asncodec.jar.
The library repos are published at:
https://github.com/Softnet-Free/softnet-java
https://github.com/Softnet-Free/asn1codec-java
package clientTestApp;

import softnet.*;
import softnet.asn.*;
import softnet.exceptions.*;
import softnet.client.*;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.TimeZone;

public class ClientApp {
    static ClientSEndpoint clientSEndpoint = null;
	
    public static void executeTcpDemo() 
    {
        clientSEndpoint.tcpConnect(5, new TCPResponseHandler() {
            @Override
            public void onSuccess(ResponseContext context, SocketChannel socketChannel,	ConnectionMode mode) {
                System.out.println(String.format("The TCP connection is established in '%s' mode", mode));

                try {								
                    ASNEncoder asnEncoder = new ASNEncoder();
                    SequenceEncoder asnOutput = asnEncoder.Sequence();
                    asnOutput.UTF8String("Hello! I'm a client.");
					
                    int buffer_size = 100;
                    ByteBuffer buffer = ByteBuffer.allocate(buffer_size);
                    buffer.put(asnEncoder.getEncoding());
                    buffer.flip();
					
                    socketChannel.write(buffer);
                    socketChannel.shutdownOutput();
					
                    System.out.println("The hello message is sent to the service!");

                    buffer.clear();
                    while(socketChannel.read(buffer) != -1) {
                        if(buffer.position() == buffer_size)
                            break;
                    }							
					
                    buffer.flip();
                    SequenceDecoder asnInput = ASNDecoder.Sequence(buffer.array());									
                    System.out.println(String.format("The service's response: '%s'", asnInput.UTF8String()));
						
                    socketChannel.close();
                }
                catch(IOException e) {
                    System.out.println(String.format("TCP socket error: %s", e.getMessage()));
                }
                catch(AsnException e) {
                    System.out.println(String.format("Input data format error: %s", e.getMessage()));
                }	
                finally {
                    try {
                        socketChannel.close();
                    } catch (IOException e) {}
                }
            }

            @Override
            public void onError(ResponseContext context, SoftnetException exception) {
                System.out.println(String.format("The TCP connection request failed with an error: %s", exception.getMessage()));	
            }
        }, null);
    }
	
    public static void executeUdpDemo()
    {
        clientSEndpoint.udpConnect(10, new UDPResponseHandler() {
            @Override
            public void onSuccess(ResponseContext context, DatagramSocket datagramSocket, InetSocketAddress remoteSocketAddress, ConnectionMode mode) 
            {
                System.out.println(String.format("The UDP connection is established in '%s' mode", mode));
                try {			
                    int rawNumber = 0;
                    System.out.println("Sequence numbers of sent packets:");
                    for(int i=0; i<100; i++) {
                        ASNEncoder asnEncoder = new ASNEncoder();
                        asnEncoder.Sequence().Int32(i);
                        byte[] buffer = asnEncoder.getEncoding();
                        DatagramPacket packet = new DatagramPacket(buffer, buffer.length, remoteSocketAddress);
                        datagramSocket.send(packet);  				
                        if(i / 10 != rawNumber) {
                            rawNumber = i / 10;
                            System.out.println();
                        }
                        System.out.print(String.format("%d,\t", i));									
                        Thread.sleep(200);
                    }
                    System.out.println();
                }
                catch(IOException e) {
                    System.out.println(String.format("UDP socket error: %s", e.getMessage()));
                }
                catch(InterruptedException e) { }
                finally {
                    datagramSocket.close();
                }
            }

            @Override
            public void onError(ResponseContext context, SoftnetException exception) {
                System.out.println(String.format("The UDP connection request failed with an error: %s", exception.getMessage()));	
            }			
        });
    }
	
    public static void executeRpcDemo()
    {		
        final String author = "Jane Smith";
		
        RemoteProcedure remoteProcedure = new RemoteProcedure("getBookListByAuthor");		
        remoteProcedure.arguments.UTF8String(author);
		
        clientSEndpoint.call(remoteProcedure, new RPCResponseHandler() {
            @Override
            public void onSuccess(ResponseContext context, SequenceDecoder result)
            {
                System.out.println();
                try {
                    if(result.count() > 0) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
                        dateFormat.setTimeZone(utcTimeZone);
						
                        System.out.println(String.format("Books found by %s:", author));
                        System.out.println();
						
                        while(result.hasNext()) {
                            SequenceDecoder asnBook = result.Sequence();
                            String title = asnBook.UTF8String();
                            asnBook.skip();
                            java.util.Date pubDate = asnBook.GndTimeToDate();
                            int pages = asnBook.Int32();
                            float price = asnBook.Real32();
							
                            System.out.println(String.format("Title: %s", title));
                            System.out.println(String.format("Pub.Date: %s", dateFormat.format(pubDate)));
                            System.out.println(String.format("Pages: %d", pages));
                            System.out.println(String.format("Price: %f", price));
                            System.out.println();
                        }
                    }
                    else {
                        System.out.println("No books found for your request");
                    }
                }
                catch(AsnException e) {
                    System.out.println("The respone from the service has an invalid format");
                }
            }

            @Override
            public void onError(ResponseContext context, int errorCode, SequenceDecoder error) {
                System.out.println(String.format("The service returned an error code: %d", errorCode));
            }

            @Override
            public void onError(ResponseContext context, SoftnetException exception) {
                System.out.println(String.format("The RPC request failed with an error: %s", exception.getMessage()));
            }
        });
    }
	
    public static void executeEventReceivingDemo()
    {
        clientSEndpoint.subscribeToREvent("CurrentTemperature", new RemoteEventListener() {
            @Override
            public void accept(ClientEndpoint clientEndpoint, RemoteEvent remoteEvent) {
                try {
                    System.out.println(String.format("Received event: '%s'. Event number: %d. Age: %d seconds", 
                    remoteEvent.name, remoteEvent.arguments.Int32(), remoteEvent.age));
                }
                catch(AsnException e) {
                    System.out.println("Data format error");
                }
            }

            @Override
            public void acceptError(ClientEndpoint clientEndpoint, SoftnetException exception) {
                System.out.println(String.format("Event subscription error: %s", exception.getMessage()));
            }
        });
		
        clientSEndpoint.subscribeToQEvent("CriticalPressure", new RemoteEventListener() {
            @Override
            public void accept(ClientEndpoint clientEndpoint, RemoteEvent remoteEvent) {
                try {
                    System.out.println(String.format("Received event: '%s'. Event number: %d. Age: %d seconds.", 
                    remoteEvent.name, remoteEvent.arguments.Int32(), remoteEvent.age));
                }
                catch(AsnException e) {
                    System.out.println("Data format error");
                }
            }

            @Override
            public void acceptError(ClientEndpoint clientEndpoint, SoftnetException exception) {
                System.out.println(String.format("Event subscription error: %s", exception.getMessage()));
            }
    	});		
    }
	
    static void subscribeToTemperatureSet()
    {
        clientSEndpoint.subscribeToPEvent("TemperatureSet", new RemoteEventListener() {
            @Override
            public void accept(ClientEndpoint clientEndpoint, RemoteEvent remoteEvent) {
                try {
                    /* Print the task ID and current time */
                    SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
                    System.out.println(String.format("The 'Set Temperature' task completed. Task ID: %s. Time: %s", 
                    remoteEvent.arguments.OctetStringToUUID().toString(), timeFormat.format(new Date())));
                }
                catch(AsnException e) {
                    System.out.println("Data format error");
                }
            }

            @Override
            public void acceptError(ClientEndpoint clientEndpoint, SoftnetException exception) {
                System.out.println(String.format("Event subscription error: %s", exception.getMessage()));
            }
        });		
    }
	
    public static void executeAsyncCommDemo()
    {
        RemoteProcedure remoteProcedure = new RemoteProcedure("setTemperature");
        /* set the temperature to 30 degrees Celsius */ 
        remoteProcedure.arguments.Int32(30);
		
        clientSEndpoint.call(remoteProcedure, new RPCResponseHandler() {
            @Override
            public void onSuccess(ResponseContext context, SequenceDecoder result) {
                /* Print the task ID and current time */
                try {
                    SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
                    System.out.println(String.format("The 'Set Temperature' task created. Task ID: %s. Time: %s", 
                        result.OctetStringToUUID().toString(), timeFormat.format(new Date())));
                } 
                catch (AsnException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(ResponseContext context, int errorCode, SequenceDecoder error) {
                System.out.println(String.format("The service returned an error code: %d", errorCode));
            }

            @Override
            public void onError(ResponseContext context, SoftnetException exception) {
                System.out.println(String.format("The RPC request failed with an error: %s", exception.getMessage()));
            }
        });
    }
		
    public static void main(String[] args) {
        // Instead of this account details, specify the details of your client
        String client_uri = "softnet-s://hj1syian@ts.softnet-iot.org";
        String password = "ZW81jj8NQd";
		
        try {
            ClientURI clientURI = new ClientURI(client_uri);
	
            clientSEndpoint = ClientSEndpoint.create(
                "Test Service", 
                "John Doe",
                clientURI, 
                password,
                "Test Client");
			
            clientSEndpoint.addEventListener(new ClientEventAdapter() {
                @Override
                public void onConnectivityChanged(ClientEndpointEvent e) {
                    ClientEndpoint client = e.getEndpoint();
                    EndpointConnectivity connectivity = client.getConnectivity();
                    System.out.println(String.format("Endpoint connectivity status: %s, Error: %s, Message: %s", connectivity.status, connectivity.error, connectivity.message));
                }

                @Override
                public void onStatusChanged(ClientEndpointEvent e) {
                    ClientEndpoint client = e.getEndpoint();
                    System.out.println(String.format("Client status: %s", client.getStatus()));					
                }
				
                @Override
                public void onServiceOnline(RemoteServiceEvent e) {
                    System.out.println(String.format("Remote service online! Hostname: %s, API version: %s", e.service.getHostname(), e.service.getVersion()));
                    
                    executeTcpDemo();
                    //executeUdpDemo();
                    //executeRpcDemo();
                    //executeAsyncCommDemo();
                }		
            });
            
            //clientSEndpoint.setPersistenceL2();
            //executeEventReceivingDemo();
            
            // subscribeToTemperatureSet();
                        
            clientSEndpoint.connect();
            System.in.read(); /* waits until pressing the Enter key */
        }	
        catch(java.lang.Throwable e) {
            e.printStackTrace();
        }
        finally	{
            if(clientSEndpoint != null)
                clientSEndpoint.close();
            System.out.println("The client app is closed!");
        }
    }		
}

Credits

Robert Koifman
1 project • 1 follower
Contact

Comments

Please log in or sign up to comment.