Index: misc/client.py
===================================================================
--- misc/client.py	(revision 7)
+++ misc/client.py	(revision 8)
@@ -39,4 +39,43 @@
 
     @staticmethod
+    def _parseGetSpec(words):
+        """Parse a data query specification from the given argument word lisst.
+
+        If there is some failure, it returns None.
+
+        Otherwise it returns a tuple of two items:
+        - the parsed data, which is a tuple of these items:
+          - the name of the dataref to query
+          - the type of the dataref to query (it is a valid type, checked by
+          the function)
+          - the length of the dataref to query
+          - the offset to query the dataref from
+
+        - the remainder of the argument list."""
+        if len(words)<2:
+            print >> sys.stderr, "Missing parameters"
+            return None
+
+        nextIndex = 2
+
+        name = words[0]
+        type = words[1]
+        if type not in CLI._types:
+            print >> sys.stderr, "Invalid type"
+            return None
+
+        length = None
+        offset = None
+        if type in ["ia", "fa", "ba", "s"]:
+            if len(words)<4:
+                print >> sys.stderr, "Missing parameters"
+                return None
+            length = int(words[2])
+            offset = int(words[3])
+            nextIndex = 4
+
+        return ((name, type, length, offset), words[nextIndex:])
+
+    @staticmethod
     def _packLength(x):
         """Pack the given integer as a variable-length value."""
@@ -60,4 +99,5 @@
 
         self._stream = stream
+        self._multiGets = {}
 
         self.use_rawinput = True
@@ -78,20 +118,9 @@
         """Handle the 'get' command"""
         words = self._splitArgs(args)
-        if len(words)<2:
-            print >> sys.stderr, "Missing parameters"
-            return False
-
-        name = words[0]
-
-        type = words[1]
-        if type not in self._types:
-            print >> sys.stderr, "Invalid type"
-            return False
-
-        length = None
-        offset = None
-        if len(words)>3:
-            length = int(words[2])
-            offset = int(words[3])
+        result = self._parseGetSpec(words)
+        if result is None:
+            return False
+
+        (name, type, length, offset) = result[0]
 
         self._writeU8(0x01)
@@ -107,5 +136,5 @@
             print self._readValue(type)
         else:
-            print "Error code:", result
+            print >> sys.stderr, "Error code:", result
 
     def do_set(self, args):
@@ -143,5 +172,87 @@
         result = self._readU8()
         if result!=0:
-            print "Error code:", result
+            print >> sys.stderr, "Error code:", result
+
+    def do_reg_multiget(self, args):
+        """Handle the 'reg_multiget' command"""
+        words = self._splitArgs(args)
+        specs = []
+        while words:
+            result = self._parseGetSpec(words)
+            if result is None:
+                return False
+            else:
+                specs.append(result[0])
+                words = result[1]
+
+        if not specs:
+            return False
+
+        self._writeU8(0x11)
+        self._writeU32(len(specs))
+        for (name, type, length, offset) in specs:
+            print name, type
+            self._writeString(name)
+            self._writeU8(self._types[type])
+            if length is not None:
+                self._writeS32(length)
+                self._writeS32(offset)
+        self._flush()
+
+        result = self._readU8()
+        if result==0:
+            id = self._readU32()
+            self._multiGets[id] = \
+                [type for (name, type, length, offset) in specs]
+            print "ID:", id
+        else:
+            print >> sys.stderr, "Error code:", result
+
+    def do_unreg_multiget(self, args):
+        """Handle the 'unreg_multiget' command"""
+        words = self._splitArgs(args)
+        if len(words)<1:
+            print >> sys.stderr, "Missing parameter"
+            return False
+
+        id = int(words[0])
+
+        self._writeU8(0x12)
+        self._writeU32(id)
+        self._flush()
+
+        result = self._readU8()
+        if result!=0:
+            print >> sys.stderr, "Error code:", result
+
+        if id in self._multiGets:
+            del self._multiGets[id]
+
+    def do_exec_multiget(self, args):
+        """Handle the 'unreg_multiget' command"""
+        words = self._splitArgs(args)
+        if len(words)<1:
+            print >> sys.stderr, "Missing parameter"
+            return False
+
+        id = int(words[0])
+        if id not in self._multiGets:
+            print >> sys.stderr, "Invalid ID"
+            return False
+
+        self._writeU8(0x13)
+        self._writeU32(id)
+        self._flush()
+
+        result = self._readU8()
+        if result==0:
+            for type in self._multiGets[id]:
+                value = self._readValue(type)
+                print value
+        elif result==0x02:
+            index = self._readU32()
+            print >> sys.stderr, "Invalid dataref at #%d" % (index,)
+        else:
+            print >> sys.stderr, "Error code:", result
 
     def _writeU8(self, x):
