diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt
index f7f1baeafe34261a38033dd3d2db828df334ce25..c4edd2505b39320d39b25b7653667f7992567ccc 100644
--- a/src/language/modules/CMakeLists.txt
+++ b/src/language/modules/CMakeLists.txt
@@ -10,6 +10,7 @@ add_library(PugsLanguageModules
   MeshModule.cpp
   ModuleRepository.cpp
   SchemeModule.cpp
+  SocketModule.cpp
   UnaryOperatorRegisterForVh.cpp
   UtilsModule.cpp
   WriterModule.cpp
diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp
index 71a1368b9dc374b19562bae27cc14a5d2b54c233..85810cbfc3f12bba920ae2ee99002be73f0db39d 100644
--- a/src/language/modules/ModuleRepository.cpp
+++ b/src/language/modules/ModuleRepository.cpp
@@ -6,6 +6,7 @@
 #include <language/modules/MathModule.hpp>
 #include <language/modules/MeshModule.hpp>
 #include <language/modules/SchemeModule.hpp>
+#include <language/modules/SocketModule.hpp>
 #include <language/modules/UtilsModule.hpp>
 #include <language/modules/WriterModule.hpp>
 #include <language/utils/BasicAffectationRegistrerFor.hpp>
@@ -56,6 +57,7 @@ ModuleRepository::ModuleRepository()
   this->_subscribe(std::make_unique<MathModule>());
   this->_subscribe(std::make_unique<MeshModule>());
   this->_subscribe(std::make_unique<SchemeModule>());
+  this->_subscribe(std::make_unique<SocketModule>());
   this->_subscribe(std::make_unique<UtilsModule>());
   this->_subscribe(std::make_unique<WriterModule>());
 }
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index fcebda3dedc014d935a7e73a96785762f4cb00e1..f80201c7e424728db5663045e5b663c5e306e4d4 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -26,6 +26,7 @@
 #include <scheme/DiscreteFunctionUtils.hpp>
 #include <scheme/DiscreteFunctionVectorIntegrator.hpp>
 #include <scheme/DiscreteFunctionVectorInterpoler.hpp>
+#include <scheme/ExternalBoundaryConditionDescriptor.hpp>
 #include <scheme/FixedBoundaryConditionDescriptor.hpp>
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
 #include <scheme/FreeBoundaryConditionDescriptor.hpp>
@@ -36,6 +37,7 @@
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
 #include <scheme/NumberedBoundaryDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <utils/Socket.hpp>
 
 #include <memory>
 
@@ -273,6 +275,20 @@ SchemeModule::SchemeModule()
 
                               ));
 
