The purpose of this example is quick & dirty. It's meant to serve as a low-level example on how to establish a tcp connection to a local RabbitMQ server, perform the handshake, publish a message to a pre-existing queue, and then shut down the connection gracefully.
I wrote this as a guide for writing embedded logic on a microcontroller. That microcontroller only needs to "report" on a status when it changes, aka an alert.
My goal was not elegance. My goal was to create an MVP (minimally viable product) that is purely a learning tool...so yes, there is repeated code and its not as clean as it could be.
I am running a RabbitMQ server on a local Docker container. The rabbitmq/3-management image, to be precise.
Default Rabbit MQ setup:
- Port
5672
- Username
guest
- Password
guest
- Virtual Host
/
I created a queue named, lee.test.q
.
I am using CMake as my build utility but it's a bit of overkill for this. (That and I'm getting used to CMake)
My files:
- program.c
- The main program lives here. So does virtually all of the code.
- program.h
- The header file. Includes, some defines and some structure definitions for the most part. I also included some function prototypes for visibility.
- converters.c
- When playing with arrays of bytes, sometimes you have to extract out short and integer numbers. I have some conversion routines in here to help me out.
I had to do a lot of pouring over AMQP specifications and cross-referencing things because they just weren't clear enough for me, right off the bat. RabbitMQ has some nice documentation on the various AMQP methods I needed but to really track down the details of the data structures and values, I had to use the XML specification.
- AMQP Protocol Specification
- AMQP XML Specification
- RabbitMQ AMQP 0-9-1 Quick Reference
- Simple Authentication and Security Layer (SASL)
The job that this code performs is to publish the message, "Hello, there!" to the lee.test.q
queue on my local RabbitMQ server. I foolishly thought
it was a simple matter of opening a TCP socket and pushing some bytes to it. Oh how naive that thought was!
Turns out, my steps were more along the lines of:
- Open a TCP socket to the server
- Push a Protocol message to get things started:
{'A','M','Q','P',0,0,9,1}
- Get back a Start message
- Respond with a Start OK message
- Get a Tune message
- Respond with a Tune OK message
- Push an Open message
- Push a message to Open Channel for Channel 1.
- Channel 0 is the system channel for things like establishing connections.
- Push a message that we want to Publish some content
- Push a Content Header that describes the content
- Push the Content, which for us is "Hello, there!"
- Push a Close connection message
- Shutdown our TCP socket.
One of the nice things when troubleshooting socket communication is being able to see what is going on from both sides of the communication. I spent a lot of time just guessing what the server was seeing and how it was handling things. Those guesses led to wrong assumptions and a lot of headache/time wasted. Then Google came to the rescue with a nice little command for viewing a tail of the RabbitMQ server's logs!
docker logs localrabbit
Omg, this is helpful! Of course, I named my docker container to localrabbit
so if you didn't do that, the command will be slightly different for you.
It was telling me if the server was having difficulties parsing my messages (it was) and dumped the byte arrays for me. Normal AMQP specs state that if anything
looks weird or wrong, just kill the connection. With these logs, I can now see why the server was killing my socket.
No work nor assistance should go unpunis....er, unrecognized. I'll freely admit that I was only able to get this done with a lot of help from others who have done better work than I.
- rabbitmq-c library.
- This is a nice C library maintained by alanxz. If you are looking for a library for RabbitMQ communication, this is what you want!
- RabbitMQ .NET Client
- I datamined this library for message structures when the specs failed me.
- AMQP-CPP
- Another project I dug through for information.
- StackOverflow
- I haven't messed with bits && bytes since before StackOverflow was born. Packing/Unpacking integers to bytes escaped me one afternoon. Thank you, StackOverflow!
- Google
- I still remember what programming was like before there was Google. I don't want to go back to those days.