@@ -153,4 +264,8 @@
         self._stream.write(struct.pack("i", x))
 
+    def _writeU32(self, x):
+        """Write the given value as an unsigned, 32-bit value."""
+        self._stream.write(struct.pack("I", x))
+
     def _writeFloat(self, x):
         """Write the given value as a single-precision floating point."""
@@ -181,4 +296,9 @@
         """Read a signed, 32-bit value from the stream."""
         (value,) = struct.unpack("i", self._stream.read(4))
+        return value
+
+    def _readU32(self):
+        """Read an unsigned, 32-bit value from the stream."""
+        (value,) = struct.unpack("I", self._stream.read(4))
         return value
 
Index: misc/test3.sh
===================================================================
--- misc/test3.sh	(revision 8)
+++ misc/test3.sh	(revision 8)
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+scriptdir=`dirname $0`
+
+"${scriptdir}/client.py" <<EOF
+reg_multiget sim/aircraft/engine/acf_num_engines i sim/aircraft/engine/acf_auto_featherEQ i sim/aircraft/engine/acf_spooltime_jet f sim/aircraft/engine/acf_spooltime_prop f sim/flightmodel/position/latitude d sim/flightmodel/position/longitude d sim/aircraft/view/acf_descrip s -1 0 sim/aircraft/prop/acf_en_type ia -1 0 sim/aircraft/prop/acf_prop_dir fa -1 0
+exec_multiget 2
+exec_multiget 1
+unreg_multiget 2
+unreg_multiget 1
+exec_multiget 1
+EOF
Index: src/xplra/GetDataRefTask.cc
===================================================================
--- src/xplra/GetDataRefTask.cc	(revision 7)
+++ src/xplra/GetDataRefTask.cc	(revision 8)
@@ -42,4 +42,5 @@
 
 using xplcommon::DataStream;
+using xplcommon::Util;
 
 using std::string;
@@ -54,4 +55,7 @@
     uint8_t type = stream.readU8();
     if (!stream) return 0;
