一、ØMQ的消息處理
使用套接字來傳輸數據
- 但ØMQ的I/O模型與TCP模型有很大區別,你需要時間來轉變觀念
- 處理數據時,TCP套接字和ØMQ套接字之間的差異:
- ØMQ套接字像UDP那樣傳遞信息,而不是像TCP那樣傳遞字節流。ØMQ消息是指定長度的二進制數據,因爲它們的設計針對性能進行了優化,所以有點棘手
- ØMQ套接字在一個後臺線程執行自己的IO。這意味着消息到達本地輸入隊列並從本地輸出隊列被髮送,不會影響到你的應用程序運行
- ØMQ套接字根據套接字類型具有內置的1對N的路由行爲
zmq_msg_xxx()消息處理接口
- 在內存中,ØMQ消息是zmq_msg_t表示的結構(或類,取決於你採用的語言)
- 下面是C語言中使用ØMQ消息的基本規則:
- 創建並創建zmq_msg_t對象,使用zmq_msg_t來表示消息,而不是使用普通的數據塊(char*)來交互數據
- 要讀取消息,可使用zmq_msg_int()創建一個空的消息,然後傳遞給zmq_msg_recv()
- 要寫入消息,可以使用zmq_msg_init_size()來創建消息,並分配某個大小的數據塊數據,使用memcpy()將數據塊的數據拷貝給zmq_msg_t,然後將zmq_msg_t傳遞給zmq_msg_send()進行發送
- 要釋放消息,則調用zmq_msg_close(),這會刪除一個引用,當消息引用爲0時,ØMQ會最終自動幫你銷燬該消息
- 要訪問消息內容,可以使用zmq_msg_data()
- 要知道消息包含多少數據,可以使用zmq_msg_size()
- 一般不建議使用zmq_msg_move()、zmq_msg_copy()、zmq_msg_init_data(),除非你的目標很明確就是要用這些函數
- zmq_msg_send()傳遞一個消息時候,會把該消息清除(把它的大小設置爲0),因此消息發送之後需要關閉(zmq_msg_close())並且不再使用。如果你想多次發送相同的數據,可以創建兩個zmq_msg_t消息對象發送,或者在調用zmq_msg_init()之前使用zmq_msg_copy()拷貝兩份一樣的數據並同時發送
- 此處給出的只是個大概,更多的細節參閱下面的接口介紹和演示案例
ØMQ對字符串的處理
- 在前面的文章我們介紹瞭如何處理ØMQ的字符串並封裝了下面兩個字符串處理函數,文章可以參閱:https://blog.csdn.net/qq_41453285/article/details/105991716
- 下面是自定義的兩個函數:
- 一個是字符串接收函數:其從網絡中接收一個ØMQ字符串,並申請多1個字節空間的內存保存該字符串,然後在尾部要添加0,以終止該字符串
- 一個是字符串發送函數:向網絡中發送一個字符串,單發送的字符串不含尾部的空字符
// 從套接字接收ØMQ字符串,並將其轉換爲C/C++字符串(在尾部添加0) static char *s_recv(void* socket) { // 此處使用zmq_msg_init()初始化即可, zmq_msg_recv()在內部會自動對zmq_msg_t對象進行大小設定 zmq_msg_t message; zmq_msg_init(&message); int size = zmq_msg_recv(&message, socket, 0); if(size == -1) return NULL; char *string = (char*)malloc(size + 1); memcpy(string, zmq_msg_data(&message), size); zmq_msg_close(&message); string[size] = 0; return string; }
// 將C字符串轉換爲ØMQ字符串(去掉尾部的'\0'),併發送到指定的套接字上 static int s_send(void *socket, char *string) { // 因爲是將數據拷貝給zmq_msg_t對象, 因此需要使用zmq_msg_init_size進行初始化 zmq_msg_t msg; zmq_msg_init_size(&msg, strlen(string)); memcpy(zmq_msg_data(&msg), string, strlen(string)); // 發送數據 int rc = zmq_msg_send(&msg, socket, 0); // 關閉zmq_msg_t對象 zmq_msg_close(&msg); return rc; }
“部件”和“幀”的概念
- 幀(Frame)(在ØMQ參考手冊中也稱爲“消息部件”)是ØMQ消息的基本線路格式。幀是已指定長度的數據塊,此長度可以從零向上。如果做過任何TCP編程工作,你就會明白,爲什麼幀是“現在我應該從網絡套接字讀出多少數據?”這個問題的一個有用的答案
- 最初,一個ØMQ消息是一幀,像UDP一樣。後面我們採用多部分消息來擴展了這一點,這是相當簡單的帶有被設置爲1的“more”位的幀的序列,接着是一個該位被設置爲零的幀。ØMQ API然後讓你寫入一個“more”標誌的消息,並且當你讀取消息是,它可以讓你檢查是否存在“more”
- 因此,在低級別ØMQ API和參考手冊中,有關於消息與部分有一些模糊性。下面用一個有用的詞彙表來說明:
- 消息可以是一個或多個部件
- 這些部件也稱爲幀
- 每個部件都是一個zmq_msg_t對象
- 你用低級別的API分別發送和接收各個部件
- 高級別API爲發送整個多部分消息提供包裝
-
其他與消息相關的內容:
- 你可以發送長度爲0的消息。例如,用於從一個線程把一個信號發送給另一個線程
- ØMQ保證提供一個消息所有的部分(一個或多個)或者一個部分也沒有
- ØMQ不立刻發送消息(單個或多部分),而在以後某些不確定的時間發送。因此,一個多部分消息必須在內存中裝入
- 單部分消息也必須裝入內存。如果你想發送任意大小的文件,應該把它們分解成片,並把每一片作爲獨立的單部分消息發送
- 在作用域關閉時不自動銷燬對象的語言中,在完成消息時,必須調用zmq_msg_close()
- 輕易不要使用zmq_msg_init_data(),這是一個零拷貝的方法,如果使用不好會帶來麻煩。零拷貝可參閱:https://blog.csdn.net/qq_41453285/article/details/106845900
二、多部分消息
- ØMQ允許我們撰寫由多個幀組成的單個消息,從而給我們一個“多部分消息”。實際的應用程序中相當多地使用了多部分消息,無論是包裝帶地址信息的消息,還是進行簡單的序列化
- 關於多部分消息,你需要了解的是:
- 當發送一個多部分消息時,僅當你發送最後的部分時,所有的消息纔會整整在線路上實際發送
- 如果你使用的是zmq_poll(),當你收到一條消息的第一部分時,其餘部分也都是已經到達了
- ØMQ確保消息的原子傳遞:對等方應該收到消息的所有消息部分,或者根本不收到任何消息。除非關閉套接字,否則沒有辦法取消部分發送的消息
- 消息部分的總數不受可用存儲空間的限制
- 在使用多部分消息時,每個部分都是一個zmq_msg_t條目。例如,如果你要發送的消息具有5個部分,你就必須構造5個zmq_msg_t對象
- 在發送時,ØMQ消息幀都在內存中排隊,直到最後一個小熊被接收到位置,然後再一起發送它們
- 在接收時,無論你是否設置RCVMORE選項,都將受到一條消息的所有部分
- 在後面的文章中我們會研究應答封包。現在我們學習如何安全地(但一位地)在需要轉發消息但不檢查它們的任何應用程序(如代理)中讀寫多部分消息
寫多部分消息
- 例如,下面發送三條消息,三條消息組成一條多部分消息,並且調用三次zmq_msg_send()發送出去,注意其中用到了ZMQ_SNDMORE選項
zmq_msg_t message1; zmq_msg_t message2; zmq_msg_t message3; //初始化這三條消息 //發送第一條, 指定ZMQ_SNDMORE選項, 表示發送的是多部分消息的其中一部分, 後面還要消息要發送 zmq_msg_send(socket, &message1, ZMQ_SNDMORE); //發送第二條,同上 zmq_msg_send(socket, &message2, ZMQ_SNDMORE); //發送最後一條消息, 因爲後面沒有消息要發送了, 因此最後一個參數爲0即可 zmq_msg_send(socket, &message3, 0);
- 更多詳細細節可以參閱下面“zmq_msg_send()的介紹及其演示案例②”
讀多部分消息
- 在接收消息時,可以使用ZMQ_RCVMORE調用zmq_getsockopt()函數來判斷套接字是否還有更多的消息要接收
- 下面是一個即可以處理單部分消息又可以處理多部分消息的代碼
while(1) { zmq_msg_t message; zmq_msg_init(&message); zmq_msg_recv(socket, &message, 0); zmq_msg_close(&message); int more; size_t more_size = sizeof(more); zmq_getsockopt(socket, ZMQ_RCVMORE, &more, &more_size); if(!more) break; }
三、接口使用的幾點說明
關於zmq_msg_init()和zmq_msg_init_size()的踩坑記錄
- 這兩個函數曾經騷擾我半天,由於ØMQ操作文檔說明的不想洗,我搞了半天才弄好
- ①在發送數據的時候:我們需要調用memcpy()將數據拷貝到zmq_msg_t中進行發送,不可以調用zmq_msg_init()初始化的zmq_msg_t對象進行存儲,因爲zmq_msg_init()初始化的對象其大小被設定爲0,在調用zmq_msg_send()的時候會報錯的。見下面代碼
/*******下面這種情況是錯誤的*******/ zmq_msg_t msg; zmq_msg_init(&msg); // 將str拷貝給msg char *str= "HelloWolrd"; memcpy(zmq_msg_data(&msg), str, 11); // 打印的是HelloWolrd, 但是大小爲0, 大小爲0就代表該zmq_msg_t對象不可用 printf("%s %ld\n", (char*)zmq_msg_data(&msg), zmq_msg_size(&msg)); // zmq_msg_send()會出錯, msg雖然有內容, 但是其大小爲0 // zmq_msg_send(&msg, socket, 0);
- 發送數據的時候請使用zmq_msg_init_size()初始化對象,這樣發送出去的zmq_msg_t對象是有大小的,不會被zmq_msg_send()判斷爲是錯的
/*******下面這種情況是正確的*******/ char *str= "HelloWolrd"; // 初始化時指定其大小 zmq_msg_t msg; zmq_msg_init_size(&msg, strlen(str) + 1); // 將str拷貝給msg memcpy(zmq_msg_data(&msg), str, 11); // 打印HelloWorld, 大小爲10 printf("%s %ld\n", (char*)zmq_msg_data(&msg), zmq_msg_size(&msg)); // zmq_msg_send()調用成功 // zmq_msg_send(&msg, socket, 0);
- ②在接收數據的時候:接收數據時,可以使用zmq_msg_init()定義的zmq_msg_t對象來保存數據,zmq_msg_recv()函數內部會自動的設置zmq_msg_t對象的大小
// 初始化時指定其大小 zmq_msg_t msg; zmq_msg_init(&msg); // 接收數據, zmq_msg_recv()內部會自動 zmq_msg_recv(&msg, socket, 0);
關於拷貝
- 當把數據拷貝給zmq_msg_t對象時,如果數據的長度超過zmq_msg_t對象的大小,zmq_msg_t對象仍然可以獲取完整數據,但是使用起來時只能使用其指定的大小
#include <stdio.h> #include <zmq.h> int main() { // 初始化msg時, 指定其大小爲5 zmq_msg_t msg; zmq_msg_init_size(&msg, 5); // 拷貝11字節給msg memcpy(zmq_msg_data(&msg), "HelloWorld", 11); // 打印的大小將爲5 printf("%s %ld\n", (char*)zmq_msg_data(&msg), zmq_msg_size(&msg)); zmq_msg_close(&msg); return 0; }
四、zmq_msg_t結構及源碼分析
- 本文使用的源碼爲zeromq4.1.7
zmq_msg_t的結構
- ØMQ用zmq_msg_t結構來表示一條小消息,其源碼定義如下:
//zmq.h typedef struct zmq_msg_t { #if defined (__GNUC__) || defined ( __INTEL_COMPILER) || \ (defined (__SUNPRO_C) && __SUNPRO_C >= 0x590) || \ (defined (__SUNPRO_CC) && __SUNPRO_CC >= 0x590) unsigned char _ [64] __attribute__ ((aligned (sizeof (void *)))); #elif defined (_MSC_VER) && (defined (_M_X64) || defined (_M_ARM64)) __declspec (align (8)) unsigned char _ [64]; #elif defined (_MSC_VER) && (defined (_M_IX86) || defined (_M_ARM_ARMV7VE)) __declspec (align (4)) unsigned char _ [64]; #else unsigned char _ [64]; #endif } zmq_msg_t;
- zmq_msg_t並不是真正存儲數據的地方:我們在操作的時候zmq_msg_t的時候,實際上將其轉換爲zmq命令空間中的一種msg_t類來使用的。例如下圖所示:調用zmq_msg_int()初始化zmq_msg_t的時候實際調用的就是msg_t類的init()方法
msg_t類
- msg_t類是真正存儲數據的地方,如下圖所示(代碼被縮減了,完整的定義見msg.hpp)
- 結構如下:
- base結構體:其有一個type成員用來表示這個消息屬於什麼類型的,不同類型的消息會用下面不同的結構體存儲消息
- vsm、lmsg、cmsg等結構體:代表消息的不同類型,當前msg_t屬於哪一種類型,哪一個結構體就會被初始化。其中這些結構體中的data字段存儲的是消息的真正值、size存儲消息的大小等
//msg.hpp namespace zmq { class msg_t { public: bool check (); int init (); int init_size (size_t size_); int init_data (void *data_, size_t size_, msg_free_fn *ffn_, void *hint_); int init_delimiter (); int close (); int move (msg_t &src_); int copy (msg_t &src_); void *data (); size_t size (); private: struct content_t { void *data; //真正存儲數據的複方 size_t size; //數據的大小 msg_free_fn *ffn; //數據釋放函數, 見zmq_msg_init_data()函數 void *hint; //傳遞給ffn的參數 zmq::atomic_counter_t refcnt; //消息的引用計數 }; union { struct { metadata_t *metadata; unsigned char unused [msg_t_size - (8 + sizeof (metadata_t *) + 2)]; unsigned char type; unsigned char flags; } base; struct { metadata_t *metadata;//元數據 unsigned char data [max_vsm_size];//消息數據 unsigned char size; unsigned char type; unsigned char flags; } vsm; //vsm消息類型 struct { metadata_t *metadata;//元數據 content_t *content;//消息數據 unsigned char unused [msg_t_size - (8 + sizeof (metadata_t *) + sizeof (content_t*) + 2)]; unsigned char type; unsigned char flags; } lmsg; //lmsg消息類型 struct { metadata_t *metadata; void* data;//消息數據 size_t size; unsigned char unused [msg_t_size - (8 + sizeof (metadata_t *) + sizeof (void*) + sizeof (size_t) + 2)]; unsigned char type; unsigned char flags; } cmsg; //cmsg消息類型 struct { metadata_t *metadata; unsigned char unused [msg_t_size - (8 + sizeof (metadata_t *) + 2)]; unsigned char type; unsigned char flags; } delimiter; } u; }; }
API分析
- 以zmq_msg_init()爲例,它會在內部調用msg_t的init_size()函數,
- init_size()函數會根據消息的大小來初始化msg_t的不同類型(vsm、lmsg、cmsg等類型),同時將結構的數據初始化
- 以zmq_msg_data()爲例,它會在內部調用msg_t的data()函數,
- data()函數會根據base結構體中的type字段來判斷當前數據屬於什麼類型,進而將不同結構的數據(返回data字段)進行返回
五、初始化空的ØMQ消息(zmq_msg_init)
int zmq_msg_init (zmq_msg_t *msg);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-init
- 功能:初始化空的ØMQ消息
- 參數:要初始化的ØMQ消息結構
- 返回值:該函數總是返回0,沒有錯誤定義
- 相關描述:
- zmq_msg_init()函數將初始化msg引用的消息對象,以表示一條空消息。在使用zmq_msg_recv()接收消息之前調用此函數最有用
- 永遠不要直接訪問zmq_msg_t成員,而是始終使用zmq_msg_xxx()系列函數
- 函數zmq_msg_init()、zmq_msg_init_data()和zmq_msg_init_size()是互斥的。調用這三者之一即可,不要初始化相同的zmq_msg_t兩次
- zmq_msg_init()的zmq_msg_t對象,其大小爲0,因此不能用在類似發送的函數中,但是可以用來接收(詳情參閱上面的“接口使用的幾點說明”)
演示案例
- 從套接字接收消息
zmq_msg_t msg; rc = zmq_msg_init(&msg); assert(rc == 0); int nbytes = zmq_msg_recv(socket, &msg, 0); assert(nbytes != -1);
六、初始化指定大小的ØMQ消息(zmq_msg_init_size)
int zmq_msg_init_size (zmq_msg_t *msg, size_t size);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-init-size
- 功能:初始化指定大小的ØMQ消息
- 參數:
- msg:要初始化的ØMQ消息結構
- size:初始化ØMQ消息結構的大小
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- ENOMEM:可用的存儲空間不足
- 相關描述:
- zmq_msg_init_size()函數將分配存儲消息大小字節所需的任何資源,並初始化msg引用的消息對象來表示新分配的消息
- 實現應該選擇將消息內容存儲在堆棧(小消息)還是堆(大消息)上。出於性能原因,zmq_msg_init_size()不得清除消息數據
- 永遠不要直接訪問zmq_msg_t成員,而是始終使用zmq_msg_xxx()系列函數
- 函數zmq_msg_init()、zmq_msg_init_data()和zmq_msg_init_size()是互斥的。調用這三者之一即可,不要初始化相同的zmq_msg_t兩次
七、從緩衝區中初始化ØMQ消息(zmq_msg_init_data)
typedef void (zmq_free_fn) (void *data, void *hint);
int zmq_msg_init_data (zmq_msg_t *msg, void *data, size_t size, zmq_free_fn *ffn, void *hint);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-init-data
- 功能:從提供的緩衝區初始化ØMQ消息
- 參數:
- msg:要初始化的ØMQ消息結構
- data:緩衝區的數據,是用來初始化msg的
- size:參數data緩衝區數據對應的大小
- ffn:如果data是動態申請的,那麼可以添加這個函數用來回收內存,其參數1data就是zmq_msg_init_data()函數的參數1,參數2hint是zmq_msg_init_data()函數的參數4
- hint:傳遞給ffn函數的參數2
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- ENOMEM:可用的存儲空間不足
- 相關描述:
- zmq_msg_init_data()函數將初始化msg引用的消息對象。不得進行復制任何數據,ØMQ應擁有所提供緩衝區的所有權
- 如果提供釋放功能(參數4),則一旦ØMQ不再需要緩衝區中的數據,就應該調用釋放函數ffn釋放內存
- 釋放函數ffn需要是線程安全的,因爲它將從任意線程調用。如果沒有提供釋放函數,則分配的內存將不會被釋放,這可能會導致內存泄漏
- 永遠不要直接訪問zmq_msg_t成員,而是始終使用zmq_msg_xxx()系列函數
- 函數zmq_msg_init()、zmq_msg_init_data()和zmq_msg_init_size()是互斥的。調用這三者之一即可,不要初始化相同的zmq_msg_t兩次
- ØMQ使用zmq_msg_init_data()來實現零拷貝,可參閱:https://blog.csdn.net/qq_41453285/article/details/106845900
演示案例
- 從提供的緩衝區初始化消息
//釋放函數 void my_free (void *data, void *hint) { free (data); } /* ... */ //申請數據 void *data = malloc (6); assert (data); memcpy (data, "ABCDEF", 6); //用data初始化msg zmq_msg_t msg; rc = zmq_msg_init_data (&msg, data, 6, my_free, NULL); assert (rc == 0);
八、釋放ØMQ消息(zmq_msg_close)
int zmq_msg_close (zmq_msg_t *msg);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-close
- 功能:將消息的引用計數減1
- 參數:
- msg:要釋放的消息結構
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EFAULT:無效的信息
- 相關描述:
- 應用程序應該確保在不再需要消息時調用zmq_msg_close(),否則可能發生內存泄漏
- 該函數只是將zmq_msg_t對象所指數據的引用計數減1,並把自己的大小設置爲0而已。當一個zmq_msg_t對象調用zmq_msg_close()之後,如果其之前所指的數據還有其他zmq_msg_t對象引用,那麼該zmq_msg_t對象所指的數據不會真正的被釋放,只有數據的最後一個zmq_msg_t引用對象調用zmq_msg_close()時才真正的釋放內存(見下面演示案例①)
- 當一個zmq_msg_t對象調用zmq_msg_close()之後就不能再對這個zmq_msg_t對象進行操作了,如果操作會報錯(見下面演示案例②)。即使它所指的數據還有其它zmq_msg_t對象引用也不行
- 注意,在zmq_msg_send()成功之後,zmq_msg_send()會把zmq_msg_t對象的大小設置爲0(變爲0之後就標記這個zmq_msg_t對象不需要再去使用了),但是沒有關閉該對象,因此zmq_msg_send()之後需要關閉zmq_msg_t對象(更多詳細的細節見下面zmq_msg_send()函數的介紹和演示案例)
演示案例①
- 下面創建兩個消息msg1和msg2,其中msg2拷貝於msg1
#include <stdio.h> #include <string.h> #include <zmq.h> int main() { // 1.初始化第一個消息 printf("第一步: 初始化msg1:\n"); zmq_msg_t msg1; zmq_msg_init_size(&msg1, 6); memcpy(zmq_msg_data(&msg1), "Hello", 6); printf("\tmsg1: %s, size: %d\n\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); // 2.將msg1拷貝給msg2 printf("第二步:將msg1拷貝給msg2:\n"); zmq_msg_t msg2; zmq_msg_init_size(&msg2, 6); zmq_msg_copy(&msg2, &msg1); printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 3.關閉msg1 printf("第三步:關閉msg1:\n"); zmq_msg_close(&msg1); //printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); msg1已經關閉了不能再進行訪問了 printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 4.關閉msg2 printf("第四步:關閉msg2:\n"); zmq_msg_close(&msg2); //msg2已經關閉了不能再進行訪問了 //printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); return 0; }
- 編譯運行效果如下:
演示案例②
- 下面創建一個消息msg1,然後初始化msg1消息,最後把它釋放,釋放之後就不能再操作該zmq_msg_t對象了,如果操作就會報錯
#include <stdio.h> #include <string.h> #include <zmq.h> int main() { // 1.初始化第一個消息 printf("第一步: 初始化msg:\n"); zmq_msg_t msg; zmq_msg_init_size(&msg, 6); memcpy(zmq_msg_data(&msg), "Hello", 6); printf("\tmsg1: %s, size: %d\n\n", (char*)zmq_msg_data(&msg), (int)zmq_msg_size(&msg)); // 2.關閉msg1 printf("第二步:關閉msg:\n"); zmq_msg_close(&msg); // 3.msg已經關閉了, 再去訪問就會報錯 printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg), (int)zmq_msg_size(&msg)); return 0; }
九、設置/獲取消息屬性(zmq_msg_set、zmq_msg_get)
zmq_msg_set
int zmq_msg_set (zmq_msg_t *message, int property, int value);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-set
- 功能:設置消息屬性
- 參數:
- message:要設置的消息
- property:要設置的屬性
- value:屬性值
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EINVAL:請求的屬性是未知的
- 相關描述:當前zmq_msg_set()函數不能設置任何屬性
zmq_msg_get
int zmq_msg_get (zmq_msg_t *message, int property);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-get
- 功能:獲取消息屬性
- 參數:
- message:要獲取的消息
- property:要獲取的屬性
- 返回值:
- 成功:返回參數2所指定的屬性的值
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EINVAL:請求的屬性是未知的
- 可以獲取的屬性如下:
- ZMQ_MORE:指示消息後面有更多消息幀要跟隨
- ZMQ_SRCFD:返回從套接字讀取消息的文件描述符。這允許應用程序通過getpeername()檢索遠程端點。請注意,相應的套接字可能已經關閉,甚至可以重用。目前只針對TCP套接字實現
- ZMQ_SHARED:指示消息可以與該消息的另一個副本共享底層存儲
十、獲取消息元數據屬性(zmq_msg_gets)
const char *zmq_msg_gets (zmq_msg_t *message, const char *property);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-gets
- 功能:獲取消息元數據屬性
- 參數:
- message:要獲取的消息
- property:要獲取的屬性
- 返回值:
- 成功:返回屬性的字符串值。調用方不得修改或釋放返回的值,該值應歸消息所有。屬性和值的編碼應爲UTF8
- 失敗:返回NULL,並將errno設置爲以下定義的值之一:
- EINVAL:請求的屬性是未知的
- 相關描述:
- 該函數返回message的元數據,要獲取的元數據屬性爲參數2所指定的屬性,是一個字符串形式。參數2應該是以NULL結尾的UTF8編碼字符串
- 如https://rfc.zeromq.org/spec/37/中所指定的,在ZeroMQ連接握手期間,將基於每個連接定義元數據。應用程序可以使用zmq_setsockopt()設置ZMQ_METADATA設置元數據屬性。應用程序元數據屬性必須以X-爲前綴
- 另外,當底層傳輸可用時,Peer-Address屬性將返回由getnameinfo()返回的遠程端點的IP地址
- 這些屬性的名稱也在zmq.h中定義爲:ZMQ_MSG_PROPERTY_SOCKET_TYPE ZMQ_MSG_PROPERTY_ROUTING_ID、ZMQ_MSG_PROPERTY_PEER_ADDRESS。目前,這些定義僅作爲API草案提供
- 可以根據底層安全機制定義其他屬性,請參閱下面的ZAP身份驗證連接示例
- 除了應用程序元數據外,還可以使用該函數檢索以下ZMTP屬性:
Socket-Type
Routing-Id
# 注意:Identity是Routing-Id的一個不贊成使用的別名
演示案例
- 獲取消息的zap身份驗證用戶ID:
zmq_msg_t msg; zmq_msg_init (&msg); rc = zmq_msg_recv (&msg, dealer, 0); assert (rc != -1); const char *user_id = zmq_msg_gets (&msg, ZMQ_MSG_PROPERTY_USER_ID); zmq_msg_close (&msg);
十一、獲取指向消息內容的指針(zmq_msg_data)
void *zmq_msg_data (zmq_msg_t *msg);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-data
- 功能:獲取指向消息內容的指針,會將msg的內容以以指針的形式返回
- 參數:
- msg:要檢索的消息結構
- 返回值:
- 成功:返回一個指針,指向於msg的消息內容
- 失敗:沒有出錯的情況
十二、獲取消息內容大小(zmq_msg_size)
size_t zmq_msg_size (zmq_msg_t *msg);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-size
- 功能:獲取消息內容的大小,以字節爲單位
- 參數:
- msg:要檢索的消息結構
- 返回值:
- 成功:返回msg消息內容的大小(以字節爲單位)
- 失敗:沒有出錯的情況
十三、複製消息內容(zmq_msg_copy)
int zmq_msg_copy (zmq_msg_t *dest, zmq_msg_t *src);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-copy
- 功能:將一條消息的內容複製給另一條消息,此時兩條消息同指一塊緩衝區數據
- 參數:
- dest:目標消息結構
- src:源消息結構
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EFAULT:無效的信息
- 相關描述:
- zmq_msg_copy()函數將src所引用的消息對象複製到dest所引用的消息對象中,如果dest之前有內容,則將其釋放。在複製之前你必須初始化dest
- 引用計數:zmq_msg_copy()的實現並不是在內存中創建一份dest新實例,然後將src拷貝給dest,而是將dest指向於src所指的內容,因此,dest和src是共享底層緩衝區中的數據的
- 因此在複製之後要避免修改消息的內容,因爲可能有多者共享這一條消息,其他修改會導致其它消息結構使用時產生未定義的行爲
- 如果您需要的是一個實際的硬拷貝,那麼就不要使用該函數,可以使用zmq_msg_init_size()分配一個新消息,並使用memcpy()複製消息內容
- 關於zmq_msg_copy()的演示案例可以看上面“zmq_msg_close()”函數的演示案例①
演示案例
- 複製消息
//初始化msg zmq_msg_t msg; zmq_msg_init_size (&msg, 255); memcpy(zmq_msg_data(&msg), "Hello, World", 12); //將msg拷貝給copy, 此時copy與msg指向於同一塊數據 zmq_msg_t copy; zmq_msg_init(©); zmq_msg_copy(©, &msg); //... zmq_msg_close (©); //關閉copy, 此時msg的內容只有自己引用 zmq_msg_close (&msg); //再關閉msg, 此時msg指向的內容才真正釋放
十四、移動消息內容(zmq_msg_move)
int zmq_msg_move (zmq_msg_t *dest, zmq_msg_t *src);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-move
- 功能:將一條消息的內容移動給另一條消息
- 參數:
- dest:目標消息結構
- src:源消息結構
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EFAULT:無效的信息
- 相關描述:
- zmq_msg_move()函數將把src引用的消息對象的內容移動到dest引用的消息對象,在調用zmq_msg_move()後,src變成一個空消息,如果dest之前有內容,則將其釋放然後更改爲src的內容
- 一個zmq_msg_t對象在調用zmq_msg_move()之後只是將自己的數據拷貝給其它zmq_msg_t對象,並把自己的大小設置爲0。因此在移動之後其還可以訪問之前的數據,但是大小變爲0了(變爲0之後就標記這個zmq_msg_t對象不需要再去使用了),因此在一個zmq_msg_t對象調用zmq_msg_move()之後建議調用zmq_msg_close()關閉自己不要再使用了(見下面演示案例)
- 與msg_msg_copy()的不同:msg_msg_copy()是將一個zmq_msg_t對象的數據拷貝給其他zmq_msg_t對象,導致數據的引用計數加1;zmq_msg_move()是將一個zmq_msg_t對象的數據移動到另外一個zmq_msg_t對象上
演示案例
- 下面創建並初始化一個msg1對象,之後把msg1對象的內容移動給msg2對象
#include <stdio.h> #include <string.h> #include <zmq.h> int main() { // 1.初始化第一個消息 printf("第一步: 初始化msg1:\n"); zmq_msg_t msg1; zmq_msg_init_size(&msg1, 6); memcpy(zmq_msg_data(&msg1), "Hello", 6); printf("\tmsg1: %s, size: %d\n\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); // 2.將msg1內容移動到給msg2 printf("第二步:將msg1數據移動給msg2:\n"); zmq_msg_t msg2; zmq_msg_init_size(&msg2, 6); zmq_msg_move(&msg2, &msg1); //雖然msg1的內容進行移動了, 但是其還可以訪問數據, 只是大小變爲0了 printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 3.關閉msg1, 因爲msg1數據進行移動了, 因此建議關閉不要再去使用 printf("第三步:關閉msg1:\n"); zmq_msg_close(&msg1); //printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); //msg1已經關閉了, 不能再去操作了 printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 4.關閉msg2 printf("第四步:關閉msg2:\n"); zmq_msg_close(&msg2); //printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); //msg2已經關閉了不能再進行訪問了 return 0; }
十五、消息路由ID的設置/獲取(zmq_msg_set_routing_id、zmq_msg_routing_id)
設置路由ID(zmq_msg_set_routing_id)
int zmq_msg_set_routing_id (zmq_msg_t *message, uint32_t routing_id);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-set-routing-id
- 功能:在消息上設置路由ID屬性
- 參數:
- message:設置的消息結構
- routing_id:路由ID
- 返回值:
- 成功:返回0
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EINVAL:提供的routing_id爲零
- 相關描述:
- 函數的作用是:在消息參數所指向的消息上設置指定的routing_id
- routing_id必須大於零
- 要獲得有效的路由ID,您必須從ZMQ_SERVER套接字接收一條消息,並使用libzmq:zmq_msg_routing_id方法
- 路由id是臨時的
獲取路由ID(zmq_msg_routing_id)
uint32_t zmq_msg_routing_id (zmq_msg_t *message);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-routing-id
- 功能:獲取消息的路由ID(如果有)
- 參數:
- message:要獲取的消息結構
- 返回值:
- 沒有路由ID:返回0
- 否則:返回一個大於0的32位無符號整數
- 相關描述:
- 函數的作用是:返回消息的路由ID(如果有的話)
- 在從ZMQ_SERVER套接字接收的所有消息上設置路由ID
- 要向ZMQ_SERVER套接字發送消息,必須設置已連接的ZMQ_CLIENT對等點的路由ID
- 路由id是臨時的
演示案例
- 接收客戶端消息和路由ID
// 1.創建上下文 void *ctx = zmq_ctx_new (); assert (ctx); // 2.創建一個ZMQ_SERVER服務端, 並開啓服務 void *server = zmq_socket (ctx, ZMQ_SERVER); assert (server); int rc = zmq_bind (server, "tcp://127.0.0.1:8080"); assert (rc == 0); // 3.初始化一個消息結構 zmq_msg_t message; rc = zmq_msg_init (&message); assert (rc == 0); // 4.接收消息 rc = zmq_msg_recv (server, &message, 0); assert (rc != -1); // 5.接收之後, 獲取路由ID uint32_t routing_id = zmq_msg_routing_id (&message); assert (routing_id);
十六、發送消息(zmq_msg_send)
int zmq_msg_send (zmq_msg_t *msg, void *socket, int flags);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-send
- 功能:在指定的套接字上發送消息
- 相關描述:
- 該函數替換了zmq_sendmsg()函數,zmq_sendmsg()不推薦使用了,關於zmq_sendmsg()可以參閱:http://api.zeromq.org/master:zmq-sendmsg
- zmq_msg_send()函數將把消息發送給socket,並且把msg消息在socket的消息隊列中進行排隊
- 當把msg傳遞給該函數之後,msg所對應的zmq_msg_t結構就失效了,zmq_msg_send()會把zmq_msg_t對象的大小設置爲0(變爲0之後就標記這個zmq_msg_t對象不需要再去使用了),但是沒有關閉該對象,因此在zmq_msg_send()之後建議調用zmq_close()關閉msg參數對應的zmq_msg_t對象
- 根據上面的特性,如果你想重複使用zmq_msg_send()之前msg參數對應的數據,那麼可以在調用zmq_msg_send()之前使用zmq_msg_copy()拷貝msg參數,這樣就有多個zmq_msg_t對象引用msg對引用的數據
- 成功調用zmq_msg_send()並不表示消息已傳輸到網絡中,僅表名它已在套接字上排隊並且ØMQ承擔了對該消息的責任
- 參數:
- msg:要發送的消息
- socket:操作的套接字
- flags:一些標誌,如下所示:
- ZMQ_DONTWAIT:對於套接字類型(DEALER,PUSH),當沒有可用的對等點(或所有的對等點有完整的高水位標記)時阻塞,指定該操作應該在非阻塞模式下執行。如果消息不能在套接字上排隊,則zmq_msg_send()函數將失敗,errno設置爲EAGAIN
- ZMQ_SNDMORE:指定要發送的消息是多部分消息,並且後面還將有其他消息部分。詳情參閱下面“多部分消息”介紹和演示案例②
- 返回值:
- 成功:返回消息中的字節數(如果字節數大於MAX_INT,函數將返回MAX_INT)
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EAGAIN:zmq_msg_send()在非阻塞模式(設置了ZMQ_DONTWAIT)下發送消息,但無法發送消息(詳情見上面的ZMQ_DONTWAIT套接字選項)
- ENOTSUP:此套接字類型不支持zmq_msg_send()操作
- EINVAL:發送方試圖發送多部分數據,這是套接字類型不允許的
- EFSM:由於套接字不處於適當的狀態,目前無法在該套接字上執行zmq_msg_send()操作。如果套接字類型在多個狀態之間切換,比如ZMQ_REP,可能會發生此錯誤。有關更多信息,請參閱zmq_socket()的消息傳遞模式部分
- ETERM:與指定套接字關聯的ØMQ 上下文已終止(可以參閱zmq_ctx_destroy():https://blog.csdn.net/qq_41453285/article/details/105993260)
- ENOTSOCK:提供的套接字無效
- EINTR:在消息被髮送之前,一個信號的發送中斷了操作
- EFAULT:無效的消息
- EHOSTUNREACH:該消息無法路由
多部分消息
- 多部分消息在文章最初已經介紹過了,詳情見文章最開始即可,此處做一個簡單的介紹就可以了
- ØMQ消息由1或更多的消息部分。每個消息部分本身是一個獨立的zmq_msg_t
- 發送多部分消息的應用程序在發送每個消息部分(最後一個消息除外)時必須使用ZMQ_SNDMORE標誌(見下面演示案例②)
演示案例①
- 下面是一個客戶端程序,其創建一個msg1對象和一個msg2對象,然後把msg1的內容拷貝給msg2,然後再將msg1的數據發送給服務端(這裏想測一下zmq_msg_send()對參數1的影響)
#include <stdio.h> #include <string.h> #include <zmq.h> int main() { // 1.初始化上下文 void *context = zmq_ctx_new(); if(context == NULL) { printf("zmq_ctx_new error\n"); return -1; } // 2.創建套接字, 綁定地址 void *requester = zmq_socket(context, ZMQ_REQ); if(zmq_connect(requester, "tcp://localhost:5555") == -1) { printf("zmq_connect error\n"); return -1; } // 3.初始化msg1 printf("初始化第一條消息msg1:\n"); zmq_msg_t msg1; zmq_msg_init_size(&msg1, 6); memcpy(zmq_msg_data(&msg1), "Hello", 6); printf("\tmsg1: %s, size: %d\n\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); // 4.將msg1拷貝給msg2 printf("將msg1拷貝給msg2:\n"); zmq_msg_t msg2; zmq_msg_init_size(&msg2, 6); zmq_msg_copy(&msg2, &msg1); printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); //5.發送數據 printf("發送msg1:\n"); if(zmq_msg_send(&msg1, requester, 0) == -1) { printf("zmq_msg_send error\n"); return -1; } //可以看到zmq_msg_send()是把msg1的大小設置爲0(標誌其不能去使用了), 但是仍可以可以訪問msg1的數據 printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 6.關閉msg1, 因爲msg1的大小被設置爲0, 不再使用了 printf("關閉msg1:\n"); zmq_msg_close(&msg1); //printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); //msg1被關閉了, 不能再去使用了 printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 7. 關閉msg2 printf("關閉msg2:\n"); zmq_msg_close(&msg2); //printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); //msg2被關閉了, 不能再去使用了 // 8.關閉套接字, 釋放上下文 zmq_close(requester); zmq_ctx_term(context); return 0; }
- 運行結果如下所示:
- 左側就是我們上面這個程序,右側是服務端代碼(服務端代碼這裏就不給出了,想看的可以參閱https://github.com/dongyusheng/csdn-code/blob/master/ZeroMQ/hwserver.c)
- 可以看到在zmq_msg_send()之後,函數會把參數1的zmq_msg_t對象的大小設置爲0,但是沒有關閉(zmq_msg_close())該對象,因此我們還可以訪問該對象
- 但是在 zmq_msg_send()之後,建議手動關閉(zmq_msg_close())參數1的zmq_msg_t對象大小
演示案例②
- 下面發送多部分信息,我們同時像服務端發送了msg1和msg2兩個消息,備註:
#include <stdio.h> #include <string.h> #include <zmq.h> int main() { // 1.初始化上下文 void *context = zmq_ctx_new(); if(context == NULL) { printf("zmq_ctx_new error\n"); return -1; } // 2.創建套接字, 綁定地址 void *requester = zmq_socket(context, ZMQ_REQ); if(zmq_connect(requester, "tcp://localhost:5555") == -1) { printf("zmq_connect error\n"); return -1; } // 3.初始化msg1 printf("初始化第一條消息msg1:\n"); zmq_msg_t msg1; zmq_msg_init_size(&msg1, 6); memcpy(zmq_msg_data(&msg1), "Hello", 6); printf("\tmsg1: %s, size: %d\n\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); // 4.初始化msg2 printf("初始化第二消息msg2:\n"); zmq_msg_t msg2; zmq_msg_init_size(&msg2, 6); memcpy(zmq_msg_data(&msg2), "Hello", 6); printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); //5.將msg1和msg2原子地發送出去, 其中第一個send需要指明ZMQ_SNDMORE標記 printf("發送msg1和msg2:\n"); if(zmq_msg_send(&msg1, requester, ZMQ_SNDMORE) == -1) { printf("zmq_msg_send error\n"); return -1; } if(zmq_msg_send(&msg2, requester, 0) == -1) { printf("zmq_msg_send error\n"); return -1; } //可以看到zmq_msg_send()是把msg1和msg2的大小設置爲0(標誌其不能去使用了), 但是仍可以可以訪問它們的數據 printf("\tmsg1: %s, size: %d\n", (char*)zmq_msg_data(&msg1), (int)zmq_msg_size(&msg1)); printf("\tmsg2: %s, size: %d\n\n", (char*)zmq_msg_data(&msg2), (int)zmq_msg_size(&msg2)); // 6.關閉msg1和msg2, 它們都不再使用了 printf("關閉msg1和msg2:\n"); zmq_msg_close(&msg1); zmq_msg_close(&msg2); // 8.關閉套接字, 釋放上下文 zmq_close(requester); zmq_ctx_term(context); return 0; }
- 效果如下所示:
- 左側發送了兩條消息給服務端,右側服務端收到兩條消息
- 注意:服務端是通過調用兩次zmq_msg_recv()接收數據的,不是一次zmq_msg_recv(),見下面解析
- 服務端代碼解析:服務端核心代碼如下,先是接收客戶端數據,然後再向服務端發送數據。通過上圖我們知道,服務端打印了兩次“Received Hello”,所以服務端執行了兩次while(1),每次執行while循環的時候接收數據,然後發送數據,發送數據的時候由於客戶端沒有接收函數,所以其直接返回繼續執行下一次循環,因此上面打印了兩次“Received Hello”
十七、接收消息(zmq_msg_recv)
int zmq_msg_recv (zmq_msg_t *msg, void *socket, int flags);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-recv
- 功能:在指定的套接字上接收消息
- 相關描述:
- 該函數替換了zmq_recvmsg()函數,zmq_recvmsg()不推薦使用了,關於zmq_recvmsg()可以參閱:http://api.zeromq.org/master:zmq-recvmsg
- zmq_msg_recv()函數將從套接字參數引用的套接字接收消息部分,並將其存儲在msg參數引用的消息中
- 如果msg之前存儲有消息則會被正確的釋放
- 如果指定的套接字上沒有可用的消息部分,則zmq_msg_recv()函數將阻塞,直到滿足請求爲止
- 參數:
- msg:用來保存接收的數據
- socket:操作的套接字
- flags:一些標誌,如下所示:
- ZMQ_DONTWAIT:指定該操作在非阻塞模式下執行。如果指定的套接字上沒有可用的消息,則zmq_msg_recv()函數將失敗並將errno設置爲EAGAIN
- 返回值:
- 成功:返回接收的消息的字節數。注意,如果消息被截斷,該值可能會超過len參數的值
- 失敗:返回-1,並將errno設置爲以下定義的值之一:
- EAGAIN:zmq_msg_recv()函數在非阻塞模式(ZMQ_DONTWAIT)下運行,套接字上沒有數據可接收
- ENOTSUP:這個套接字類型不支持zmq_msg_recv()操作
- EFSM:由於套接字不處於適當的狀態,目前無法在該套接字上執行zmq_msg_recv()操作。如果套接字類型在多個狀態之間切換,比如ZMQ_REP,可能會發生此錯誤。有關更多信息,請參閱zmq_socket()的消息傳遞模式部分
- ETERM:與指定套接字關聯的ØMQ上下文已終止(可以參閱zmq_ctx_destroy():https://blog.csdn.net/qq_41453285/article/details/105993260)
- ENOTSOCK:提供的套接字無效
- EINTR:在消息被髮送之前,一個信號的發送中斷了操作
多部分消息
- 多部分消息在文章最初已經介紹過了,詳情見文章最開始即可,此處做一個簡單的介紹就可以了
- ØMQ消息由1或更多的消息部分。每個消息部分本身是一個獨立的zmq_msg_t
- 處理多部分消息的應用程序在調用zmq_msg_recv()之後,必須傳遞ZMQ_RCVMORE選項給zmq_getsockopt()來確定是否還有其他部分要接收
演示案例①
- 從套接字接收消息
// 初始化消息結構 zmq_msg_t msg; int rc = zmq_msg_init (&msg); assert (rc == 0); // 接收消息 rc = zmq_msg_recv (&msg, socket, 0); assert (rc != -1); // 釋放消息 zmq_msg_close (&msg);
演示案例②
- 接收多部分消息
int more; size_t more_size = sizeof (more); do { // 初始化消息 zmq_msg_t part; int rc = zmq_msg_init (&part); assert (rc == 0); // 接收消息 rc = zmq_msg_recv (&part, socket, 0); assert (rc != -1); //判斷是否還有消息要接收 rc = zmq_getsockopt (socket, ZMQ_RCVMORE, &more, &more_size); assert (rc == 0); // 關閉消息 zmq_msg_close (&part); } while (more);
十八、判斷是否還有很多的消息要接收(zmq_msg_more)
int zmq_msg_more(zmq_msg_t *message);
- API參考文檔:http://api.zeromq.org/master:zmq-msg-more
- 功能:查詢是否還有更多消息要接收
- 相關描述:
- zmq_msg_more()函數判斷message參數所指的消息是否是由多個部分組成的消息的一部分,以及是否有其他部分要接收
- 可以在zmq_msg_close()之後安全地調用此方法。該方法與帶有ZMQ_MORE參數的zmq_msg_get()相同
- 參數:
- message:要判斷的消息結構
- 返回值:
- 成功0:如果這是多部分消息的最後一部分,或者是單部分消息的唯一一部分,返回0
- 失敗1:如果這是多部分消息的最後一部分,或者是單部分消息的唯一一部分
演示案例
- 接收多部分信息
zmq_msg_t part; while (true) { // 初始化消息 int rc = zmq_msg_init (&part); assert (rc == 0); // 接收消息 rc = zmq_msg_recv (socket, &part, 0); assert (rc != -1); // 判斷是否還有更多消息要接收, 如果繼續循環接收 if (zmq_msg_more (&part)) fprintf (stderr, "more\n"); else { //否則退出循環 fprintf (stderr, "end\n"); break; } //關閉消息 zmq_msg_close (&part); }
十九、其他接口
- zmq_recvmsg():已經被zmq_msg_recv()取代了,不建議使用。參考文檔爲:http://api.zeromq.org/master:zmq-recvmsg
- zmq_sendmsg():已經被zmq_msg_send()取代了,不建議使用。參考文檔爲:http://api.zeromq.org/master:zmq-sendmsg
- zmq_send():與zmq_msg_send()接口的功能一樣,也是向套接字發送數據,不過其發送的數據(參數2)爲void*類型,不是zmq_msg_t類型。參考文檔爲:http://api.zeromq.org/master:zmq-send
- zmq_recv():與zmq_msg_recv()接口的功能一樣,也是從套接字接收數據,不過其接收的數據(參數2)爲void*類型,不是zmq_msg_t類型。參考文檔爲:http://api.zeromq.org/master:zmq-recv
- zmq_send_const():與zmq_send()一樣,也是向套接字發送數據,不過其是發送恆定內存消息,也就是參數2所指的消息緩衝區爲常量內存,因此不會以任何方式複製或釋放。參考文檔爲:http://api.zeromq.org/master:zmq-send-const