項目需要,在發送和接受數據的時候使用RabbitMQ隊列作爲中間基站。想要對RabbitMQ有個基本的瞭解,可以結合官網:http://www.rabbitmq.com/,和這篇博客:http://blog.csdn.net/anzhsoft/article/details/19563091一起來理解。
因爲是在Qt中使用,所以需要用RabbitMQ-c,官網沒有c語言使用RabbitMQ的詳細介紹,上面的博客是使用Python來作爲說明的,所以還需另尋他路,自己琢磨琢磨。
準備工作:
1、使用Qt 4.7.4和Qt Creator 2.4.1及其的MinGW:
具體的下載安裝請見另一篇博客:Qt中使用Protocol Buffers(一):使用QT的MinGW + msys編譯Protocol Buffers v2.6.1
2、Cmak:
下載:https://cmake.org/download/,當前最新版本3.8.0,下載cmake-3.8.0-win64-x64.zip
安裝:解壓並將bin目錄添加到環境目錄中去,安裝成功後可以在cmd中輸入cmake --version查看cmake的版本,cmake --help查看cmake的幫助
3、RabbitMQ-c:
下載:GitHub上下載:https://github.com/alanxz/rabbitmq-c/,下載rabbitmq-c-master.zip,下載完後解壓,就可以開始編譯啦
開始編譯:
根據RabbitMQ-c的下載地址下面的說明文檔進行編譯
1、使用cmd進入到RabbitMQ的解壓地址,在rabbitmq-c-master文件夾中的cmake文件夾中創建文件夾build,然後輸入cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=../../install ../..來配置
cmake表示命令
-G "MinGW Makefiles"表示使用MinGW來生成文件
-DCMAKE_INSTALL_PREFIX=../../install表示在rabbitmq-c-master文件夾中創建(如果沒有)install文件夾,並在install的時候將文件存放其中。
../..表示CmakeLists.txt所在的文件夾rabbitmq-c-master的文件夾
配置結果:
在這一步的時候遇到兩個錯誤:
Error1: could not find cmakeroot,解決辦法:重新安裝cmake
Error2: could not find openssl:
解決辦法在這個網站找到:https://zhidao.baidu.com/question/587845628097951765.html,即將CMakeLists.txt文件中的ENABLE_SSL_SUPPORT的ON改成OFF,再刪除之前生成的文件重新編譯。
正確的編譯結果是:
D:\RabbitMQ\rabbitmq-c-master\cmake\build>cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=../../install ../..
-- The C compiler identification is GNU 4.4.0
-- Check for working C compiler: D:/Qt/qtcreator-2.4.1/mingw/bin/gcc.exe
-- Check for working C compiler: D:/Qt/qtcreator-2.4.1/mingw/bin/gcc.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- CMAKE_BUILD_TYPE not specified. Creating Release build
-- Found C inline keyword: inline
-- Looking for htonll
-- Looking for htonll - not found
-- Looking for poll
-- Looking for poll - not found
-- Looking for clock_gettime in rt
-- Looking for clock_gettime in rt - not found
-- Looking for posix_spawnp in rt
-- Looking for posix_spawnp in rt - not found
-- Performing Test HAVE_GNU90
-- Performing Test HAVE_GNU90 - Failed
-- Performing Test HAVE_C90
-- Performing Test HAVE_C90 - Failed
-- Could NOT find POPT (missing: POPT_INCLUDE_DIR POPT_LIBRARY)
-- Could NOT find XMLTO (missing: XMLTO_EXECUTABLE)
-- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
-- Building rabbitmq as a shared library - yes
-- Building rabbitmq as a static library - yes
-- Configuring done
-- Generating done
-- Build files have been written to: D:/RabbitMQ/rabbitmq-c-master/cmake/build
2、配置成功後就是編譯make,輸入make
3、編譯成功後就是安裝,輸入make install:
如果安裝成功後再次輸入make install就是如下內容:
4、安裝成功,可以在install文件夾中查找到以下內容:
bin文件夾:librabbitmq.4.dll
include文件夾:amqp.h、amqp_framing.h、amqp_tcp_socket.h
lib文件夾:liblibrabbitmq.4.a、librabbitmq.4.dll.a
簡單實例:(測試安裝是否成功)
依然是根據GitHub上面的安裝教程,後面接着的running the examples,不過它是用cmd命令行運行的,我直接在Qt中使用。
1、分別建兩個工程,控制檯類型,一個用來發送一個用來監聽
2、分別在每個工程文件的文件夾裏添加上三個頭文件:amqp.h、amqp_framing.h、amqp_tcp_socket.h和一個靜態庫文件:librabbitmq.4.dll.a
3、分別在每個工程的.pro文件中添加
LIBS += -LPWD”是當前目錄,即main.cpp所在目錄,因爲我的librabbitmq.4.dll.a放在當前目錄中,所以直接這麼寫就可以,注意:/後面可以有空格,但是-l和文件之間不能有空格,且文件名中的lib和.dll.a皆省略。
4、編譯運行文件會發現不成功,但會生成一個工程文件夾,將librabbitmq.4.dll文件添加到生成的工程文件夾中的debug文件夾中去,再次編譯運行即
運行結果:
運行出現的錯誤:
Logging in:socket is closed,兩個可能:
1、檢查hostname、prot、exchange、routingkey / bindingkey、user、psw,可能筆誤寫錯。
2、主機的服務器沒有開啓,導致socket is closed,本例是使用localhost作爲服務器,所以還需要在本地下載RabbitMQ的服務端,並打開使用才能成功運行。
rabbitmq-c的使用說明:
1、需要有主機名:hostname、端口號:port、交換機:exchange、路由鑰匙:routingkey / 綁定鑰匙:bindingkey、用戶名:user、密碼:psw,我們的實例中默認用戶名和密碼都是guest。
2、先建立TCP連接:
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
3、打開建立的TCP連接,使用socket,主機名和端口號:
status = amqp_socket_open(socket, hostname, port);
4、登錄:
amqp_login(conn, “/”, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, “guest”, “guest”);
5、打開信道:
amqp_channel_open(conn, 1);
amqp_get_rpc_reply(conn);
6、開始發送或者監聽消息
7、關閉信道、關閉連接、銷燬連接:
amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS);
amqp_connection_close(conn, AMQP_REPLY_SUCCESS);
amqp_destroy_connection(conn);
發送和監聽的main.cpp文件分別是:
//發送"Hello World!”消息
#include <QtCore/QCoreApplication>
#include <QDebug>
//RabbitMQ相關頭文件
#include "amqp.h"
#include "amqp_framing.h"
#include "amqp_tcp_socket.h"
void die_on_error(int x, char const *context)
{
if (x < 0) {
fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x));
exit(1);
}
}
void die_on_amqp_error(amqp_rpc_reply_t x, char const *context)
{
switch (x.reply_type) {
case AMQP_RESPONSE_NORMAL:
return;
case AMQP_RESPONSE_NONE:
fprintf(stderr, "%s: missing RPC reply type!\n", context);
break;
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x.library_error));
break;
case AMQP_RESPONSE_SERVER_EXCEPTION:
switch (x.reply.id) {
case AMQP_CONNECTION_CLOSE_METHOD: {
amqp_connection_close_t *m = (amqp_connection_close_t *) x.reply.decoded;
fprintf(stderr, "%s: server connection error %uh, message: %.*s\n",
context,
m->reply_code,
(int) m->reply_text.len, (char *) m->reply_text.bytes);
break;
}
case AMQP_CHANNEL_CLOSE_METHOD: {
amqp_channel_close_t *m = (amqp_channel_close_t *) x.reply.decoded;
fprintf(stderr, "%s: server channel error %uh, message: %.*s\n",
context,
m->reply_code,
(int) m->reply_text.len, (char *) m->reply_text.bytes);
break;
}
default:
fprintf(stderr, "%s: unknown server error, method id 0x%08X\n", context, x.reply.id);
break;
}
break;
}
exit(1);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//RabbitMQ start
char const *hostname;
int port, status;
char const *exchange;
char const *routingkey;
char const *messagebody;
amqp_socket_t *socket;
amqp_connection_state_t conn;
hostname = "localhost";
port = 5672;
exchange = "amq.direct";
routingkey = "test";
messagebody = "Hello World!";
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
qDebug() << "creating TCP socket";
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
qDebug() << "opening TCP socket";
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, "guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
{
amqp_basic_properties_t props;
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;
props.content_type = amqp_cstring_bytes("text/plain");
props.delivery_mode = 2; /* persistent delivery mode */
die_on_error(amqp_basic_publish(conn,
1,
amqp_cstring_bytes(exchange),
amqp_cstring_bytes(routingkey),
0,
0,
&props,
amqp_cstring_bytes(messagebody)),
"Publishing");
}
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), "Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), "Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
//RabbitMQ end
return a.exec();
}
//監聽“Hello World!”消息
#include <QtCore/QCoreApplication>
#include <QDebug>
//RabbitMQ相關頭文件
#include "amqp.h"
#include "amqp_framing.h"
#include "amqp_tcp_socket.h"
void die_on_error(int x, char const *context)
{
if (x < 0) {
fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x));
exit(1);
}
}
void die_on_amqp_error(amqp_rpc_reply_t x, char const *context)
{
switch (x.reply_type) {
case AMQP_RESPONSE_NORMAL:
return;
case AMQP_RESPONSE_NONE:
fprintf(stderr, "%s: missing RPC reply type!\n", context);
break;
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x.library_error));
break;
case AMQP_RESPONSE_SERVER_EXCEPTION:
switch (x.reply.id) {
case AMQP_CONNECTION_CLOSE_METHOD: {
amqp_connection_close_t *m = (amqp_connection_close_t *) x.reply.decoded;
fprintf(stderr, "%s: server connection error %uh, message: %.*s\n",
context,
m->reply_code,
(int) m->reply_text.len, (char *) m->reply_text.bytes);
break;
}
case AMQP_CHANNEL_CLOSE_METHOD: {
amqp_channel_close_t *m = (amqp_channel_close_t *) x.reply.decoded;
fprintf(stderr, "%s: server channel error %uh, message: %.*s\n",
context,
m->reply_code,
(int) m->reply_text.len, (char *) m->reply_text.bytes);
break;
}
default:
fprintf(stderr, "%s: unknown server error, method id 0x%08X\n", context, x.reply.id);
break;
}
break;
}
exit(1);
}
static void dump_row(long count, int numinrow, int *chs)
{
int i;
printf("%08lX:", count - numinrow);
if (numinrow > 0) {
for (i = 0; i < numinrow; i++) {
if (i == 8) {
printf(" :");
}
printf(" %02X", chs[i]);
}
for (i = numinrow; i < 16; i++) {
if (i == 8) {
printf(" :");
}
printf(" ");
}
printf(" ");
for (i = 0; i < numinrow; i++) {
if (isprint(chs[i])) {
printf("%c", chs[i]);
} else {
printf(".");
}
}
}
printf("\n");
}
static int rows_eq(int *a, int *b)
{
int i;
for (i=0; i<16; i++)
if (a[i] != b[i]) {
return 0;
}
return 1;
}
void amqp_dump(void const *buffer, size_t len)
{
unsigned char *buf = (unsigned char *) buffer;
long count = 0;
int numinrow = 0;
int chs[16];
int oldchs[16] = {0};
int showed_dots = 0;
size_t i;
for (i = 0; i < len; i++) {
int ch = buf[i];
if (numinrow == 16) {
int j;
if (rows_eq(oldchs, chs)) {
if (!showed_dots) {
showed_dots = 1;
printf(" .. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..\n");
}
} else {
showed_dots = 0;
dump_row(count, numinrow, chs);
}
for (j=0; j<16; j++) {
oldchs[j] = chs[j];
}
numinrow = 0;
}
count++;
chs[numinrow++] = ch;
}
dump_row(count, numinrow, chs);
if (numinrow != 0) {
printf("%08lX:\n", count);
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//RabbitMQ start
char const *hostname;
int port, status;
char const *exchange;
char const *bindingkey;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
amqp_bytes_t queuename;
hostname = "localhost";
port = 5672;
exchange = "amq.direct";
bindingkey = "test";
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
qDebug() << "creating TCP socket";
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
qDebug() << "opening TCP socket";
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, "guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
//大括號指明變量的作用域
{
amqp_queue_declare_ok_t *r = amqp_queue_declare(conn, 1, amqp_empty_bytes, 0, 0, 0, 1,
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
queuename = amqp_bytes_malloc_dup(r->queue);
if (queuename.bytes == NULL) {
fprintf(stderr, "Out of memory while copying queue name");
return 1;
}
}
amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange), amqp_cstring_bytes(bindingkey),
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue");
amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0, amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
{
for (;;) {
amqp_rpc_reply_t res;
amqp_envelope_t envelope;
amqp_maybe_release_buffers(conn);
res = amqp_consume_message(conn, &envelope, NULL, 0);
if (AMQP_RESPONSE_NORMAL != res.reply_type) {
break;
}
printf("Delivery %u, exchange %.*s routingkey %.*s\n",
(unsigned) envelope.delivery_tag,
(int) envelope.exchange.len, (char *) envelope.exchange.bytes,
(int) envelope.routing_key.len, (char *) envelope.routing_key.bytes);
if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
printf("Content-type: %.*s\n",
(int) envelope.message.properties.content_type.len,
(char *) envelope.message.properties.content_type.bytes);
}
printf("----\n");
amqp_dump(envelope.message.body.bytes, envelope.message.body.len);
amqp_destroy_envelope(&envelope);
}
}
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), "Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), "Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
//RabbitMQ end
return a.exec();
}