+
+    // Util::debug("hu.varadiistvan.xplra.GetDataRefTask::execute: name='%s', type=0x%02x\n",
+    //             name.c_str(), type);
 
     if (type==Protocol::TYPE_INT) {
Index: src/xplra/GetMultiDataRefRequest.cc
===================================================================
--- src/xplra/GetMultiDataRefRequest.cc	(revision 8)
+++ src/xplra/GetMultiDataRefRequest.cc	(revision 8)
@@ -0,0 +1,87 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+//------------------------------------------------------------------------------
+
+#include "GetMultiDataRefRequest.h"
+
+#include "Protocol.h"
+
+#include <xplcommon/Util.h>
+
+//------------------------------------------------------------------------------
+
+using xplra::GetMultiDataRefRequest;
+
+using xplcommon::Util;
+
+//------------------------------------------------------------------------------
+
+GetMultiDataRefRequest::GetMultiDataRefRequest(uint8_t& result, size_t numTasks,
+                                               xplcommon::DataStream& stream)
+{
+    result = Protocol::RESULT_OK;
+    for(size_t i = 0;
+        i<numTasks && result==Protocol::RESULT_OK && stream; ++i)
+    {
+        GetDataRefTask* task = GetDataRefTask::create(result, stream);
+        if (task!=0) addTask(task);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void GetMultiDataRefRequest::writeResult(xplcommon::DataStream& stream) const
+{
+    // Util::debug("hu.varadiistvan.xplra.GetMultiDataRefRequest[%p]::writeResult\n", this);
+    size_t numTasks = getNumTasks();
+    for(size_t i = 0; i<numTasks; ++i) {
+        GetDataRefTask* task = getTask(i);
+        // Util::debug("hu.varadiistvan.xplra.GetMultiDataRefRequest[%p]::writeResult: task[%zu]: %d @ %p\n",
+        //             this, i, task->isValid(), task);
+        if (!task->isValid()) {
+            stream.writeU8(Protocol::RESULT_UNKNOWN_DATAREF);
+            stream.writeU32(i);
+            return;
+        }
+    }
+
+    stream.writeU8(Protocol::RESULT_OK);
+    for(size_t i = 0; i<numTasks; ++i) {
+        GetDataRefTask* task = getTask(i);
+        task->writeValue(stream);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/xplra/GetMultiDataRefRequest.h
===================================================================
--- src/xplra/GetMultiDataRefRequest.h	(revision 8)
+++ src/xplra/GetMultiDataRefRequest.h	(revision 8)
@@ -0,0 +1,79 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+#ifndef XPLRA_GETMULTIDATAREFREQUEST_H
+#define XPLRA_GETMULTIDATAREFREQUEST_H
+//------------------------------------------------------------------------------
+
+#include "MultiTaskRequest.h"
+#include "GetDataRefTask.h"
+
+//------------------------------------------------------------------------------
+
+namespace xplra {
+
+//------------------------------------------------------------------------------
+
+/**
+ * A request containing several dataref query tasks.
+ */
+class GetMultiDataRefRequest : public MultiTaskRequestTemplate<GetDataRefTask>
+{
+public:
+    /**
+     * Construct the request by reading the given number of task
+     * specifications from the given stream.
+     */
+    GetMultiDataRefRequest(uint8_t& result, size_t numTasks,
+                           xplcommon::DataStream& stream);
+
+    /**
+     * Write the result of an execution.
+     *
+     * If there is at least one invalid dataref, the result will be
+     * RESULT_UNKNOWN_DATAREF followed by a 32-bit unsigned value with
+     * the index of the first unknown dataref.
+     *
+     * Otherwise the result is RESULT_OK, followed by the values of
+     * the datarefs queried.
+     */
+    void writeResult(xplcommon::DataStream& stream) const;
+};
+
+//------------------------------------------------------------------------------
+
+} /* namespace xplra */
+
+//------------------------------------------------------------------------------
+#endif // XPLRA_GETMULTIDATAREFREQUEST_H
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/xplra/Makefile.am
===================================================================
--- src/xplra/Makefile.am	(revision 7)
+++ src/xplra/Makefile.am	(revision 8)
@@ -24,24 +24,28 @@
 
 libxplra_la_SOURCES= \
-	plugin.cc		\
-	ListenThread.cc		\
-	RequestQueue.cc		\
-	ServerThread.cc		\
-	TaskRequest.cc		\
-	DataRefTask.cc		\
-	GetDataRefTask.cc	\
-	SetDataRefTask.cc
+	plugin.cc			\
+	ListenThread.cc			\
+	RequestQueue.cc			\
+	ServerThread.cc			\
+	TaskRequest.cc			\
+	DataRefTask.cc			\
+	GetDataRefTask.cc		\
+	SetDataRefTask.cc		\
+	MultiTaskRequest.cc		\
+	GetMultiDataRefRequest.cc
 
 libxplra_la_LIBADD=$(LIBXPLCOMMON_LIBS)
 
 noinst_HEADERS= \
-	ListenThread.h		\
-	Request.h		\
-	RequestQueue.h		\
-	Protocol.h		\
-	ServerThread.h		\
-	Task.h			\
-	TaskRequest.h		\
-	DataRefTask.h		\
-	GetDataRefTask.h	\
-	SetDataRefTask.h
+	ListenThread.h			\
+	Request.h			\
+	RequestQueue.h			\
+	Protocol.h			\
+	ServerThread.h			\
+	Task.h				\
+	TaskRequest.h			\
+	DataRefTask.h			\
+	GetDataRefTask.h		\
+	SetDataRefTask.h		\
+	MultiTaskRequest.h		\
+	GetMultiDataRefRequest.h
Index: src/xplra/MultiTaskRequest.cc
===================================================================
--- src/xplra/MultiTaskRequest.cc	(revision 8)
+++ src/xplra/MultiTaskRequest.cc	(revision 8)
@@ -0,0 +1,70 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+//------------------------------------------------------------------------------
+
+#include "MultiTaskRequest.h"
+
+#include "Task.h"
+
+#include <xplcommon/Util.h>
+
+//------------------------------------------------------------------------------
+
+using xplra::MultiTaskRequest;
+
+using xplcommon::Util;
+
+//------------------------------------------------------------------------------
+
+MultiTaskRequest::~MultiTaskRequest()
+{
+    for(tasks_t::iterator i = tasks.begin(); i!=tasks.end(); ++i) {
+        delete *i;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void MultiTaskRequest::execute()
+{
+    // Util::debug("hu.varadiistvan.xplra.MultiTaskRequest[%p]::execute\n", this);
+    for(tasks_t::iterator i = tasks.begin(); i!=tasks.end(); ++i) {
+        // Util::debug("hu.varadiistvan.xplra.MultiTaskRequest[%p]::execute: task=%p\n",
+        //             this, *i);
+        (*i)->perform();
+    }
+}
+
+//------------------------------------------------------------------------------
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/xplra/MultiTaskRequest.h
===================================================================
--- src/xplra/MultiTaskRequest.h	(revision 8)
+++ src/xplra/MultiTaskRequest.h	(revision 8)
@@ -0,0 +1,170 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+#ifndef XPLRA_MULTITASKREQUEST_H
+#define XPLRA_MULTITASKREQUEST_H
+//------------------------------------------------------------------------------
+
+#include "Request.h"
+
+#include <vector>
+
+#include <cstdlib>
+
+//------------------------------------------------------------------------------
+
+namespace xplra {
+
+//------------------------------------------------------------------------------
+
+class Task;
+
+//------------------------------------------------------------------------------
+
+/**
+ * A request containing several tasks which it performs one-by-one
+ * when executing the request. It owns the tasks, which get deleted
+ * when the request object is deleted.
+ */
+class MultiTaskRequest : public Request
+{
+private:
+    /**
+     * Type for the vector of the tasks.
+     */
+    typedef std::vector<Task*> tasks_t;
+
+    /**
+     * The tasks.
+     */
+    tasks_t tasks;
+
+public:
+    /**
+     * Destroy the request by deleting the tasks.
+     */
+    virtual ~MultiTaskRequest();
+
+    /**
+     * Add a task to the request.
+     */
+    void addTask(Task* task);
+
+    /**
+     * Get the number of tasks.
+     */
+    size_t getNumTasks() const;
+
+    /**
+     * Get the task at the given index.
+     */
+    Task* getTask(size_t index) const;
+
+protected:
+    /**
+     * Perform the tasks one-by-one.
+     */
+    virtual void execute();
+};
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+/**
+ * A templated version of MultiTaskRequest, which accepts and returns
+ * tasks of a given type only.
+ */
+template <class TaskClass>
+class MultiTaskRequestTemplate : public MultiTaskRequest
+{
+public:
+    /**
+     * Add the given task.
+     */
+    void addTask(TaskClass* task);
+
+    /**
+     * Get the task at the given index.
+     */
+    TaskClass* getTask(size_t index) const;
+};
+
+//------------------------------------------------------------------------------
+// Inline definitions
+//------------------------------------------------------------------------------
+
+inline void MultiTaskRequest::addTask(Task* task)
+{
+    tasks.push_back(task);
+}
+
+//------------------------------------------------------------------------------
+
+inline size_t MultiTaskRequest::getNumTasks() const
+{
+    return tasks.size();
+}
+
+//------------------------------------------------------------------------------
+
+inline Task* MultiTaskRequest::getTask(size_t index) const
+{
+    return tasks[index];
+}
+
+//------------------------------------------------------------------------------
+// Template definitions
+//------------------------------------------------------------------------------
+
+template <class TaskClass>
+inline void MultiTaskRequestTemplate<TaskClass>::addTask(TaskClass* task)
+{
+    MultiTaskRequest::addTask(task);
+}
+
+//------------------------------------------------------------------------------
+
+template <class TaskClass>
+inline TaskClass*
+MultiTaskRequestTemplate<TaskClass>::getTask(size_t index) const
+{
+    return static_cast<TaskClass*>(MultiTaskRequest::getTask(index));
+}
+
+//------------------------------------------------------------------------------
+
+} /* namespace xplra */
+
+//------------------------------------------------------------------------------
+#endif // XPLRA_MULTITASKREQUEST_H
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/xplra/Protocol.h
===================================================================
--- src/xplra/Protocol.h	(revision 7)
+++ src/xplra/Protocol.h	(revision 8)
@@ -50,4 +50,19 @@
      */
     static const uint8_t COMMAND_SET_SINGLE = 0x02;
+
+    /**
+     * Command: register a multiple-data query request
+     */
+    static const uint8_t COMMAND_REGISTER_GET_MULTI = 0x11;
+
+    /**
+     * Command: unregister a multiple-data query request
+     */
+    static const uint8_t COMMAND_UNREGISTER_GET_MULTI = 0x12;
+
+    /**
+     * Command: execute a registered multiple-data query request
+     */
+    static const uint8_t COMMAND_EXECUTE_GET_MULTI = 0x13;
 
     /**
@@ -112,4 +127,14 @@
 
     /**
+     * Result code: invalid count
+     */
+    static const uint8_t RESULT_INVALID_COUNT = 0x06;
+
+    /**
+     * Result code: invalid ID
+     */
+    static const uint8_t RESULT_INVALID_ID = 0x07;
+
+    /**
      * Result code: other error
      */
@@ -120,4 +145,10 @@
      */
     static const int MAX_LENGTH = 2048;
+
+    /**
+     * The maximal count of requests in a multiple-data query or
+     * update.
+     */
+    static const size_t MAX_MULTI_COUNT = 1024;
 };
 
Index: src/xplra/Request.h
===================================================================
--- src/xplra/Request.h	(revision 2)
+++ src/xplra/Request.h	(revision 8)
@@ -58,4 +58,5 @@
     virtual ~Request();
 
+protected:
     /**
      * Execute the request. Make this very fast!
Index: src/xplra/ServerThread.cc
===================================================================
--- src/xplra/ServerThread.cc	(revision 7)
+++ src/xplra/ServerThread.cc	(revision 8)
@@ -36,4 +36,5 @@
 #include "SetDataRefTask.h"
 #include "TaskRequest.h"
+#include "GetMultiDataRefRequest.h"
 
 #include <xplcommon/Util.h>
@@ -74,5 +75,6 @@
     requestQueue(requestQueue),
     bufferedStream(acceptor.getSocket(&waiter)),
-    stream(*bufferedStream)
+    stream(*bufferedStream),
+    nextGetMultiRequestID(1)
 {
     instancesMutex.lock();
@@ -86,4 +88,10 @@
 {
     delete bufferedStream;
+
+    for(getMultiRequests_t::iterator i = getMultiRequests.begin();
+        i!=getMultiRequests.end(); ++i)
+    {
+        delete i->second;
+    }
 
     instancesMutex.lock();
@@ -115,4 +123,10 @@
         } else if (command==Protocol::COMMAND_SET_SINGLE) {
             if (!handleSetSingle()) break;
+        } else if (command==Protocol::COMMAND_REGISTER_GET_MULTI) {
+            if (!handleRegisterGetMulti()) break;
+        } else if (command==Protocol::COMMAND_UNREGISTER_GET_MULTI) {
+            if (!handleUnregisterGetMulti()) break;
+        } else if (command==Protocol::COMMAND_EXECUTE_GET_MULTI) {
+            if (!handleExecuteGetMulti()) break;
         } else {
             stream.writeU8(Protocol::RESULT_INVALID_COMMAND);
@@ -131,6 +145,7 @@
     GetDataRefTask* task = GetDataRefTask::create(result, stream);
 
-    if (!stream) return false;
-    else if (task==0) {
+    if (!stream) {
+        return false;
+    } else if (task==0) {
         stream.writeU8(result);
         return true;
@@ -157,6 +172,7 @@
     SetDataRefTask* task = SetDataRefTask::create(result, stream);
 
-    if (!stream) return false;
-    else if (task==0) {
+    if (!stream) {
+        return false;
+    } else if (task==0) {
         stream.writeU8(result);
         return true;
@@ -168,4 +184,73 @@
     stream.writeU8(task->isValid() ? Protocol::RESULT_OK :
                    Protocol::RESULT_UNKNOWN_DATAREF);
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ServerThread::handleRegisterGetMulti()
+{
+    uint32_t numTasks = stream.readU32();
+    if (!stream) {
+        return false;
+    } else if (numTasks==0 || numTasks>Protocol::MAX_MULTI_COUNT) {
+        stream.writeU8(Protocol::RESULT_INVALID_COUNT);
+        return true;
+    }
+
+    uint8_t result = Protocol::RESULT_OK;
+    GetMultiDataRefRequest* request =
+        new GetMultiDataRefRequest(result, numTasks, stream);
+    if (result!=Protocol::RESULT_OK || !stream) {
+        delete request;
+        stream.writeU8(result);
+        return stream;
+    }
+
+    size_t id = nextGetMultiRequestID++;
+    getMultiRequests[id] = request;
+
+    stream.writeU8(Protocol::RESULT_OK);
+    stream.writeU32(id);
+
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ServerThread::handleUnregisterGetMulti()
+{
+    uint32_t id = stream.readU32();
+    if (!stream) return false;
+
+    getMultiRequests_t::iterator i = getMultiRequests.find(id);
+    if (i==getMultiRequests.end()) {
+        stream.writeU8(Protocol::RESULT_INVALID_ID);
+    } else {
+        GetMultiDataRefRequest* request = i->second;
+        getMultiRequests.erase(i);
+        delete request;
+        stream.writeU8(Protocol::RESULT_OK);
+    }
+
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ServerThread::handleExecuteGetMulti()
+{
+    uint32_t id = stream.readU32();
+    if (!stream) return false;
+
+    getMultiRequests_t::iterator i = getMultiRequests.find(id);
+    if (i==getMultiRequests.end()) {
+        stream.writeU8(Protocol::RESULT_INVALID_ID);
+    } else {
+        GetMultiDataRefRequest* request = i->second;
+        if (!requestQueue.execute(request)) return false;
+        request->writeResult(stream);
+    }
+
     return true;
 }
Index: src/xplra/ServerThread.h
===================================================================
--- src/xplra/ServerThread.h	(revision 7)
+++ src/xplra/ServerThread.h	(revision 8)
@@ -41,4 +41,5 @@
 
 #include <set>
+#include <map>
 
 //------------------------------------------------------------------------------
@@ -49,4 +50,6 @@
 
 class RequestQueue;
+
+class GetMultiDataRefRequest;
 
 //------------------------------------------------------------------------------
@@ -62,4 +65,9 @@
      */
     typedef std::set<ServerThread*> instances_t;
+
+    /**
+     * Type for the registered multiple-data requests.
+     */
+    typedef std::map<size_t, GetMultiDataRefRequest*> getMultiRequests_t;
 
     /**
@@ -100,4 +108,14 @@
     xplcommon::DataStream stream;
 
+    /**
+     * The ID of the next multiple-data query request.
+     */
+    size_t nextGetMultiRequestID;
+
+    /**
+     * The registered multiple-data query requests.
+     */
+    getMultiRequests_t getMultiRequests;
+
 public:
     /**
@@ -137,4 +155,25 @@
      */
     bool handleSetSingle();
+
+    /**
+     * Handle the COMMAND_REGISTER_GET_MULTI command
+     *
+     * @return true, if we can continue, false if the thread should quit
+     */
+    bool handleRegisterGetMulti();
+
+    /**
+     * Handle the COMMAND_UNREGISTER_GET_MULTI command
+     *
+     * @return true, if we can continue, false if the thread should quit
+     */
+    bool handleUnregisterGetMulti();
+
+    /**
+     * Handle the COMMAND_EXECUTE_GET_MULTI command
+     *
+     * @return true, if we can continue, false if the thread should quit
+     */
+    bool handleExecuteGetMulti();
 };
 