+  this->_addBuiltinFunction("external_fsi_velocity",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<
+                              const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>,
+                                                                  const std::shared_ptr<const Socket>&)>>(
+
+                              [](std::shared_ptr<const IBoundaryDescriptor> boundary,
+                                 const std::shared_ptr<const Socket>& socket)
+                                -> std::shared_ptr<const IBoundaryConditionDescriptor> {
+                                return std::make_shared<ExternalBoundaryConditionDescriptor>("external_fsi_velocity",
+                                                                                             boundary, socket);
+                              }
+
+                              ));
+
   this->_addBuiltinFunction("glace_solver",
                             std::make_shared<BuiltinFunctionEmbedder<std::tuple<
                               std::shared_ptr<const IMesh>, std::shared_ptr<const IDiscreteFunction>,
diff --git a/src/language/modules/SocketModule.cpp b/src/language/modules/SocketModule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0540af2b60553c33f61ccaeeed049d9696f6ba5e
--- /dev/null
+++ b/src/language/modules/SocketModule.cpp
@@ -0,0 +1,291 @@
+#include <language/modules/SocketModule.hpp>
+
+#include <language/utils/BinaryOperatorProcessorBuilder.hpp>
+#include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/OStream.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <utils/Socket.hpp>
+
+SocketModule::SocketModule()
+{
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const Socket>>);
+
+  this->_addBuiltinFunction("createSocketServer",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const uint64_t&)>>(
+
+                              [](const uint64_t& port_number) -> std::shared_ptr<const Socket> {
+                                return std::make_shared<const Socket>(createServerSocket(port_number));
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("acceptSocketClient",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(std::shared_ptr<const Socket>)>>(
+
+                              [](std::shared_ptr<const Socket> server_socket) -> std::shared_ptr<const Socket> {
+                                return std::make_shared<const Socket>(acceptClientSocket(*server_socket));
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("connectSocketServer",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const std::string&,
+                                                                                                   const uint64_t&)>>(
+
+                              [](const std::string& hostname,
+                                 const uint64_t& port_number) -> std::shared_ptr<const Socket> {
+                                return std::make_shared<const Socket>(connectServerSocket(hostname, port_number));
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const bool&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const bool& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const uint64_t&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const uint64_t& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const int64_t&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const int64_t& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const double&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const double& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyVector<1>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyVector<1>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyVector<2>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyVector<2>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyVector<3>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyVector<3>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyMatrix<1>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyMatrix<1>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyMatrix<2>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyMatrix<2>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyMatrix<3>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyMatrix<3>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const std::string&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const std::string& value) -> void {
+                                write(*socket, value.size());
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_B",
+                            std::make_shared<BuiltinFunctionEmbedder<bool(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> bool {
+                                bool value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_N",
+                            std::make_shared<BuiltinFunctionEmbedder<uint64_t(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> uint64_t {
+                                uint64_t value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_Z",
+                            std::make_shared<BuiltinFunctionEmbedder<int64_t(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> int64_t {
+                                int64_t value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R",
+                            std::make_shared<BuiltinFunctionEmbedder<double(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> double {
+                                double value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R1", std::make_shared<
+                                         BuiltinFunctionEmbedder<TinyVector<1>(const std::shared_ptr<const Socket>&)>>(
+
+                                         [](const std::shared_ptr<const Socket>& socket) -> TinyVector<1> {
+                                           TinyVector<1> value;
+                                           read(*socket, value);
+
+                                           return value;
+                                         }
+
+                                         ));
+
+  this->_addBuiltinFunction("read_R2", std::make_shared<
+                                         BuiltinFunctionEmbedder<TinyVector<2>(const std::shared_ptr<const Socket>&)>>(
+
+                                         [](const std::shared_ptr<const Socket>& socket) -> TinyVector<2> {
+                                           TinyVector<2> value;
+                                           read(*socket, value);
+
+                                           return value;
+                                         }
+
+                                         ));
+
+  this->_addBuiltinFunction("read_R3", std::make_shared<
+                                         BuiltinFunctionEmbedder<TinyVector<3>(const std::shared_ptr<const Socket>&)>>(
+
+                                         [](const std::shared_ptr<const Socket>& socket) -> TinyVector<3> {
+                                           TinyVector<3> value;
+                                           read(*socket, value);
+
+                                           return value;
+                                         }
+
+                                         ));
+
+  this->_addBuiltinFunction("read_R1x1",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<TinyMatrix<1>(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<1> {
+                                TinyMatrix<1> value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R2x2",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<TinyMatrix<2>(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<2> {
+                                TinyMatrix<2> value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R3x3",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<TinyMatrix<3>(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<3> {
+                                TinyMatrix<3> value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this
+    ->_addBuiltinFunction("read_string",
+                          std::make_shared<BuiltinFunctionEmbedder<std::string(const std::shared_ptr<const Socket>&)>>(
+
+                            [](const std::shared_ptr<const Socket>& socket) -> std::string {
+                              size_t size;
+                              read(*socket, size);
+                              std::string value;
+                              if (size > 0) {
+                                value.resize(size);
+                                read(*socket, value);
+                              }
+                              return value;
+                            }
+
+                            ));
+}
+
+void
+SocketModule::registerOperators() const
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::shared_ptr<const Socket>>>());
+}
diff --git a/src/language/modules/SocketModule.hpp b/src/language/modules/SocketModule.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b32b64fafc72829e6598cfa78a95ce14f61ed9b
--- /dev/null
+++ b/src/language/modules/SocketModule.hpp
@@ -0,0 +1,28 @@
+#ifndef SOCKET_MODULE_HPP
+#define SOCKET_MODULE_HPP
+
+#include <language/modules/BuiltinModule.hpp>
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
+
+class Socket;
+
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const Socket>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("socket");
+
+class SocketModule : public BuiltinModule
+{
+ public:
+  std::string_view
+  name() const final
+  {
+    return "socket";
+  }
+
+  void registerOperators() const final;
+
+  SocketModule();
+  ~SocketModule() = default;
+};
+
+#endif   // SOCKET_MODULE_HPP
diff --git a/src/language/utils/OStream.hpp b/src/language/utils/OStream.hpp
index d9410e25e35d2a3e9d5ac147911f6464d3b71eb7..bef02277ef8e07be09cc5731b33c6f5c3363e37d 100644
--- a/src/language/utils/OStream.hpp
+++ b/src/language/utils/OStream.hpp
@@ -23,6 +23,17 @@ class OStream
     return os;
   }
 
+  template <typename DataT>
+  friend std::shared_ptr<const OStream>
+  operator<<(const std::shared_ptr<const OStream>& os, const std::shared_ptr<const DataT>& t)
+  {
+    Assert(os.use_count() > 0, "non allocated stream");
+    if (os->m_ostream != nullptr) {
+      *os->m_ostream << *t;
+    }
+    return os;
+  }
+
   OStream(std::ostream& os) : m_ostream(&os) {}
   OStream() = default;
 
diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp
index e5171badb11fa509a020025ad38e7e7bbd96281d..098b146e55efe52ffc7297b8ebb841206d9e3d43 100644
--- a/src/scheme/AcousticSolver.cpp
+++ b/src/scheme/AcousticSolver.cpp
@@ -8,11 +8,13 @@
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
+#include <scheme/ExternalBoundaryConditionDescriptor.hpp>
 #include <scheme/FixedBoundaryConditionDescriptor.hpp>
 #include <scheme/IBoundaryConditionDescriptor.hpp>
 #include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <utils/Socket.hpp>
 
 #include <variant>
 #include <vector>
@@ -75,9 +77,13 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   class PressureBoundaryCondition;
   class SymmetryBoundaryCondition;
   class VelocityBoundaryCondition;
+  class ExternalFSIVelocityBoundaryCondition;
 
-  using BoundaryCondition = std::
-    variant<FixedBoundaryCondition, PressureBoundaryCondition, SymmetryBoundaryCondition, VelocityBoundaryCondition>;
+  using BoundaryCondition = std::variant<FixedBoundaryCondition,
+                                         PressureBoundaryCondition,
+                                         SymmetryBoundaryCondition,
+                                         VelocityBoundaryCondition,
+                                         ExternalFSIVelocityBoundaryCondition>;
 
   using BoundaryConditionList = std::vector<BoundaryCondition>;
 
@@ -254,6 +260,18 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
         bc_list.emplace_back(FixedBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())));
         break;
       }
+      case IBoundaryConditionDescriptor::Type::external: {
+        if constexpr (Dimension == 1) {
+          const ExternalBoundaryConditionDescriptor& extern_bc_descriptor =
+            dynamic_cast<const ExternalBoundaryConditionDescriptor&>(*bc_descriptor);
+          bc_list.emplace_back(
+            ExternalFSIVelocityBoundaryCondition(mesh, getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()),
+                                                 extern_bc_descriptor.socket()));
+        } else {
+          throw NotImplementedError("external FSI velocity not implemented for dimension > 1");
+        }
+        break;
+      }
       case IBoundaryConditionDescriptor::Type::dirichlet: {
         const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
           dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
@@ -320,6 +338,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   void _applyPressureBC(const MeshType& mesh, NodeValue<Rd>& br);
   void _applySymmetryBC(NodeValue<Rdxd>& Ar, NodeValue<Rd>& br);
   void _applyVelocityBC(NodeValue<Rdxd>& Ar, NodeValue<Rd>& br);
+  void _applyExternalVelocityBC(const DiscreteScalarFunction& p, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br);
 
   void
   _applyBoundaryConditions(const MeshType& mesh, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br)
@@ -380,6 +399,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
     NodeValue<Rd> br   = this->_computeBr(mesh, Ajr, u, p);
 
     this->_applyBoundaryConditions(mesh, Ar, br);
+    this->_applyExternalVelocityBC(p, Ar, br);
 
     synchronize(Ar);
     synchronize(br);
@@ -610,6 +630,34 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applyVelocityBC(NodeValue<Rdx
   }
 }
 
+template <size_t Dimension>
+void
+AcousticSolverHandler::AcousticSolver<Dimension>::_applyExternalVelocityBC(const DiscreteScalarFunction& p,
+                                                                           NodeValue<Rdxd>& Ar,
+                                                                           NodeValue<Rd>& br)
+{
+  for (const auto& boundary_condition : m_boundary_condition_list) {
+    std::visit(
+      [&](auto&& bc) {
+        using T = std::decay_t<decltype(bc)>;
+        if constexpr (std::is_same_v<ExternalFSIVelocityBoundaryCondition, T>) {
+          const auto& node_list  = bc.nodeList();
+          const auto& value_list = bc.valueList(p);
+
+          parallel_for(
+            node_list.size(), PUGS_LAMBDA(size_t i_node) {
+              NodeId node_id    = node_list[i_node];
+              const auto& value = value_list[i_node];
+
+              Ar[node_id] = identity;
+              br[node_id] = value;
+            });
+        }
+      },
+      boundary_condition);
+  }
+}
+
 template <size_t Dimension>
 class AcousticSolverHandler::AcousticSolver<Dimension>::FixedBoundaryCondition
 {
@@ -685,6 +733,55 @@ class AcousticSolverHandler::AcousticSolver<1>::PressureBoundaryCondition
   ~PressureBoundaryCondition() = default;
 };
 
+template <size_t Dimension>
+class AcousticSolverHandler::AcousticSolver<Dimension>::ExternalFSIVelocityBoundaryCondition
+{
+ private:
+  const ItemToItemMatrix<ItemType::node, ItemType::cell> m_node_to_cell_matrix;
+  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const Array<TinyVector<Dimension>> m_value_list;
+  const std::shared_ptr<const Socket> m_socket;
+
+ public:
+  const Array<const NodeId>&
+  nodeList() const
+  {
+    return m_mesh_node_boundary.nodeList();
+  }
+
+  Array<const TinyVector<Dimension>>
+  valueList(const DiscreteScalarFunction& p) const
+  {
+    if (parallel::size() > 1) {
+      throw NotImplementedError("Parallelism is not supported");
+    }
+    if (m_value_list.size() != 1) {
+      throw NotImplementedError("Non connected boundaries are not supported");
+    }
+
+    const CellId cell_id = m_node_to_cell_matrix[m_mesh_node_boundary.nodeList()[0]][0];
+
+    write(*m_socket, p[cell_id]);
+    read(*m_socket, m_value_list[0]);
+    return m_value_list;
+  }
+
+  ExternalFSIVelocityBoundaryCondition(const Mesh<Connectivity<Dimension>>& mesh,
+                                       const MeshNodeBoundary<Dimension>& mesh_node_boundary,
+                                       const std::shared_ptr<const Socket>& socket)
+    : m_node_to_cell_matrix{mesh.connectivity().nodeToCellMatrix()},
+      m_mesh_node_boundary{mesh_node_boundary},
+      m_value_list(mesh_node_boundary.nodeList().size()),
+      m_socket{socket}
+  {
+    if constexpr (Dimension > 1) {
+      throw NotImplementedError("external FSI velocity bc in dimension > 1");
+    }
+  }
+
+  ~ExternalFSIVelocityBoundaryCondition() = default;
+};
+
 template <size_t Dimension>
 class AcousticSolverHandler::AcousticSolver<Dimension>::VelocityBoundaryCondition
 {
diff --git a/src/scheme/ExternalBoundaryConditionDescriptor.hpp b/src/scheme/ExternalBoundaryConditionDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..056592931b1ce1cf6801f7455d5e8ab3f785dc9f
--- /dev/null
+++ b/src/scheme/ExternalBoundaryConditionDescriptor.hpp
@@ -0,0 +1,62 @@
+#ifndef EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP
+#define EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP
+
+#include <mesh/IBoundaryDescriptor.hpp>
+#include <scheme/IBoundaryConditionDescriptor.hpp>
+#include <utils/Socket.hpp>
+
+#include <memory>
+
+class ExternalBoundaryConditionDescriptor : public IBoundaryConditionDescriptor
+{
+ private:
+  std::ostream&
+  _write(std::ostream& os) const final
+  {
+    os << "external(" << m_name << ',' << *m_boundary_descriptor << ")";
+    return os;
+  }
+
+  const std::string_view m_name;
+
+  std::shared_ptr<const IBoundaryDescriptor> m_boundary_descriptor;
+  const std::shared_ptr<const Socket> m_socket;
+
+ public:
+  std::string_view
+  name() const
+  {
+    return m_name;
+  }
+
+  const std::shared_ptr<const Socket>&
+  socket() const
+  {
+    return m_socket;
+  }
+
+  const IBoundaryDescriptor&
+  boundaryDescriptor() const final
+  {
+    return *m_boundary_descriptor;
+  }
+
+  Type
+  type() const final
+  {
+    return Type::external;
+  }
+
+  ExternalBoundaryConditionDescriptor(const std::string_view name,
+                                      std::shared_ptr<const IBoundaryDescriptor> boundary_descriptor,
+                                      const std::shared_ptr<const Socket>& socket)
+    : m_name{name}, m_boundary_descriptor(boundary_descriptor), m_socket{socket}
+  {}
+
+  ExternalBoundaryConditionDescriptor(const ExternalBoundaryConditionDescriptor&) = delete;
+  ExternalBoundaryConditionDescriptor(ExternalBoundaryConditionDescriptor&&)      = delete;
+
+  ~ExternalBoundaryConditionDescriptor() = default;
+};
+
+#endif   // EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP
diff --git a/src/scheme/IBoundaryConditionDescriptor.hpp b/src/scheme/IBoundaryConditionDescriptor.hpp
index c0ea161db5d5f869d8ac8517b85ae2385af1d94a..f99eff9355f6fdf6fbd65ad1898f3cba655af284 100644
--- a/src/scheme/IBoundaryConditionDescriptor.hpp
+++ b/src/scheme/IBoundaryConditionDescriptor.hpp
@@ -12,6 +12,7 @@ class IBoundaryConditionDescriptor
   {
     axis,
     dirichlet,
+    external,
     fourier,
     fixed,
     free,
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 19feb273c4a6e2ad09818f467caa9f0f79636b35..741b0d5104cef791cf045fd8cc76cd8b16db30a2 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -15,7 +15,8 @@ add_library(
   RandomEngine.cpp
   RevisionInfo.cpp
   SignalManager.cpp
-  SLEPcWrapper.cpp)
+  SLEPcWrapper.cpp
+  Socket.cpp)
 
 target_link_libraries(
   PugsUtils
diff --git a/src/utils/Messenger.hpp b/src/utils/Messenger.hpp
index 75ef1732a496e367917b55b86e4f2075570acd86..44772529e8539814eb30b36dad7b936e4c0269a6 100644
--- a/src/utils/Messenger.hpp
+++ b/src/utils/Messenger.hpp
@@ -19,8 +19,6 @@
 #include <utils/Exceptions.hpp>
 #include <utils/PugsTraits.hpp>
 
-#include <iostream>
-
 namespace parallel
 {
 class Messenger
diff --git a/src/utils/Socket.cpp b/src/utils/Socket.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3582e359be22a257b2b52109f7a3ed57b2944a1b
--- /dev/null
+++ b/src/utils/Socket.cpp
@@ -0,0 +1,220 @@
+#include <utils/Exceptions.hpp>
+#include <utils/Socket.hpp>
+
+#include <utils/Messenger.hpp>
+
+#include <arpa/inet.h>
+#include <cstring>
+#include <iostream>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdexcept>
+#include <sys/socket.h>
+#include <unistd.h>
+
+class Socket::Internals
+{
+ private:
+  int m_socket_fd;
+  sockaddr_in m_address;
+
+  const bool m_is_server_socket;
+
+ public:
+  friend Socket createServerSocket(int port_number);
+  friend Socket acceptClientSocket(const Socket& server);
+  friend Socket connectServerSocket(const std::string& server_name, int port_number);
+
+  friend std::ostream&
+  operator<<(std::ostream& os, const Socket::Internals& internals)
+  {
+    // This function's coverage is not performed since it's quite
+    // complex to create its various conditions
+    //
+    // LCOV_EXCL_START
+    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+    if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
+                      sizeof(hbuf), sbuf, sizeof(sbuf), NI_NAMEREQD) == 0) {
+      os << hbuf << ':' << sbuf;
+    } else if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
+                             sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0) {
+      if (std ::string{hbuf} == "0.0.0.0") {
+        if (::gethostname(hbuf, NI_MAXHOST) == 0) {
+          os << hbuf << ':' << sbuf;
+        } else {
+          os << "localhost:" << sbuf;
+        }
+      } else {
+        os << hbuf << ':' << sbuf;
+      }
+    } else {
+      os << "<unknown host>";
+    }
+    // LCOV_EXCL_STOP
+    return os;
+  }
+
+  bool
+  isServerSocket() const
+  {
+    return m_is_server_socket;
+  }
+
+  int
+  portNumber() const
+  {
+    return ::ntohs(m_address.sin_port);
+  }
+
+  int
+  fileDescriptor() const
+  {
+    return m_socket_fd;
+  }
+
+  Internals(const Internals&) = delete;
+  Internals(Internals&&)      = delete;
+
+  Internals(bool is_server_socket = false) : m_is_server_socket{is_server_socket}
+  {
+    if (parallel::size() > 1) {
+      // LCOV_EXCL_START
+      throw NotImplementedError("Sockets are not managed in parallel");
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  ~Internals()
+  {
+    close(m_socket_fd);
+  }
+};
+
+std::ostream&
+operator<<(std::ostream& os, const Socket& socket)
+{
+  return os << *socket.m_internals;
+}
+
+int
+Socket::portNumber() const
+{
+  return m_internals->portNumber();
+}
+
+Socket
+createServerSocket(int port_number)
+{
+  auto p_socket_internals             = std::make_shared<Socket::Internals>(true);
+  Socket::Internals& socket_internals = *p_socket_internals;
+
+  socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_internals.m_socket_fd < 0) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  socket_internals.m_address.sin_family      = AF_INET;
+  socket_internals.m_address.sin_addr.s_addr = INADDR_ANY;
+  socket_internals.m_address.sin_port        = htons(port_number);
+
+  int on = 1;
+  ::setsockopt(socket_internals.m_socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+  socklen_t length = sizeof(socket_internals.m_address);
+
+  if (::bind(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), length) < 0) {
+    throw NormalError(strerror(errno));
+  }
+
+  if (::getsockname(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), &length) ==
+      -1) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  ::listen(socket_internals.m_socket_fd, 1);
+
+  return Socket{p_socket_internals};
+}
+
+Socket
+acceptClientSocket(const Socket& server)
+{
+  auto p_socket_internals             = std::make_shared<Socket::Internals>();
+  Socket::Internals& socket_internals = *p_socket_internals;
+
+  socklen_t address_lenght     = sizeof(socket_internals.m_address);
+  socket_internals.m_socket_fd = ::accept(server.m_internals->m_socket_fd,
+                                          reinterpret_cast<sockaddr*>(&socket_internals.m_address), &address_lenght);
+
+  if (socket_internals.m_socket_fd < 0) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  return Socket{p_socket_internals};
+}
+
+Socket
+connectServerSocket(const std::string& server_name, int port_number)
+{
+  auto p_socket_internals             = std::make_shared<Socket::Internals>();
+  Socket::Internals& socket_internals = *p_socket_internals;
+
+  socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_internals.m_socket_fd < 0) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  hostent* server = ::gethostbyname(server_name.c_str());
+  if (server == NULL) {
+    throw NormalError(strerror(errno));
+  }
+
+  sockaddr_in& serv_addr = socket_internals.m_address;
+  ::memset(reinterpret_cast<char*>(&serv_addr), 0, sizeof(serv_addr));
+  serv_addr.sin_family = AF_INET;
+
+  ::memcpy(reinterpret_cast<char*>(&serv_addr.sin_addr.s_addr), reinterpret_cast<char*>(server->h_addr),
+           server->h_length);
+
+  serv_addr.sin_port = ::htons(port_number);
+
+  if (::connect(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(serv_addr))) {
+    throw NormalError(strerror(errno));
+  }
+
+  return Socket{p_socket_internals};
+}
+
+void
+Socket::_write(const char* data, const size_t lenght) const
+{
+  if (this->m_internals->isServerSocket()) {
+    throw NormalError("Server cannot write to server socket");
+  }
+
+  if (::write(this->m_internals->fileDescriptor(), data, lenght) < 0) {
+    // Quite complex to test
+    throw NormalError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+}
+
+void
+Socket::_read(char* data, const size_t length) const
+{
+  if (this->m_internals->isServerSocket()) {
+    throw NormalError("Server cannot read from server socket");
+  }
+
+  size_t received = 0;
+  while (received < length) {
+    int n = ::read(this->m_internals->fileDescriptor(), reinterpret_cast<char*>(data) + received, length - received);
+    if (n <= 0) {
+      throw NormalError("Could not read data");
+    }
+    received += n;
+  }
+}
diff --git a/src/utils/Socket.hpp b/src/utils/Socket.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6d25ddd1328d559e4786e677aff8cde9af06549
--- /dev/null
+++ b/src/utils/Socket.hpp
@@ -0,0 +1,86 @@
+#ifndef SOCKET_HPP
+#define SOCKET_HPP
+
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utils/PugsTraits.hpp>
+
+class Socket
+{
+ private:
+  class Internals;
+
+  std::shared_ptr<Internals> m_internals;
+
+  Socket(std::shared_ptr<Internals> internals) : m_internals{internals} {}
+
+  void _write(const char* data, const size_t lenght) const;
+  void _read(char* data, const size_t lenght) const;
+
+ public:
+  int portNumber() const;
+
+  friend std::ostream& operator<<(std::ostream& os, const Socket& s);
+
+  friend Socket createServerSocket(int port_number);
+  friend Socket acceptClientSocket(const Socket& server);
+  friend Socket connectServerSocket(const std::string& server_name, int port_number);
+
+  template <typename T>
+  friend void write(const Socket& socket, const T& value);
+
+  template <typename T>
+  friend void read(const Socket& socket, T& value);
+
+  template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+  friend void write(const Socket& socket, const ArrayT<T, R...>& array);
+
+  template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+  friend void read(const Socket& socket, ArrayT<T, R...>& array);
+
+  Socket(Socket&&)      = default;
+  Socket(const Socket&) = default;
+
+  ~Socket() = default;
+};
+
+Socket createServerSocket(int port_number);
+Socket acceptClientSocket(const Socket& server);
+Socket connectServerSocket(const std::string& server_name, int port_number);
+
+template <typename T>
+inline void
+write(const Socket& socket, const T& value)
+{
+  static_assert(std::is_arithmetic_v<T> or is_tiny_vector_v<T> or is_tiny_matrix_v<T>, "unexpected value type");
+  socket._write(reinterpret_cast<const char*>(&value), sizeof(T) / sizeof(char));
+}
+
+template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+void
+write(const Socket& socket, const ArrayT<T, R...>& array)
+{
+  socket._write(reinterpret_cast<const char*>(&array[0]), array.size() * sizeof(T) / sizeof(char));
+}
+
+template <typename T>
+inline void
+read(const Socket& socket, T& value)
+{
+  static_assert(not std::is_const_v<T>, "cannot read values into const data");
+  socket._read(reinterpret_cast<char*>(&value), sizeof(T) / sizeof(char));
+}
+
+template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+void
+read(const Socket& socket, ArrayT<T, R...>& array)
+{
+  static_assert(not std::is_const_v<T>, "cannot read values into const data");
+  static_assert(std::is_arithmetic_v<T> or is_tiny_vector_v<T> or is_tiny_matrix_v<T>, "unexpected value type");
+  if (array.size() > 0) {
+    socket._read(reinterpret_cast<char*>(&array[0]), array.size() * sizeof(T) / sizeof(char));
+  }
+}
+
+#endif   // SOCKET_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 96729d8acb0fc5dc87df0ddb732d63fc0d2a8ee7..6bccbfae744ed002e0edcde321205f309e88105a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -120,6 +120,8 @@ add_executable (unit_tests
   test_RevisionInfo.cpp
   test_SmallArray.cpp
   test_SmallVector.cpp
+  test_Socket.cpp
+  test_SocketModule.cpp
   test_SquareGaussQuadrature.cpp
   test_SquareTransformation.cpp
   test_SymbolTable.cpp
diff --git a/tests/test_BinaryExpressionProcessor_shift.cpp b/tests/test_BinaryExpressionProcessor_shift.cpp
index 2f89f1f61c45ddcc137434d28f3196201c1ce866..c1892a2ff3a793a1cdc9a48e6f1a454b090fe77d 100644
--- a/tests/test_BinaryExpressionProcessor_shift.cpp
+++ b/tests/test_BinaryExpressionProcessor_shift.cpp
@@ -5,6 +5,7 @@
 #include <utils/pugs_config.hpp>
 
 #include <fstream>
+#include <netdb.h>
 #include <unistd.h>
 
 // clazy:excludeall=non-pod-global-static
@@ -20,8 +21,10 @@ TEST_CASE("BinaryExpressionProcessor shift", "[language]")
 
     {
       std::ostringstream data;
+      data << R"(import socket;)";
       data << "let fout:ostream, fout = ofstream(\"" << path.string() << "\");\n";
-      data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n";)";
+      data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n";
+fout << createSocketServer(0);)";
 
       TAO_PEGTL_NAMESPACE::string_input input{data.str(), "test.pgs"};
       auto ast = ASTBuilder::build(input);
@@ -53,7 +56,27 @@ TEST_CASE("BinaryExpressionProcessor shift", "[language]")
         }
       } while (fin);
 
-      REQUIRE(file_content == "2 true 5\n");
+      std::string expected = "2 true 5\n";
+      char hbuf[NI_MAXHOST];
+      ::gethostname(hbuf, NI_MAXHOST);
+      expected += hbuf;
+      expected += ':';
+
+      REQUIRE(file_content.size() > expected.size());
+      REQUIRE(file_content.substr(0, expected.size()) == expected);
+
+      std::string suffix = file_content.substr(expected.size(), file_content.size() - expected.size());
+
+      auto is_int = [](const std::string& s) {
+        for (const char& c : s) {
+          if (not std::isdigit(c)) {
+            return false;
+          }
+        }
+        return true;
+      };
+
+      REQUIRE(is_int(suffix));
     }
 
     std::filesystem::remove(filename);
diff --git a/tests/test_MathModule.cpp b/tests/test_MathModule.cpp
index 6846b7c4e24a17dde8747afd9e4fcb1982678985..99801b5f1d6602cbc40b0308e20ea1c1d9009b24 100644
--- a/tests/test_MathModule.cpp
+++ b/tests/test_MathModule.cpp
@@ -4,8 +4,6 @@
 #include <language/modules/MathModule.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 
-#include <set>
-
 // clazy:excludeall=non-pod-global-static
 
 TEST_CASE("MathModule", "[language]")
diff --git a/tests/test_Socket.cpp b/tests/test_Socket.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3293f6b8d7fe18db31a0db1aaac0ae30474d8808
--- /dev/null
+++ b/tests/test_Socket.cpp
@@ -0,0 +1,116 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <utils/PugsAssert.hpp>
+
+#include <utils/Socket.hpp>
+
+#include <sstream>
+#include <thread>
+
+#include <netdb.h>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Socket", "[utils]")
+{
+  SECTION("create/connect and simple read/write")
+  {
+    auto self_client = [](int port) {
+      Socket self_server      = connectServerSocket("localhost", port);
+      std::vector<int> values = {1, 2, 4, 7};
+      write(self_server, values.size());
+      write(self_server, values);
+    };
+
+    Socket server = createServerSocket(0);
+
+    std::thread t(self_client, server.portNumber());
+
+    Socket client = acceptClientSocket(server);
+
+    size_t vector_size = [&] {
+      size_t vector_size;
+      read(client, vector_size);
+      return vector_size;
+    }();
+
+    REQUIRE(vector_size == 4);
+
+    std::vector<int> v(vector_size);
+    read(client, v);
+
+    REQUIRE(v == std::vector<int>{1, 2, 4, 7});
+
+    t.join();
+  }
+
+  SECTION("move constructor")
+  {
+    Socket server   = createServerSocket(0);
+    int port_number = server.portNumber();
+
+    Socket moved_server(std::move(server));
+
+    REQUIRE(port_number == moved_server.portNumber());
+  }
+
+  SECTION("host and port info")
+  {
+    Socket server   = createServerSocket(0);
+    int port_number = server.portNumber();
+
+    std::ostringstream info;
+    info << server;
+
+    std::ostringstream expected;
+    char hbuf[NI_MAXHOST];
+    ::gethostname(hbuf, NI_MAXHOST);
+    expected << hbuf << ':' << port_number;
+
+    REQUIRE(info.str() == expected.str());
+  }
+
+  SECTION("errors")
+  {
+    SECTION("connection")
+    {
+      {
+        auto server = createServerSocket(0);
+        REQUIRE_THROWS_WITH(createServerSocket(server.portNumber()), "error: Address already in use");
+      }
+      REQUIRE_THROWS_WITH(connectServerSocket("localhost", 1), "error: Connection refused");
+
+      // The error message is not checked since it can depend on the
+      // network connection itself
+      REQUIRE_THROWS(connectServerSocket("an invalid host name", 1));
+    }
+
+    SECTION("server <-> server")
+    {
+      Socket server = createServerSocket(0);
+      REQUIRE_THROWS_WITH(write(server, 12), "error: Server cannot write to server socket");
+      REQUIRE_THROWS_WITH(write(server, std::vector<int>{1, 2, 3}), "error: Server cannot write to server socket");
+
+      double x;
+      REQUIRE_THROWS_WITH(read(server, x), "error: Server cannot read from server socket");
+      std::vector<double> v(1);
+      REQUIRE_THROWS_WITH(read(server, v), "error: Server cannot read from server socket");
+    }
+
+    SECTION("invalid read")
+    {
+      auto self_client = [](int port) { Socket self_server = connectServerSocket("localhost", port); };
+
+      Socket server = createServerSocket(0);
+
+      std::thread t(self_client, server.portNumber());
+      Socket client = acceptClientSocket(server);
+      t.join();
+
+      double x;
+      REQUIRE_THROWS_WITH(read(client, x), "error: Could not read data");
+      // One cannot test easily write errors...
+    }
+  }
+}
diff --git a/tests/test_SocketModule.cpp b/tests/test_SocketModule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..879595965742b5aaa100784037506e58db22150f
--- /dev/null
+++ b/tests/test_SocketModule.cpp
@@ -0,0 +1,711 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <language/modules/SocketModule.hpp>
+#include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <utils/Socket.hpp>
+
+#include <thread>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SocketModule", "[language]")
+{
+  SocketModule socket_module;
+  const auto& name_builtin_function = socket_module.getNameBuiltinFunctionMap();
+
+  REQUIRE(name_builtin_function.size() == 25);
+
+  auto create_socket_server = [&name_builtin_function] {
+    auto i_function = name_builtin_function.find("createSocketServer:N");
+    REQUIRE(i_function != name_builtin_function.end());
+
+    uint64_t arg            = 0;
+    DataVariant arg_variant = arg;
+
+    IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+    DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+    REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()));
+
+    return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr();
+  };
+
+  auto accept_socket_client = [&name_builtin_function](const std::shared_ptr<const Socket>& server) {
+    auto i_function = name_builtin_function.find("acceptSocketClient:socket");
+    REQUIRE(i_function != name_builtin_function.end());
+
+    DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(server));
+
+    IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+    DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+    REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()));
+
+    return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr();
+  };
+
+  auto connect_socket_server = [&name_builtin_function](const std::string& hostname, int64_t port) {
+    auto i_function = name_builtin_function.find("connectSocketServer:string*N");
+    REQUIRE(i_function != name_builtin_function.end());
+
+    DataVariant hostname_variant = hostname;
+    DataVariant port_variant     = port;
+
+    IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+    DataVariant result_variant                  = function_embedder.apply({hostname_variant, port_variant});
+
+    REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()));
+
+    return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr();
+  };
+
+  SECTION("read")
+  {
+    SECTION("read_B")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        bool b = true;
+        write(*self_server, b);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_B:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<bool>(result_variant) == true);
+
+      t.join();
+    }
+
+    SECTION("read_N")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        uint64_t n = 12;
+        write(*self_server, n);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_N:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<uint64_t>(result_variant) == 12);
+
+      t.join();
+    }
+
+    SECTION("read_Z")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        int64_t k = -3;
+        write(*self_server, k);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_Z:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<int64_t>(result_variant) == -3);
+
+      t.join();
+    }
+
+    SECTION("read_R")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        double x = -3.141;
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<double>(result_variant) == -3.141);
+
+      t.join();
+    }
+
+    SECTION("read_R1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<1> x{1.414};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R1:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyVector<1>>(result_variant) == TinyVector<1>{1.414});
+
+      t.join();
+    }
+
+    SECTION("read_R2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<2> x{1.414, -3.7};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R2:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyVector<2>>(result_variant) == TinyVector<2>{1.414, -3.7});
+
+      t.join();
+    }
+
+    SECTION("read_R3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<3> x{1.414, -3.7, 5.19};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R3:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyVector<3>>(result_variant) == TinyVector<3>{1.414, -3.7, 5.19});
+
+      t.join();
+    }
+
+    SECTION("read_R1x1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<1> x{1.414};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R1x1:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyMatrix<1>>(result_variant) == TinyMatrix<1>{1.414});
+
+      t.join();
+    }
+
+    SECTION("read_R2x2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<2> x{1.414, -3.7, 2.4, -6};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R2x2:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyMatrix<2>>(result_variant) == TinyMatrix<2>{1.414, -3.7, 2.4, -6});
+
+      t.join();
+    }
+
+    SECTION("read_R3x3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<3> x{1.414, -3.7, 5.19, 2, 0, -2.6, 1.2, -6, 9.3};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R3x3:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyMatrix<3>>(result_variant) == TinyMatrix<3>{1.414, -3.7, 5.19, 2, 0, -2.6, 1.2, -6, 9.3});
+
+      t.join();
+    }
+
+    SECTION("read_string")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        std::string s = "foobar";
+        write(*self_server, s.size());
+        write(*self_server, s);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_string:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<std::string>(result_variant) == std::string{"foobar"});
+
+      t.join();
+    }
+  }
+
+  SECTION("write")
+  {
+    auto get_result = [&name_builtin_function](std::shared_ptr<const Socket> p_client) {
+      auto i_function = name_builtin_function.find("read_B:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+      return std::get<bool>(result_variant);
+    };
+
+    SECTION("B")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        bool b;
+        read(*self_server, b);
+        const bool result = (b == true);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*B");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = true;
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("N")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        uint64_t n;
+        read(*self_server, n);
+        const bool result = (n == 17);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*N");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = uint64_t{17};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("Z")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        int64_t k;
+        read(*self_server, k);
+        const bool result = (k == -13);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*Z");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = int64_t{-13};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        double x;
+        read(*self_server, x);
+        const bool result = (x == -3.1415);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = double{-3.1415};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<1> x;
+        read(*self_server, x);
+        const bool result = (x == TinyVector<1>{-3.1415});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^1");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyVector<1>{-3.1415};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<2> x;
+        read(*self_server, x);
+        const bool result = (x == TinyVector<2>{-3.1415, 1.414});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^2");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyVector<2>{-3.1415, 1.414};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<3> x;
+        read(*self_server, x);
+        const bool result = (x == TinyVector<3>{-3.1415, 1.414, 1.3});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^3");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyVector<3>{-3.1415, 1.414, 1.3};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^1x1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<1> x;
+        read(*self_server, x);
+        const bool result = (x == TinyMatrix<1>{-3.1415});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^1x1");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyMatrix<1>{-3.1415};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^2x2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<2> x;
+        read(*self_server, x);
+        const bool result = (x == TinyMatrix<2>{3, -3.1415, -1.2, 5.6});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^2x2");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyMatrix<2>{3, -3.1415, -1.2, 5.6};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^3x3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<3> x;
+        read(*self_server, x);
+        const bool result = (x == TinyMatrix<3>{3, -3.1415, -1.2, 5.6, -4, 1.7, -5.2, 3.4, 2});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^3x3");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyMatrix<3>{3, -3.1415, -1.2, 5.6, -4, 1.7, -5.2, 3.4, 2};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("string")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        size_t size;
+        read(*self_server, size);
+        std::string s;
+        s.resize(size);
+        read(*self_server, s);
+        const bool result = (s == "foobar");
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*string");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = std::string{"foobar"};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+  }
+}