ZeroMQ with Kotlin: part 1, simple client-server example
ZeroMQ with Kotlin:Simple client-server example
ZeroMQ is described on wikipedia as a high-performance asynchronous messaging library, aimed at use in distributed or concurrent applications.
It has features of a networking library and touches a lot on concurrency, making it a bit difficult to categorize. I think one can define it using the utility to which it serves the person using it, the authors of the library do it more justice in their definition on their site in a hundred words.
The library has ports to many languages and the site has plenty of examples. For learning purposes i wanted to explore how to implement it in Kotlin which am learning as well. Fortunately for us ZeroMQ has a nice port in Java called JeroMQ
We’ll create a basic Client-Server example where the Client will send the Server a Request and the Server will respond with a Reply. We begin by creating a blank Kotlin project that uses Gradle as the build system using our preferred IDE.
We’ll import JeroMQ from maven repository by adding this dependency in our code
We’ll create two files MyServer.kt for the server logic and MyClient.kt for the client logic. Our project structure looks as follows from the filesystem
We start by going in into MyServer.kt file
We’ll not create a class for our example but use a simple top level declaration of the main method.
We create ZeroMQ Context ZMQ.context(1)
which takes in the number of io Threads you want the context to use. As ZeroMQ sockets aren't thread-safe (but contexts are) sticking to 1 at the moment feels like a safe bet until we're experienced enough to safely use multiple threads.
In this example we’ll be using two sockets that go together which will be REQUEST and REPLY sockets (ZMQ.REP and ZMQ.REQ). There are more socket pairs like PUSH & PULL, DEALER & ROUTER, PAIR and some more all of which highlight the versatile scenarios ZeroMQ can be used.
Our server will Reply to the Requests from the client, so it will implement the Reply Socket
val socket = context.socket(ZMQ.REP)
We’ll then bind the socket to a free port of our choice
In our case we choose 5879 but in case this socket is being by another program we would get this kind of error when we run the program at the end of our example. Exception in thread "main" org.zeromq.ZMQException: Errno 48 : Address already in use
. We can simply add one to the last number so if you chose a port like 5555 and found it was in use by another program, next try 5556 and 5557 and so on, and don't forget to also match the change of the port on the client side as well, to ensure that they communicate with each other.
As we want our server to run non-stop, we create an infinite loop, inside this loop is where the server will receive request.
In ZeroMQ, Request and Reply are sent as length-specified binary data. So the rawRequest
type is Array[Byte] which can easily be converted back into a String for our usage.
We convert to String as follows and print to the console to see the requests as they come in:
What we’re doing above is converting the byte array to string using the Java constructor who’s signature is
String(bytes:Array<Byte>, offset:Int, length:Int)
It constructs a new String
by decoding the specified subarray of bytes using the platform’s default charset.
After we print the Request received, we send a Reply back to the client. In our case the client will be sending in “Hello” and we’ll be responding back with “World”. We store the string in a variable and then convert it to a ByteArray that can be sent over the wire.
We set the last byte of rawReply to 0, this is used as a delimiter between messages. This is used by ZeroMQ to differentiate where one message ends and the other begins
After that we’re ready to send the reply back to the client
The complete MyServer.kt looks like this:
The client side of the app looks the same with few differences. We create MyClient.kt file with a single top level main method as well. One key difference is that our socket in this case will not be a Reply socket but a Request one ZMQ.REQ
Also note we connect to the same port used by the server. For our client, we’ll send 10 requests with the string “Hello” to the server by looping using a range
We do the same thing as before with the server and convert our String to ByteArray and then add a 0 delimiter for ZeroMQ before sending it to the socket.
Once it is sent, we’ll want to listen for a Reply. We do this immediately after sending the request.
We convert the ByteArray reply we receive to a string and print it to the stdout.
The complete MyClient.kt looks as follows
Here’s the MyServer.kt file as well for clarity
Once we have both files in place. We’ll need to first start the server which will run continuously due to the infinite loop and then start the client. We first move to the MyServer.kt and press ‘run’ in our IDE, then move to MyClient.kt and run it -sending the 10 “Hello “ requests to the server and receive “World” Replies for each.
Here’s the link to the github repo that contains the project.If anything is unclear or you have any advise on the article please let me know i’ll greatly appreciate it.
EDIT- I figured to add images with the outputs for the server and client.
This is the MyClient.kt output
and this is the output of MyServer.kt
Edit
A correction, In Kotlin, the messages aren’t zero-terminated but are simply length-based. Converting a string to a bytearray and then directly sending it to the wire will work without doing anything else
var myByteArr = myString.toByteArray()
socket.send(myByteArr)
. And also, calling String(bytearray) to convert it to a string will suffice
var myString = String(receivedByteArr)
Here’s part 2 where we cover a bit on push/pull and pub/sub