The NetworkManager is what’s responsible for handling everything related to the network packets, it’s core interface revolving around the SocketObject that implements Winsock2. From connecting & establishing, disconnecting and dispatching the received messages to the messaging interface to allow for handling of those messages. Also, for being able to insert particular messages into the send queue from another network component within the engine. It’s architecture maintains a multi-threaded send & receive system to allow for continuous non-blocking execution.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
/* Author: Chase Hutchens Date : 7/1/15 - 1/16/15 */ // All content © 2014-2015 DigiPen (USA) Corporation, all rights reserved. #pragma once // Local Modules #include "exports.h" // Other Engine Modules #include "../Engine/Messages.h" #include "../DebugTools/DebugTimer.h" // Third Party Modules #include <thread> /* std::thread */ #include <queue> /* std::queue */ #include <memory> /* std::unique_ptr */ #include <mutex> /* std::mutex */ #define PACKET_SIZE 4096 // Used for declaring a particular message #define NETMSG_DECLARATION '!' #define NMD NETMSG_DECLARATION // Used for separating specific message pieces #define NETMSG_SEPARATOR ':' #define NMS NETMSG_SEPARATOR // Used for separating your data pieces within specific message pieces #define NETMSG_DATA_SEPARATOR '|' #define NMDS NETMSG_DATA_SEPARATOR // Used for declaring the end of the specific message #define NETMSG_END "\r\n" #define NME NETMSG_END class SocketObject; class NetworkChatService; class ClientMessageLogger; typedef unsigned short u_short; enum class NET_EXPORT MESSAGE_TYPE { Info, // Message Containing Information for the Server to Log Game, // Message Containing Game Information Relay, // Message that will be relayed to all other clients Data, // Message that is received when receiving downloaded data }; class NetworkManager : public EventBase { private: bool establishInProgress, activeConnection, allowNewMessages; bool disconnectMsgsCompleted; float timeSincePing, sentKbps, recvKbps; SocketObject* clientSocket; double networkTime; std::string sessionID, pingMsg; std::thread connector, receiver, sender, disconnector; std::mutex toSendLock; std::vector<std::string> receivedInfoMsgs, receivedGameMsgs, receivedRelayMsgs, receivedDataMsgs; std::queue<std::string> toSendData; DebugTimer m_DBT_NetworkManager; // keeps track of our network ping time std::unique_ptr<ClientMessageLogger> logger; private: // thread functions void EstablishConnection(float waitTime, float timeoutTime); void ReceivePacket(); void SendPacket(); void DisconnectConnection(); // -- void ExecuteReceivedServerMessages(); public: NET_EXPORT static NetworkManager& GetInstance(); NET_EXPORT static void Init(); NET_EXPORT static void Update(); NET_EXPORT static void Free(); NET_EXPORT void Connect(int protocol, const char* ip, u_short port); NET_EXPORT void Disconnect(); NET_EXPORT void OfflineSafeDisconnect(); NET_EXPORT void AddMsgToSend(const std::string& msg); NET_EXPORT bool IsActiveConnection(); NET_EXPORT bool IsEstablishInProgress(); NET_EXPORT DebugTimer* GetNetworkDebugPingTimer(); NET_EXPORT std::string GetServerCmdFromMessageType(MESSAGE_TYPE msgType); NET_EXPORT std::string GetSessionID(); NET_EXPORT double GetNetworkTime(); NET_EXPORT float GetSentKbps(); NET_EXPORT float GetRecvKbps(); }; |
Here’s how I’m inserting packets for sending over the network:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
void NetworkManager::SendPacket() { float kbps = 0, elapsedTime = 0; while (this->activeConnection) { NetworkManager& networkManager = NetworkManager::GetInstance(); toSendLock.lock(); // only when allowing new messages if (this->allowNewMessages) { networkManager.AttachData<NetworkEvent>(EventList::SendNetworkMessage)->sendData = &networkManager.toSendData; networkManager.DispatchEvent(EventList::SendNetworkMessage); } while (!this->toSendData.empty()) { std::string toSendMsg = this->toSendData.front(); this->toSendData.pop(); logger->AddLogMsg("SEND ", toSendMsg); char buffer[PACKET_SIZE] = { 0 }; strcat_s(buffer, PACKET_SIZE * sizeof(char), toSendMsg.c_str()); int msgSize = toSendMsg.size() * sizeof(char); if (networkManager.clientSocket->GetProtocolType() == IPPROTO_TCP) { networkManager.clientSocket->TCP_SendPacketToSocket(buffer, msgSize); } else if (networkManager.clientSocket->GetProtocolType() == IPPROTO_UDP) { networkManager.clientSocket->UDP_SendPacketToSocket(buffer, msgSize); } kbps += msgSize; if (elapsedTime >= 1) { sentKbps = kbps * .001f; kbps = elapsedTime = 0; } } if (!this->allowNewMessages) { networkManager.disconnectMsgsCompleted = networkManager.toSendData.empty(); } toSendLock.unlock(); elapsedTime += .016f; std::this_thread::sleep_for(std::chrono::milliseconds(16)); } } |
Here’s how I’m receiving network messages and then dispatching through the event system:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
void NetworkManager::ReceivePacket() { float kbps = 0, elapsedTime = 0; while (this->activeConnection && this->allowNewMessages) { NetworkManager& networkManager = NetworkManager::GetInstance(); int bytesReceived = 0, totalBytes = 0; bool finished = false; std::queue<std::string> messages; char buffer[PACKET_SIZE] = { 0 }; while (!finished) { if (networkManager.clientSocket->GetProtocolType() == IPPROTO_TCP) { bytesReceived = networkManager.clientSocket->TCP_ReceivePacketFromSocket(buffer, PACKET_SIZE); } else if (networkManager.clientSocket->GetProtocolType() == IPPROTO_UDP) { bytesReceived = networkManager.clientSocket->UDP_ReceivePacketFromSocket(buffer, PACKET_SIZE, (sockaddr*)&networkManager.clientSocket->GetAddress()); } if (bytesReceived <= 0) { finished = true; } else if (bytesReceived > 0) { totalBytes += bytesReceived; messages.push(buffer); } } if (!messages.empty()) { kbps += totalBytes; if (elapsedTime >= 1) { recvKbps = kbps * .001f; kbps = elapsedTime = 0; } while (!messages.empty()) { size_t pos = 0; std::string received = messages.front(); messages.pop(); // parse received messages into individual messages while ((pos = received.find("\r\n")) != std::string::npos) { // collect the sub msg up to the \r\n delimiter std::string subMsg = received.substr(0, pos); logger->AddLogMsg("RECEIVE", subMsg + "\r\n"); // determine what type of message this is // :INFO, :GAME, :RELAY, :DATA std::string msgType = ParseUtilities::ParseFromCollectTo(subMsg, ':', 1, '!'); msgType = ":" + msgType; // need to append the ':' since the ParseUtilities removes it if (std::strcmp(msgType.c_str(), networkManager.GetServerCmdFromMessageType(MESSAGE_TYPE::Info).c_str()) == 0) { networkManager.receivedInfoMsgs.push_back(subMsg); } else if (std::strcmp(msgType.c_str(), networkManager.GetServerCmdFromMessageType(MESSAGE_TYPE::Game).c_str()) == 0) { networkManager.receivedGameMsgs.push_back(subMsg); } else if (std::strcmp(msgType.c_str(), networkManager.GetServerCmdFromMessageType(MESSAGE_TYPE::Relay).c_str()) == 0) { networkManager.receivedRelayMsgs.push_back(subMsg); } else if (std::strcmp(msgType.c_str(), networkManager.GetServerCmdFromMessageType(MESSAGE_TYPE::Data).c_str()) == 0) { networkManager.receivedDataMsgs.push_back(subMsg); } // erase this message and continue to next received.erase(0, pos + 1); } } // determine separating of server specific message and from other client messages? networkManager.ExecuteReceivedServerMessages(); // send individual messages per event or all messages per event? std::vector<std::vector<std::string>*>& receivedMsgs = networkManager.AttachData<NetworkEvent>(EventList::RecvNetworkMessage)->recvData; receivedMsgs.push_back(&networkManager.receivedInfoMsgs); receivedMsgs.push_back(&networkManager.receivedGameMsgs); receivedMsgs.push_back(&networkManager.receivedRelayMsgs); receivedMsgs.push_back(&networkManager.receivedDataMsgs); networkManager.DispatchEvent(EventList::RecvNetworkMessage); receivedMsgs.clear(); networkManager.receivedRelayMsgs.clear(); networkManager.receivedGameMsgs.clear(); networkManager.receivedInfoMsgs.clear(); networkManager.receivedDataMsgs.clear(); } elapsedTime += .001f; std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } |