Real-Time Data Transmission using UDP #5
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
-
UDP is widely used for real-time data transmission. Such data can include live audio/video or, for example, real-time digital samples of analog signals. The client application in this demo creates a UDP connection to the service application and sends 100 UDP packets at 200 milliseconds interval, with each packet containing a sequence number. The service application, in turn, prints a sequence number of each received packet. Because UDP is a non-guaranteed delivery protocol, depending on the quality of the network route, some of these packets may be lost. Which packets are lost can be determined by the missing numbers in the series of numbers printed by the service.
As usual, when explaining 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/.
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 “UDP 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 “Real-Time Data Transmission using UDP”. 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 “UDP 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 can be stored in the application's config file.
At this point your code may look like the following:
Do not forget to close the service endpoint in the end!
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 UDP connection and receiving packets from the UDP socket. Note that a UDP connection should be thought of as a route between two endpoints to transmit packets over the Internet, rather than a classic handshaking connection like TCP. How to work with UDP in service applications is described in "Handling UDP connection requests". The service endpoint accepts UDP connection requests on a virtual port, which is not associated with any permanent physical port. The 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 udpListen 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 17. The second parameter, <backlog>, plays the same role as in classical sockets. See in the "Handling UDP connection requests" for details. We set its value to 2.
To accept the established connections, ServiceEndpoint has a method udpAccept:
The first parameter, <virtualPort>, must have the same value as you specified for the udpListen. In our case, this is 17. The second parameter takes an implementation of the UDPAcceptHandler interface. The udpAccept method is called whenever the application is ready to handle the next established UDP connection. If you want to have only one accepted connection at a time, you can implement the next call to udpAccept just after closing the socket currently receiving data. In our case, we do this before leaving the accept method of MyUDPAcceptHandler:
accept is the only method of UDPAcceptHandler to implement. Its first parameter of type RequestContext has a serviceEndpoint field which is the service endpoint we created earlier. After receiving the set of UDP packets and closing the socket, we use this field to call udpAccept again. This is not a recursive call because udpAccept is a method called asynchronously. The second parameter, <datagramSocket>, of the accept method is a UDP socket for an established connection. The third parameter, <remoteSocketAddress>, is an IP address and port of the remote endpoint with which the connection is established. And the fourth parameter indicates the connection mode: peer-to-peer or proxy. We use the ASN.1 DER codec to encode/decode the UDP packet sequence numbers. 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, it has 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
Now we'll develop a client. In your favorite Java IDE create a console 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 “UDP Demo” and “Softnet Guide”, accordingly. The client account data, of course, is also different.
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 networking functionality for 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 “UDP 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 UDP connection to virtual port 17, which the service app is listening on. ClientSEndpoint has a method udpConnect 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 udpConnect similar to those of ClientSEndpoint, but these methods each has an additional parameter RemoteService used to specify the target remote service. See «Making UDP connection requests» for details.
So, for the first parameter of udpConnect we specify 17. The second parameter takes an implementation of UDPResponseHandler. 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 described in Making UDP connection requests. The second parameter, <datagramSocket>, of the onSuccess method is a UDP socket for an established connection. The third parameter, <remoteSocketAddress>, is an IP address and port of the remote endpoint with which the connection is established. And the fourth 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 UDP connection requests.
We can implement the UDPResponseHandler by anonymous class. Then, it might look like the following:
We can place the code for calling the udpConnect and the code for sending UDP packets 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 udpConnect 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 starts sending UDP packets, each containing a sequence number encoded in the ASN.1 format. When sending the packets, it prints their sequence numbers:
On the other side, the service app prints the sequence numbers of the received packets:
Beta Was this translation helpful? Give feedback.
All reactions