Nếu bạn cần được hỗ trợ về AIDL, hãy xem thêm FMQ với AIDL.
Cơ sở hạ tầng lệnh gọi quy trình từ xa (RPC) của HIDL sử dụng cơ chế Binder, tức là các lệnh gọi liên quan đến mức hao tổn, yêu cầu hoạt động hạt nhân và có thể kích hoạt hành động của bộ lập lịch biểu. Tuy nhiên, đối với trường hợp dữ liệu phải được chuyển giữa các quy trình có chi phí thấp hơn và không liên quan đến hạt nhân, Hàng đợi thông báo nhanh (FMQ) được sử dụng.
FMQ tạo hàng đợi thư với các thuộc tính mong muốn. Một
Đối tượng MQDescriptorSync
hoặc MQDescriptorUnsync
có thể là
được gửi qua lệnh gọi HIDL RPC và được quy trình nhận sử dụng để truy cập vào
hàng đợi tin nhắn.
Hàng đợi tin nhắn nhanh chỉ được hỗ trợ trong C++ và trên các thiết bị chạy Android 8.0 trở lên.
Các loại MessageQueue
Android hỗ trợ 2 loại hàng đợi (được gọi là phiên bản):
- Hàng đợi không đồng bộ được phép bị tràn và có thể có nhiều hàng đợi độc giả; mỗi độc giả phải đọc dữ liệu kịp thời, nếu không sẽ làm mất dữ liệu.
- Hàng đợi đã đồng bộ hoá không được phép bị tràn và chỉ có thể một độc giả.
Cả hai loại hàng đợi này đều không được phép chạy dưới mức (đọc từ một hàng đợi trống) không thành công) và chỉ có thể có một người viết.
Chưa đồng bộ hóa
Hàng đợi chưa đồng bộ chỉ có một người viết nhưng có thể có bất kỳ số lượng độc giả. Có một vị trí ghi cho hàng đợi; tuy nhiên, mỗi độc giả vẫn muốn theo dõi vị trí đọc độc lập của chính nó.
Ghi vào hàng đợi luôn thành công (không được kiểm tra xem có bị tràn hay không) miễn là chúng không lớn hơn dung lượng hàng đợi đã định cấu hình (ghi lớn hơn dung lượng dung lượng hàng đợi bị lỗi ngay lập tức). Vì mỗi độc giả có thể có một lượt đọc khác nhau thay vì chờ mọi độc giả đọc từng phần dữ liệu, dữ liệu được phép đưa ra khỏi hàng đợi bất cứ khi nào các lượt ghi mới cần khoảng trống.
Độc giả chịu trách nhiệm truy xuất dữ liệu trước khi dữ liệu đó kết thúc hàng đợi. Lần đọc cố gắng đọc nhiều dữ liệu hơn mức có sẵn thất bại ngay lập tức (nếu không chặn) hoặc chờ có đủ dữ liệu (nếu chặn). Lần đọc cố gắng đọc nhiều dữ liệu hơn dung lượng của hàng đợi luôn sẽ không thành công ngay lập tức.
Nếu độc giả không nắm bắt được thông tin của tác giả, để lượng dữ liệu được ghi nhưng chưa được độc giả đó đọc lớn hơn dung lượng của hàng đợi, thì lần đọc tiếp theo không trả về dữ liệu; thay vào đó, thao tác này sẽ đặt lại lượt đọc của độc giả để bằng với vị trí ghi gần đây nhất, sau đó trả về lỗi. Nếu dữ liệu có sẵn để đọc được kiểm tra sau khi tràn nhưng trước lần đọc tiếp theo, dữ liệu cho thấy nhiều dữ liệu cần đọc hơn dung lượng của hàng đợi, cho biết đã xảy ra tràn. (Nếu hàng đợi tràn giữa các lần kiểm tra dữ liệu hiện có và cố đọc dữ liệu đó, dấu hiệu duy nhất của tràn là đọc không thành công).
Những độc giả của Hàng đợi chưa đồng bộ hoá có thể không muốn đặt lại con trỏ đọc và ghi của hàng đợi. Vì vậy, khi tạo hàng đợi từ trình đọc phần mô tả nên sử dụng đối số "false" cho "resetPointers" .
Đã đồng bộ hoá
Một hàng đợi được đồng bộ hoá gồm một người viết và một trình đọc với một lượt ghi duy nhất và một vị trí đọc duy nhất. Không thể ghi nhiều dữ liệu hơn hàng đợi có không gian cho hoặc đọc nhiều dữ liệu hơn hàng đợi hiện đang lưu giữ. Tuỳ thuộc vào việc hàm ghi hoặc đọc chặn hay không tuần tự đã gọi, cố gắng vượt quá dung lượng có sẵn hoặc dữ liệu trả về lỗi ngay lập tức hoặc chặn cho đến khi thao tác mong muốn có thể được hoàn tất. Nỗ lực đọc hoặc ghi nhiều dữ liệu hơn dung lượng của hàng đợi sẽ luôn gặp lỗi ngay lập tức.
Thiết lập FMQ
Hàng đợi thông báo yêu cầu nhiều đối tượng MessageQueue
: từ một đối tượng đến
được ghi vào và một hoặc nhiều nơi để đọc. Không có
cấu hình của đối tượng được dùng để ghi hoặc đọc; điều này tùy thuộc vào
người dùng để đảm bảo rằng không có đối tượng nào được sử dụng cho cả việc đọc và ghi, rằng có
chỉ có tối đa một người viết và đối với các hàng đợi được đồng bộ hoá, sẽ có tối đa một người viết
người đọc.
Tạo đối tượng MessageQueue đầu tiên
Một hàng đợi tin nhắn sẽ được tạo và định cấu hình bằng một lệnh gọi:
#include <fmq/MessageQueue.h> using android::hardware::kSynchronizedReadWrite; using android::hardware::kUnsynchronizedWrite; using android::hardware::MQDescriptorSync; using android::hardware::MQDescriptorUnsync; using android::hardware::MessageQueue; .... // For a synchronized non-blocking FMQ mFmqSynchronized = new (std::nothrow) MessageQueue<uint16_t, kSynchronizedReadWrite> (kNumElementsInQueue); // For an unsynchronized FMQ that supports blocking mFmqUnsynchronizedBlocking = new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite> (kNumElementsInQueue, true /* enable blocking operations */);
- Trình khởi tạo
MessageQueue<T, flavor>(numElements)
sẽ tạo và khởi chạy một đối tượng hỗ trợ chức năng hàng đợi thông báo. - Trình khởi tạo
MessageQueue<T, flavor>(numElements, configureEventFlagWord)
sẽ tạo và khởi tạo một đối tượng hỗ trợ chức năng hàng đợi tin nhắn có chặn. flavor
có thể làkSynchronizedReadWrite
cho một hàng đợi được đồng bộ hoá hoặckUnsynchronizedWrite
cho hàng đợi chưa được đồng bộ hoá hàng đợi.uint16_t
(trong ví dụ này) có thể là bất kỳ Kiểu dữ liệu do HiDL xác định không liên quan đến vùng đệm lồng nhau (không cóstring
hoặcvec
các loại), tay cầm hoặc giao diện.kNumElementsInQueue
cho biết quy mô của hàng đợi theo số mục nhập; mã này xác định dung lượng vùng đệm bộ nhớ dùng chung được phân bổ cho hàng đợi.
Tạo đối tượng MessageQueue thứ hai
Phần thứ hai của hàng đợi tin nhắn được tạo bằng cách sử dụng
Đối tượng MQDescriptor
thu được từ mặt đầu tiên. Chiến lược phát hành đĩa đơn
Đối tượng MQDescriptor
được gửi qua lệnh gọi RPC HIDL hoặc AIDL đến quy trình này
giữ đầu thứ hai của hàng đợi tin nhắn. Chiến lược phát hành đĩa đơn
MQDescriptor
chứa thông tin về hàng đợi, bao gồm:
- Thông tin để ánh xạ vùng đệm và con trỏ ghi.
- Thông tin liên kết con trỏ đọc (nếu hàng đợi được đồng bộ hoá).
- Thông tin liên kết từ cờ sự kiện (nếu hàng đợi đang chặn).
- Loại đối tượng (
<T, flavor>
), bao gồm Kiểu dữ liệu do HiDL xác định của các phần tử hàng đợi và phiên bản hàng đợi (được đồng bộ hoá hoặc không đồng bộ hoá).
Bạn có thể dùng đối tượng MQDescriptor
để tạo
Đối tượng MessageQueue
:
MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)
Tham số resetPointers
cho biết có đặt lại chế độ đọc hay không
và ghi vị trí thành 0 trong khi tạo đối tượng MessageQueue
này.
Trong hàng đợi chưa đồng bộ hoá, vị trí đọc (được cục bộ cho mỗi
đối tượng MessageQueue
trong hàng đợi chưa đồng bộ hoá) luôn được đặt thành 0
trong quá trình tạo. Thông thường, MQDescriptor
được khởi tạo trong
tạo đối tượng hàng đợi thông báo đầu tiên. Để có thêm quyền kiểm soát đối với
bộ nhớ, bạn có thể thiết lập MQDescriptor
theo cách thủ công
(MQDescriptor
được định nghĩa trong
system/libhidl/base/include/hidl/MQDescriptor.h
)
thì hãy tạo từng đối tượng MessageQueue
như mô tả trong phần này.
Chặn hàng đợi và cờ sự kiện
Theo mặc định, hàng đợi không hỗ trợ chặn lượt đọc/ghi. Có hai loại chặn lệnh gọi đọc/ghi:
- Dạng ngắn, với 3 tham số (con trỏ dữ liệu, số lượng mục,
thời gian chờ). Hỗ trợ chặn từng thao tác đọc/ghi trên một thiết bị
hàng đợi. Khi sử dụng biểu mẫu này, hàng đợi sẽ xử lý cờ sự kiện và mặt nạ bit
nội bộ và đối tượng hàng đợi thông báo đầu tiên phải
sẽ được khởi động bằng tham số thứ hai là
true
. Ví dụ:// For an unsynchronized FMQ that supports blocking mFmqUnsynchronizedBlocking = new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite> (kNumElementsInQueue, true /* enable blocking operations */);
- Dạng dài, với 6 thông số (bao gồm cờ sự kiện và mặt nạ bit).
Hỗ trợ sử dụng đối tượng
EventFlag
dùng chung giữa nhiều hàng đợi và cho phép chỉ định mặt nạ bit thông báo sẽ được sử dụng. Trong trường hợp này, cờ sự kiện và mặt nạ bit phải được cung cấp cho mỗi lệnh gọi đọc và ghi.
Đối với định dạng dài, bạn có thể cung cấp EventFlag
một cách rõ ràng trong
mỗi lệnh gọi readBlocking()
và writeBlocking()
. Một trong số
hàng đợi có thể được khởi chạy bằng một cờ sự kiện nội bộ. Sau đó, cờ này phải
được trích xuất từ các đối tượng MessageQueue
của hàng đợi đó bằng cách sử dụng
getEventFlagWord()
và dùng để tạo EventFlag
các đối tượng trong mỗi quy trình để sử dụng với các FMQ khác. Ngoài ra,
Có thể khởi tạo đối tượng EventFlag
bằng bất kỳ đối tượng chia sẻ nào phù hợp
bộ nhớ.
Nói chung, mỗi hàng đợi chỉ nên sử dụng một thuộc tính không chặn, dạng ngắn chặn hoặc chặn video dài. Đó không phải là lỗi khi kết hợp chúng, nhưng hãy cẩn thận lập trình quảng cáo để có được kết quả mong muốn.
Đánh dấu kỷ niệm là chỉ có thể đọc
Theo mặc định, bộ nhớ dùng chung có quyền đọc và ghi. Đối với thiết bị chưa được đồng bộ hoá
hàng đợi (kUnsynchronizedWrite
), người viết có thể muốn xoá quyền ghi cho tất cả
số độc giả trước khi đưa ra đối tượng MQDescriptorUnsync
. Điều này đảm bảo rằng các giá trị khác
các quy trình không thể ghi vào hàng đợi.
Bạn nên làm việc này để ngăn chặn lỗi hoặc hành vi xấu trong
mà độc giả xử lý.
Nếu người viết muốn độc giả có thể đặt lại hàng đợi bất cứ khi nào họ sử dụng
MQDescriptorUnsync
để tạo phía đọc của hàng đợi, sau đó, hệ thống sẽ không đánh dấu kỷ niệm này
ở dạng chỉ đọc. Đây là hành vi mặc định của hàm khởi tạo "MessageQueue". Vì vậy, nếu đã có
người dùng hiện tại của hàng đợi này, mã của họ cần được thay đổi để tạo hàng đợi bằng
resetPointer=false
.
- Người ghi: gọi
ashmem_set_prot_region
bằng chỉ số mô tả tệpMQDescriptor
và khu vực được đặt thành chỉ đọc (PROT_READ
):int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
- Trình đọc: tạo hàng đợi thư bằng
resetPointer=false
( mặc định làtrue
):mFmq = new (std::nothrow) MessageQueue(mqDesc, false);
Sử dụng MessageQueue
API công khai của đối tượng MessageQueue
là:
size_t availableToWrite() // Space available (number of elements). size_t availableToRead() // Number of elements available. size_t getQuantumSize() // Size of type T in bytes. size_t getQuantumCount() // Number of items of type T that fit in the FMQ. bool isValid() // Whether the FMQ is configured correctly. const MQDescriptor<T, flavor>* getDesc() // Return info to send to other process. bool write(const T* data) // Write one T to FMQ; true if successful. bool write(const T* data, size_t count) // Write count T's; no partial writes. bool read(T* data); // read one T from FMQ; true if successful. bool read(T* data, size_t count); // Read count T's; no partial reads. bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0); bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0); // Allows multiple queues to share a single event flag word std::atomic<uint32_t>* getEventFlagWord(); bool writeBlocking(const T* data, size_t count, uint32_t readNotification, uint32_t writeNotification, int64_t timeOutNanos = 0, android::hardware::EventFlag* evFlag = nullptr); // Blocking write operation for count Ts. bool readBlocking(T* data, size_t count, uint32_t readNotification, uint32_t writeNotification, int64_t timeOutNanos = 0, android::hardware::EventFlag* evFlag = nullptr) // Blocking read operation for count Ts; //APIs to allow zero copy read/write operations bool beginWrite(size_t nMessages, MemTransaction* memTx) const; bool commitWrite(size_t nMessages); bool beginRead(size_t nMessages, MemTransaction* memTx) const; bool commitRead(size_t nMessages);
Bạn có thể sử dụng availableToWrite()
và availableToRead()
để xác định lượng dữ liệu có thể được chuyển trong một thao tác. Trong một
hàng đợi chưa đồng bộ hoá:
availableToWrite()
luôn trả về sức chứa của hàng đợi.- Mỗi độc giả có vị trí đọc riêng và thực hiện phép tính riêng về
availableToRead()
. - Theo quan điểm của một trình đọc chậm, hàng đợi được phép tràn;
điều này có thể khiến
availableToRead()
trả về một giá trị lớn hơn kích thước của hàng đợi. Lần đọc đầu tiên sau khi tràn không thành công và dẫn đến vị trí đọc của trình đọc đó đang được đặt bằng với con trỏ ghi hiện tại, liệu sự cố tràn có được báo cáo qua hay khôngavailableToRead()
.
Phương thức read()
và write()
trả về
true
nếu tất cả dữ liệu được yêu cầu có thể được (và đã) được chuyển đến/từ
hàng đợi. Các phương thức này không chặn; họ đều thành công (và quay lại quảng cáo)
true
) hoặc lỗi trả về (false
) ngay lập tức.
Các phương thức readBlocking()
và writeBlocking()
đang chờ
cho đến khi thao tác được yêu cầu có thể được hoàn tất hoặc cho đến khi chúng hết thời gian chờ (
Giá trị timeOutNanos
bằng 0 có nghĩa là không bao giờ hết thời gian chờ).
Thao tác chặn được triển khai bằng một từ cờ hiệu sự kiện. Theo mặc định,
mỗi hàng đợi tạo và sử dụng từ cờ riêng để hỗ trợ dạng ngắn
readBlocking()
và writeBlocking()
. Có thể
nhiều hàng đợi để chia sẻ một từ duy nhất, để quy trình có thể chờ khi ghi hoặc
đọc bất kỳ hàng đợi nào. Con trỏ trỏ tới từ cờ sự kiện của hàng đợi có thể là
có được bằng cách gọi getEventFlagWord()
và con trỏ đó (hoặc bất kỳ
con trỏ đến một vị trí bộ nhớ dùng chung phù hợp) có thể dùng để tạo một
đối tượng EventFlag
để truyền vào dạng dài của
readBlocking()
và writeBlocking()
cho một
hàng đợi. readNotification
và writeNotification
các tham số cho biết bit nào trong cờ sự kiện nên được dùng để báo hiệu các lần đọc và
ghi vào hàng đợi đó. readNotification
và
writeNotification
là mặt nạ bit 32 bit.
readBlocking()
chờ trên các bit writeNotification
;
nếu tham số đó là 0 thì cuộc gọi sẽ luôn không thành công. Nếu
Giá trị readNotification
bằng 0, lệnh gọi không thành công, nhưng
đọc thành công sẽ không đặt bất kỳ bit thông báo nào. Trong hàng đợi được đồng bộ hoá,
điều này có nghĩa là lệnh gọi writeBlocking()
tương ứng
không bao giờ đánh thức trừ phi bit được đặt ở nơi khác. Trong hàng đợi chưa đồng bộ hoá,
writeBlocking()
không đợi (vẫn nên dùng để đặt giá trị
ghi bit thông báo) và phù hợp với các lượt đọc không đặt bất kỳ
bit thông báo. Tương tự, writeblocking()
sẽ không thành công nếu
readNotification
là 0 và một lần ghi thành công sẽ đặt giá trị
writeNotification
bit.
Để đợi nhiều hàng đợi cùng một lúc, hãy sử dụng đối tượng EventFlag
wait()
để chờ một mặt nạ thông báo. Chiến lược phát hành đĩa đơn
Phương thức wait()
trả về một từ trạng thái có các bit gây ra lỗi
thiết lập chế độ đánh thức. Sau đó, thông tin này sẽ được dùng để xác minh rằng hàng đợi tương ứng đã
đủ không gian hoặc dữ liệu cho thao tác ghi/đọc mong muốn và thực hiện
không chặn write()
/read()
. Để nhận được tác vụ đăng
hãy sử dụng một lệnh gọi khác đến EventFlag
wake()
. Để biết định nghĩa của EventFlag
trừu tượng, đề cập đến
system/libfmq/include/fmq/EventFlag.h
.
Không thực hiện thao tác sao chép
Chiến lược phát hành đĩa đơn
read
/write
/readBlocking
/writeBlocking()
API lấy con trỏ đến vùng đệm đầu vào/đầu ra làm đối số và sử dụng
memcpy()
gọi nội bộ để sao chép dữ liệu giữa cùng một
Vùng đệm vòng FMQ. Để cải thiện hiệu suất, Android 8.0 trở lên bao gồm một bộ
Các API cung cấp quyền truy cập con trỏ trực tiếp vào vùng đệm vòng, loại bỏ
cần sử dụng lệnh gọi memcpy
.
Sử dụng các API công khai sau đây cho các hoạt động FMQ không sao chép:
bool beginWrite(size_t nMessages, MemTransaction* memTx) const; bool commitWrite(size_t nMessages); bool beginRead(size_t nMessages, MemTransaction* memTx) const; bool commitRead(size_t nMessages);
- Phương thức
beginWrite
cung cấp con trỏ cơ sở vào vòng FMQ vùng đệm. Sau khi ghi dữ liệu, hãy xác nhận dữ liệu bằngcommitWrite()
. Phương thứcbeginRead
/commitRead
hoạt động theo cách tương tự. - Phương thức
beginRead
/Write
sẽ làm phương thức nhập số lượng thông báo sẽ được đọc/ghi và trả về một giá trị boolean cho biết liệu giá trị đọc/ghi. Nếu có thể đọc hoặc ghi,memTx
cấu trúc được điền sẵn các con trỏ cơ sở có thể dùng cho con trỏ trực tiếp quyền truy cập vào bộ nhớ dùng chung trong vùng đệm đổ chuông. - Cấu trúc
MemRegion
chứa thông tin chi tiết về một khối bộ nhớ, bao gồm con trỏ cơ sở (địa chỉ cơ sở của khối bộ nhớ) và độ dài trong số phần tửT
(chiều dài của khối bộ nhớ theo HIDL được xác định loại hàng đợi tin nhắn). - Cấu trúc
MemTransaction
chứa haiMemRegion
cấu trúc,first
vàsecond
làm lượt đọc hoặc ghi vào vùng đệm vòng có thể yêu cầu bao bọc quanh đầu hàng đợi. Chiến dịch này có nghĩa là cần 2 con trỏ cơ sở để đọc/ghi dữ liệu vào FMQ vòng đệm.
Cách lấy địa chỉ cơ sở và độ dài từ cấu trúc MemRegion
:
T* getAddress(); // gets the base address size_t getLength(); // gets the length of the memory region in terms of T size_t getLengthInBytes(); // gets the length of the memory region in bytes
Để nhận thông tin tham chiếu đến MemRegion
đầu tiên và thứ hai trong một
Đối tượng MemTransaction
:
const MemRegion& getFirstRegion(); // get a reference to the first MemRegion const MemRegion& getSecondRegion(); // get a reference to the second MemRegion
Ví dụ về cách ghi vào FMQ bằng API không sao chép:
MessageQueueSync::MemTransaction tx; if (mQueue->beginRead(dataLen, &tx)) { auto first = tx.getFirstRegion(); auto second = tx.getSecondRegion(); foo(first.getAddress(), first.getLength()); // method that performs the data write foo(second.getAddress(), second.getLength()); // method that performs the data write if(commitWrite(dataLen) == false) { // report error } } else { // report error }
Các phương thức trợ giúp sau đây cũng là một phần của MemTransaction
:
T* getSlot(size_t idx);
Trả về một con trỏ tới ôidx
trongMemRegions
thuộcMemTransaction
này . Nếu đối tượngMemTransaction
đang biểu thị bộ nhớ để đọc/ghi N mục thuộc loại T, thì phạm vi hợp lệ củaidx
nằm trong khoảng từ 0 đến N-1.bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
GhinMessages
mục thuộc loại T vào vùng bộ nhớ mà đối tượng mô tả, bắt đầu từ chỉ mụcstartIdx
. Phương thức này sử dụngmemcpy()
và không dành cho trường hợp không có bản sao nào hoạt động. Nếu đối tượngMemTransaction
biểu thị bộ nhớ cho đọc/ghi N mục thuộc loại T, thì phạm vi hợp lệ củaidx
là từ 0 đến N-1.bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
Phương thức trợ giúp để đọcnMessages
mục thuộc loại T từ vùng bộ nhớ mà đối tượng mô tả bắt đầu từstartIdx
. Chiến dịch này phương thức sử dụngmemcpy()
và không dùng cho bản sao không hoạt động.
Gửi hàng đợi qua HIDL
Về phía sáng tạo:
- Tạo đối tượng hàng đợi thông báo như mô tả ở trên.
- Xác minh đối tượng hợp lệ bằng
isValid()
. - Nếu bạn đang đợi nhiều hàng đợi bằng cách chuyển một
EventFlag
sang dạng dàireadBlocking()
/writeBlocking()
, bạn có thể trích xuất con trỏ cờ sự kiện (sử dụnggetEventFlagWord()
) từ mộtMessageQueue
được khởi tạo để tạo cờ, và hãy sử dụng cờ đó để tạo đối tượngEventFlag
cần thiết. - Sử dụng phương thức
MessageQueue
getDesc()
để lấy description [mô tả]. - Trong tệp
.hal
, hãy cung cấp cho phương thức một tham số thuộc loạifmq_sync
hoặcfmq_unsync
trong đóT
là kiểu xác định HIDL phù hợp. Sử dụng hàm này để gửi đối tượng được trả vềgetDesc()
vào quy trình nhận.
Ở bên nhận:
- Sử dụng đối tượng mô tả để tạo đối tượng
MessageQueue
. Hãy sử dụng cùng một phiên bản hàng đợi và kiểu dữ liệu, nếu không mẫu sẽ không thể biên dịch. - Nếu bạn đã trích xuất cờ sự kiện, hãy trích xuất cờ đó từ cờ
Đối tượng
MessageQueue
trong quá trình nhận. - Dùng đối tượng
MessageQueue
để chuyển dữ liệu.