Direct TCP Connections to IoT Devices #4
Robert-Koifman
started this conversation in
Basic Examples
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
This demo illustrates how to use the Softnet API for establishing TCP connections to remote devices. As in the previous example, two apps, a client and a service, exchange greeting messages, but here they do so over a TCP connection.
When explaining the development steps for this example, I refer to the following guides:
Before getting started
In order to run this example, you should have a Softnet MS account and be in the “Provider” role. For the purpose of learning Softnet Free we offer you a free registration on http://ts.softnet-iot.org/.
You can run this example in any of well-known IDEs including Geany, Eclipse and IntelliJ IDEA. For setting up the endpoint library in your project, see “Setting up the endpoint library”.
The platform requires that outgoing connections to a specific set of TCP and UDP ports not be blocked. For details, see Requirements for the network infrastructure.
Creating the service application
Note! Before "The Application-specific Steps", the following material is identical to other service apps in Basic Examples, except that we set the Service Type to “TCP Demo” while the Contract Author is the same: “Softnet Guide”. The domain name is different, and the service account data, of course, is also different.
First, we go to the http://ts.softnet-iot.org/ and sign in to Softnet MS. Then, we create a new domain and name it “Direct TCP Connections to IoT Devices”. This is how a newly created domain looks like:
Then, we create a new site in the domain. A newly created site has a status “site blank” and contains a blank service registration with status “service type undefined”:
Now open your favorite Java IDE and create a console application. Add a class named “ServiceApp” and insert the method main into the class. Then, import the following packages:
The softnet.service.ServiceEndpoint class of the platform provides the networking functionality for a service application. It has a static method to create an instance of the class:
The first parameter of the method is an object that implements the SiteStructure interface. ServiceEndpoint has a static method to create the required object:
<serviceType> and <contractAuthor> are the minimum data required to create a SiteStructure object, so the method accepts these two mandatory parameters. Set the Service Type as “TCP Demo” and the Contract Author as “Softnet Guide”.
The second parameter of the create method is <version>. Assume the service’s API version is “1.0”. The method has two more parameters: <serviceUri> and <password>. Take their values on the site you have created in the beginning:
In my case, I had the following values for the <serviceUri> and <password>:
In real applications, this data might be stored in the application's config file.
At this point your code may look like the following:
After the endpoint is created, call the connect method. Enclose the code in a try-catch block. Do not forget to close the service endpoint in the end! I've done it in the 'finally' block.
There is a set of the service's state parameters controlled by the platform that you might want to monitor. For each of these parameters, the platform generates an event when its value changes. To intercept these events, ServiceEndpoint has a listener method called addEventListener:
This method accepts an object that implements the ServiceEventListener interface with appropriate event handlers. Instead of implementing all the methods of ServiceEventListener, you can extend an abstract class ServiceEventAdapter which has empty implementations of these methods. In this case, you can just override the methods you need.
This example demonstrates monitoring the service status and the service connectivity status:
The first method, onConnectivityChanged, is called whenever the service’s connectivity changes. It reflects the status of a physical connection with the Softnet server and the result of authentication. After the connection is established, Softnet examines the status of the service and calls the listener’s method onStatusChanged. If your service’s status is <Online>, it is ready to serve clients.
The Application-specific Steps
Here we have reached the point where we can implement accepting a TCP connection and exchanging greeting messages through the connection. How to work with TCP in service applications is described in "Handling TCP connection requests". Service endpoints accept TCP requests on virtual ports, which are not associated with physical ports. A service application first creates a binding to a virtual port, then accepts incoming requests as computing resources become available.
To create a virtual port binding, ServiceEndpoint has three tcpListen method overloads that differ in the way they define access rules. For simplicity, we use one that does not impose any access restrictions:
The first parameter is a virtual port. We set it to 10. As for TCP options, we let them default and provide null. The last parameter, <backlog>, plays the same role as in classical sockets (see for details here). We set its value to 2.
To accept the established connections, ServiceEndpoint has a method tcpAccept:
The first parameter, <virtualPort>, must have the same value as you specified for the tcpListen. In our case, this is 10. The second parameter takes an implementation of the TCPAcceptHandler interface. The tcpAccept method is called whenever the application is ready to handle the next established TCP connection. If you want to have only one accepted connection at a time, you can implement the next call to tcpAccept just after closing the current connection. In our case, we do this before leaving the accept method of MyTCPAcceptHandler:
accept is the only method of TCPAcceptHandler to implement. Its first parameter of type RequestContext has a serviceEndpoint field which is the service endpoint we created earlier. After processing the message exchange, we use this field to call tcpAccept again. This is not a recursive call because tcpAccept is a method called asynchronously. The second parameter, <socketChannel>, of the accept method is a TCP socket channel for an established connection. And the third parameter indicates the connection mode: peer-to-peer or proxy. We use the ASN.1 DER codec to encode/decode messages for transmission over the network. The Developer Guide to Softnet ASN.1 Codec (Java) explains how to use this codec in Java applications.
Now we just need to add the last lines of code to the main method:
Let's put everything together and compose the entire application:
Now we need to run the application so that the platform constructs the site and we could create the client entity. If the service endpoint establishes connection with the Softnet server, you application will print out something like this:
The meaning of these messages is as follows: on the first connection of the service endpoint, the server constructs the site, and then the server demands the service endpoint to re-establish the connection. When connecting to the server for the second time, the service is assigned the Online status.
The site, which was originally blank, will be constructed in accordance with the "siteStructure" object attached to the "serviceEndpoint". In case of our example, this takes the following view:
The platform adds the Owner user to the site by default. Now we can create clients associated with this user.
Creating the client application
Note! Before "The Application-specific Steps", the following material is identical to other client apps in Basic Examples, except that in the client endpoint creation we set the Service Type and Contract Author to “TCP Demo” and “Softnet Guide”, accordingly. The client account data, of course, is also different.
In your favorite Java IDE create a console application. Add a class named “ClientApp” and insert the main method into the class. Then, import the following packages:
The softnet.client.ClientSEndpoint class provides the network functionality to a client application to communicate with a single remote service. It has a static method to create an instance of the class, i.e. a client endpoint:
We set the first two parameters, <serviceType> and <contractAuthor>, as “TCP Demo” and “Softnet Guide”. This ensures that the client will be able to communicate only with a service which Service Type and Contract Author are the same. The third parameter is <clientURI>. We get it from the account section of the client registration. But, first, we have to create the client registration on the site. Let's open the example’s domain we have created in the beginning. Then we open the site in which we’ve registered the service. Clicking the clients button opens the client management section of the site:
Clicking the add client button to the right of the Owner creates an empty client registration:
Clicking the generate password button generates the password:
In my case, I had the following values for the <clientURI> and <password>:
In real applications, this data can be stored in the application's config file.
Now, we have all the required data. The following code shows how the client endpoint creation code may look like:
As with the service application, we want to monitor the client status and the client connectivity status. To intercept the platform events related to clients, ClientSEndpoint has a method addEventListener:
This method accepts an object that implements the ClientEventListener interface with appropriate event handlers. As with the service application, instead of implementing all the ClientEventListener methods, you can extend an abstract class ClientEventAdapter which has empty implementations of these methods. This allows you just to override the methods you want to implement.
Along with two events related to the status parameters described above, we need to intercept the ServiceOnline event, which notifies the client when the remote service goes online. To do so, we'll override the onServiceOnline handler of ClientEventAdapter. The code snapshot for the event handlers might look like this:
The way onServiceOnline works requires some explanation. Before making an TCP connection request to a remote service, your client app must be able to check its own online status and the online status of the remote service. If one or both of them are offline, the client should wait until the communication platform notifies that both are online. The onServiceOnline method serves this purpose. The platform calls it in one of the following two cases:
It follows that in the case of our single-service client, if the onServiceOnline method is called, both the client and the service are online, and the client can make a request.
The Application-specific Steps
Let's write the code for establishing a TCP connection to virtual port 10, which the service app is listening on. ClientSEndpoint has a method tcpConnect for this:
The second overload of this method has an extra parameter <requestParams> of type RequestParams, that allows the caller to specify such data as an attachment for the response handler, a request timeout, and a session tag for the remote request handler.
Note that ClientSEndpoint is a class derived from ClientEndpoint, which is a multi-service endpoint class. ClientEndpoint also has two overloaded methods tcpConnect similar to those of ClientSEndpoint, but these methods each has an additional parameter RemoteService used to specify the target remote service. See «Making TCP connection requests» for details.
So, for the first parameter of tcpConnect we specify 10. And we leave the TCP options by default, specifying null for the last parameter. As its second parameter, this method takes an implementation of TCPResponseHandler. Let's take a look at this interface:
The interface has two methods to implement. Each request ends with a callback to one of these methods. In case of success, the onSuccess method is called back. Its first parameter, <context>, is provided by the platform. Any response handler has this type of parameter. It is well-described in Making TCP connection requests. The second parameter, <socketChannel>, of the onSuccess method is a TCP socket channel for an established connection. And the third parameter indicates the connection mode: peer-to-peer or proxy.
The onError method is called back when an error is detected by the platform. It provides the error in the second parameter of type SoftnetException. The possible exceptions are described in Making TCP connection requests.
We can implement the TCPResponseHandler by anonymous class. Then, it might look like the following:
We can place the code for calling the tcpConnect and processing the message exchange in the body of onServiceOnline. Now, let's put everything together. The following is the entire code of the application:
Running the example
First we run the client applications. If everything is ok, you will see the following output:
From this output we can see that the client has connected to the server and it is assigned the Online status. Since the service is still offline, the onServiceOnline handler has not yet been called by the platform.
Now we run the service application, and If everything is ok, you will see the following output:
As the service came online, the platform raises ServiceOnline event in the client app, which is caught by the onServiceOnline handler. The handler prints the message provided below and calls the tcpConnect method.
If the connection is successfully established, the client prints the following message, with the only difference that the connection mode can be 'P2P' or 'Proxy':
Right at the same time, the identical message prints the service app too with the same connection mode as printed the client:
Then the client sends a "Hello! I'm a client." message through the connection. On the other side, the service application prints this message:
The service app sends back the response message "Hello! I'm a client.", and the client app prints this:
If you leave the apps connected, whenever any of the endpoints reconnect to the server, the TCP connection will by established again and message exchange will repeat.
Beta Was this translation helpful? Give feedback.
All reactions