diff --git a/src/network/room.cpp b/src/network/room.cpp
index 48de2f5cb8..274cb41590 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -2,6 +2,8 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <atomic>
+#include <thread>
 #include "enet/enet.h"
 #include "network/room.h"
 
@@ -16,8 +18,38 @@ public:
 
     std::atomic<State> state{State::Closed}; ///< Current state of the room.
     RoomInformation room_information;        ///< Information about this room.
+
+    /// Thread that receives and dispatches network packets
+    std::unique_ptr<std::thread> room_thread;
+
+    /// Thread function that will receive and dispatch messages until the room is destroyed.
+    void ServerLoop();
+    void StartLoop();
 };
 
+// RoomImpl
+void Room::RoomImpl::ServerLoop() {
+    while (state != State::Closed) {
+        ENetEvent event;
+        if (enet_host_service(server, &event, 1000) > 0) {
+            switch (event.type) {
+            case ENET_EVENT_TYPE_RECEIVE:
+                // TODO(B3N30): Select the type of message and handle it
+                enet_packet_destroy(event.packet);
+                break;
+            case ENET_EVENT_TYPE_DISCONNECT:
+                // TODO(B3N30): Handle the disconnect from a client
+                break;
+            }
+        }
+    }
+}
+
+void Room::RoomImpl::StartLoop() {
+    room_thread = std::make_unique<std::thread>(&Room::RoomImpl::ServerLoop, this);
+}
+
+// Room
 Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
 
 Room::~Room() = default;
@@ -34,8 +66,7 @@ void Room::Create(const std::string& name, const std::string& server_address, u1
 
     room_impl->room_information.name = name;
     room_impl->room_information.member_slots = MaxConcurrentConnections;
-
-    // TODO(B3N30): Start the receiving thread
+    room_impl->StartLoop();
 }
 
 Room::State Room::GetState() const {
@@ -48,7 +79,8 @@ const RoomInformation& Room::GetRoomInformation() const {
 
 void Room::Destroy() {
     room_impl->state = State::Closed;
-    // TODO(B3n30): Join the receiving thread
+    room_impl->room_thread->join();
+    room_impl->room_thread.reset();
 
     if (room_impl->server) {
         enet_host_destroy(room_impl->server);
diff --git a/src/network/room.h b/src/network/room.h
index 70c64d5f17..0a6217c11e 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -4,7 +4,6 @@
 
 #pragma once
 
-#include <atomic>
 #include <memory>
 #include <string>
 #include "common/common_types.h"
@@ -19,6 +18,19 @@ struct RoomInformation {
     u32 member_slots; ///< Maximum number of members in this room
 };
 
+// The different types of messages that can be sent. The first byte of each packet defines the type
+typedef uint8_t MessageID;
+enum RoomMessageTypes {
+    IdJoinRequest = 1,
+    IdJoinSuccess,
+    IdRoomInformation,
+    IdSetGameName,
+    IdWifiPacket,
+    IdChatMessage,
+    IdNameCollision,
+    IdMacCollision
+};
+
 /// This is what a server [person creating a server] would use.
 class Room final {
 public:
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index c87f009f43..e1a0dfdabf 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -2,6 +2,9 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <atomic>
+#include <mutex>
+#include <thread>
 #include "common/assert.h"
 #include "enet/enet.h"
 #include "network/room_member.h"
@@ -16,10 +19,65 @@ public:
     ENetPeer* server = nullptr; ///< The server peer the client is connected to
 
     std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
+    void SetState(const State new_state);
+    bool IsConnected() const;
 
     std::string nickname; ///< The nickname of this member.
+
+    std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
+    /// Thread that receives and dispatches network packets
+    std::unique_ptr<std::thread> receive_thread;
+    void ReceiveLoop();
+    void StartLoop();
 };
 
+// RoomMemberImpl
+void RoomMember::RoomMemberImpl::SetState(const State new_state) {
+    state = new_state;
+    // TODO(B3N30): Invoke the callback functions
+}
+
+bool RoomMember::RoomMemberImpl::IsConnected() const {
+    return state == State::Joining || state == State::Joined;
+}
+
+void RoomMember::RoomMemberImpl::ReceiveLoop() {
+    // Receive packets while the connection is open
+    while (IsConnected()) {
+        std::lock_guard<std::mutex> lock(network_mutex);
+        ENetEvent event;
+        if (enet_host_service(client, &event, 1000) > 0) {
+            if (event.type == ENET_EVENT_TYPE_RECEIVE) {
+                switch (event.packet->data[0]) {
+                // TODO(B3N30): Handle the other message types
+                case IdNameCollision:
+                    SetState(State::NameCollision);
+                    enet_packet_destroy(event.packet);
+                    enet_peer_disconnect(server, 0);
+                    enet_peer_reset(server);
+                    return;
+                    break;
+                case IdMacCollision:
+                    SetState(State::MacCollision);
+                    enet_packet_destroy(event.packet);
+                    enet_peer_disconnect(server, 0);
+                    enet_peer_reset(server);
+                    return;
+                    break;
+                default:
+                    break;
+                }
+                enet_packet_destroy(event.packet);
+            }
+        }
+    }
+};
+
+void RoomMember::RoomMemberImpl::StartLoop() {
+    receive_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::ReceiveLoop, this);
+}
+
+// RoomMember
 RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
     room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
     ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client");
@@ -44,7 +102,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv
         enet_host_connect(room_member_impl->client, &address, NumChannels, 0);
 
     if (!room_member_impl->server) {
-        room_member_impl->state = State::Error;
+        room_member_impl->SetState(State::Error);
         return;
     }
 
@@ -52,22 +110,27 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv
     int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs);
     if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
         room_member_impl->nickname = nick;
-        room_member_impl->state = State::Joining;
+        room_member_impl->SetState(State::Joining);
+        room_member_impl->StartLoop();
         // TODO(B3N30): Send a join request with the nickname to the server
-        // TODO(B3N30): Start the receive thread
     } else {
-        room_member_impl->state = State::CouldNotConnect;
+        room_member_impl->SetState(State::CouldNotConnect);
     }
 }
 
 bool RoomMember::IsConnected() const {
-    return room_member_impl->state == State::Joining || room_member_impl->state == State::Joined;
+    return room_member_impl->IsConnected();
 }
 
 void RoomMember::Leave() {
-    enet_peer_disconnect(room_member_impl->server, 0);
-    room_member_impl->state = State::Idle;
-    // TODO(B3N30): Close the receive thread
+    ASSERT_MSG(room_member_impl->receive_thread != nullptr, "Must be in a room to leave it.");
+    {
+        std::lock_guard<std::mutex> lock(room_member_impl->network_mutex);
+        enet_peer_disconnect(room_member_impl->server, 0);
+        room_member_impl->SetState(State::Idle);
+    }
+    room_member_impl->receive_thread->join();
+    room_member_impl->receive_thread.reset();
     enet_peer_reset(room_member_impl->server);
 }
 
diff --git a/src/network/room_member.h b/src/network/room_member.h
index 177622b690..89ec6ae5ad 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -4,7 +4,6 @@
 
 #pragma once
 
-#include <atomic>
 #include <memory>
 #include <string>
 #include "common/common_types.h"