diff --git a/RoboCat/Chapter3.vcxproj b/RoboCat/Chapter3.vcxproj index 10d69fef..bf567325 100644 --- a/RoboCat/Chapter3.vcxproj +++ b/RoboCat/Chapter3.vcxproj @@ -381,11 +381,13 @@ + + diff --git a/RoboCat/Inc/OutputWindow.h b/RoboCat/Inc/OutputWindow.h index a12ad9ef..73312118 100644 --- a/RoboCat/Inc/OutputWindow.h +++ b/RoboCat/Inc/OutputWindow.h @@ -1,3 +1,4 @@ +#pragma once // Encapsulates a simulated "output window," where // new messages are written from top to bottom diff --git a/RoboCat/Inc/RoboCatPCH.h b/RoboCat/Inc/RoboCatPCH.h index fa2871f8..9b5f8d1f 100644 --- a/RoboCat/Inc/RoboCatPCH.h +++ b/RoboCat/Inc/RoboCatPCH.h @@ -1,4 +1,4 @@ - +#pragma once #include diff --git a/RoboCat/Inc/RoboCatShared.h b/RoboCat/Inc/RoboCatShared.h index 83a3871a..3aa9061d 100644 --- a/RoboCat/Inc/RoboCatShared.h +++ b/RoboCat/Inc/RoboCatShared.h @@ -1,3 +1,5 @@ +#pragma once + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX diff --git a/RoboCat/Inc/SocketAddress.h b/RoboCat/Inc/SocketAddress.h index 05bad2b8..0c8d5671 100644 --- a/RoboCat/Inc/SocketAddress.h +++ b/RoboCat/Inc/SocketAddress.h @@ -1,3 +1,5 @@ +#pragma once + class SocketAddress { public: diff --git a/RoboCat/Inc/SocketAddressFactory.h b/RoboCat/Inc/SocketAddressFactory.h index fe1e0516..1afaa5b8 100644 --- a/RoboCat/Inc/SocketAddressFactory.h +++ b/RoboCat/Inc/SocketAddressFactory.h @@ -1,3 +1,4 @@ +#pragma once class SocketAddressFactory diff --git a/RoboCat/Inc/SocketUtil.h b/RoboCat/Inc/SocketUtil.h index 50a726c3..7238c192 100644 --- a/RoboCat/Inc/SocketUtil.h +++ b/RoboCat/Inc/SocketUtil.h @@ -1,3 +1,5 @@ +#pragma once + enum class SocketAddressFamily { INET = AF_INET, diff --git a/RoboCat/Inc/StringUtils.h b/RoboCat/Inc/StringUtils.h index b46b745d..2552708e 100644 --- a/RoboCat/Inc/StringUtils.h +++ b/RoboCat/Inc/StringUtils.h @@ -1,3 +1,5 @@ +#pragma once + namespace StringUtils { string GetCommandLineArg( int inIndex ); diff --git a/RoboCat/Inc/TCPSocket.h b/RoboCat/Inc/TCPSocket.h index 0779a82b..3b3a42bd 100644 --- a/RoboCat/Inc/TCPSocket.h +++ b/RoboCat/Inc/TCPSocket.h @@ -1,3 +1,6 @@ +#pragma once + + class TCPSocket { public: diff --git a/RoboCat/Inc/UDPSocket.h b/RoboCat/Inc/UDPSocket.h index 939df407..dcf105c3 100644 --- a/RoboCat/Inc/UDPSocket.h +++ b/RoboCat/Inc/UDPSocket.h @@ -1,4 +1,4 @@ - +#pragma once class UDPSocket { diff --git a/RoboCat/Src/ChatUser.cpp b/RoboCat/Src/ChatUser.cpp new file mode 100644 index 00000000..4bdcc30a --- /dev/null +++ b/RoboCat/Src/ChatUser.cpp @@ -0,0 +1,247 @@ +#include "RoboCatPCH.h" +#include "ChatUser.h" +#include +#include + +// FUNCTION FROM user704565 AT https://stackoverflow.com/a/16286297: +std::vector split(std::string str, std::string sep) { + char* cstr = const_cast(str.c_str()); + char* current; + std::vector arr; + current = strtok(cstr, sep.c_str()); + while (current != NULL) { + arr.push_back(current); + current = strtok(NULL, sep.c_str()); + } + return arr; +} + +ChatUser::ChatUser() +{ + SocketUtil::StaticInit(); + sendSocket = SocketUtil::CreateTCPSocket(SocketAddressFamily::INET); + recvSocket = SocketUtil::CreateTCPSocket(SocketAddressFamily::INET); + +} + +ChatUser::~ChatUser() +{ + send("©DISCONNECT"); + shutdown(); + closeSockets(); + SocketUtil::CleanUp(); +} + +void ChatUser::startTcpThread(bool isClient) +{ + username = isClient ? "Chat User 1" : "Chat User 2"; + + if (isClient) + t = std::thread([this] { initTcpClient(); }); + else + t = std::thread([this] { initTcpServer(); }); +} + +void ChatUser::initTcpClient() +{ + sendRecvFlag = 0; + + // Create socket + sendSocket = SocketUtil::CreateTCPSocket(SocketAddressFamily::INET); + if (sendSocket == nullptr) + { + SocketUtil::ReportError("Creating client socket"); + ExitProcess(1); + } + + LOG("%s", "Client socket created"); + + // Bind() - "Bind" socket -> tells OS we want to use a specific address + + std::string address = StringUtils::Sprintf("127.0.0.1:%s", SEND_PORT.c_str()); + SocketAddressPtr clientAddress = SocketAddressFactory::CreateIPv4FromString(address.c_str()); + if (clientAddress == nullptr) + { + SocketUtil::ReportError("Creating client address"); + ExitProcess(1); + } + + if (sendSocket->Bind(*clientAddress) != NO_ERROR) + { + SocketUtil::ReportError("Binding client socket"); + // This doesn't block! + ExitProcess(1); + } + + LOG("%s", "Bound client socket"); + + // Connect() -> Connect socket to remote host + + SocketAddressPtr servAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:"+RECV_PORT); + if (servAddress == nullptr) + { + SocketUtil::ReportError("Creating server address"); + ExitProcess(1); + } + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + if (sendSocket->Connect(*servAddress) != NO_ERROR) + { + SocketUtil::ReportError("Connecting to server"); + ExitProcess(1); + } + + LOG("%s", "Connected to server!"); + hasConnected = true; + send("©CONNECT"); + quit = false; + std::thread receiveNewThread([&]() { // don't use [&] :) + while (!quit) // Need to add a quit here to have it really exit! + { + char buffer[4096]; + int32_t bytesReceived = sendSocket->Receive(buffer, 4096); + if (bytesReceived == 0) + { + // handle disconnect + } + if (bytesReceived < 0) + { + SocketUtil::ReportError("Receiving"); + return; + } + + std::string receivedMsg(buffer, bytesReceived); + recv(receivedMsg); + } + }); + receiveNewThread.join(); +} + +void ChatUser::initTcpServer() +{ + sendRecvFlag = 1; + // Create socket + recvSocket = SocketUtil::CreateTCPSocket(SocketAddressFamily::INET); + if (recvSocket == nullptr) + { + SocketUtil::ReportError("Creating listening socket"); + ExitProcess(1); + } + + //recvSocket->SetNonBlockingMode(true); + + LOG("%s", "Listening socket created"); + + // Bind() - "Bind" socket -> tells OS we want to use a specific address + + SocketAddressPtr listenAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:"+ RECV_PORT); + if (listenAddress == nullptr) + { + SocketUtil::ReportError("Creating listen address"); + ExitProcess(1); + } + + if (recvSocket->Bind(*listenAddress) != NO_ERROR) + { + SocketUtil::ReportError("Binding listening socket"); + // This doesn't block! + ExitProcess(1); + } + + LOG("%s", "Bound listening socket"); + + // Blocking function call -> Waits for some input; halts the program until something "interesting" happens + // Non-Blocking function call -> Returns right away, as soon as the action is completed + + // Listen() - Listen on socket -> Non-blocking; tells OS we care about incoming connections on this socket + if (recvSocket->Listen() != NO_ERROR) + { + SocketUtil::ReportError("Listening on listening socket"); + ExitProcess(1); + } + + LOG("%s", "Listening on socket"); + + // Accept() - Accept on socket -> Blocking; Waits for incoming connection and completes TCP handshake + + LOG("%s", "Waiting to accept connections..."); + SocketAddress incomingAddress; + recvConnSocket = recvSocket->Accept(incomingAddress); + while (recvConnSocket == nullptr) + { + recvConnSocket = recvSocket->Accept(incomingAddress); + // SocketUtil::ReportError("Accepting connection"); + // ExitProcess(1); + } + hasConnected = true; + send("©CONNECT"); + LOG("Accepted connection from %s", incomingAddress.ToString().c_str()); + + quit = false; + std::thread receiveThread([&]() { // don't use [&] :) + while (!quit) // Need to add a quit here to have it really exit! + { + char buffer[4096]; + int32_t bytesReceived = recvConnSocket->Receive(buffer, 4096); + if (bytesReceived == 0) + { + // handle disconnec + } + if (bytesReceived < 0) + { + SocketUtil::ReportError("Receiving"); + return; + } + + std::string receivedMsg(buffer, bytesReceived); + recv(receivedMsg); + } + }); + + receiveThread.join(); +} + +void ChatUser::send(std::string msg) +{ + if (strlen(msg.c_str()) > 0) + { + std::string toSend = username + "¦" + msg; + if (msg == "©DISCONNECT") + toSend = "©[SERVER]: " + username + " has disconnected!\n"; + if (msg == "©CONNECT") + toSend = "©[SERVER]: " + username + " has connected!\n"; + + (sendRecvFlag == 0 ? sendSocket : recvConnSocket)->Send(toSend.c_str(), toSend.length()); + } +} + +void ChatUser::recv(std::string msg) +{ + if (msg[0] == '©') + { + std::cout << msg.erase(0, 1); + } + else + { + std::vector str = split(msg, "¦"); + std::cout << ("[" + str[0] + "]: " + str[1] + "\n"); + } +} + +void ChatUser::closeSockets() +{ + if (sendRecvFlag == 0) + { + sendSocket->~TCPSocket(); + } + else if (sendRecvFlag == 1) + { + + recvConnSocket->~TCPSocket(); + recvConnSocket->~TCPSocket(); + } +} + +void ChatUser::shutdown() +{ + quit = true; +} diff --git a/RoboCat/Src/ChatUser.h b/RoboCat/Src/ChatUser.h new file mode 100644 index 00000000..324a8455 --- /dev/null +++ b/RoboCat/Src/ChatUser.h @@ -0,0 +1,33 @@ +#pragma once + +#include "RoboCatPCH.h" +#include + + +struct ChatUser +{ + OutputWindow win; + std::string username; + int sendRecvFlag = -1; + bool quit; + bool hasConnected = false; + + const std::string SEND_PORT = "7000", RECV_PORT = "8080"; + + TCPSocketPtr sendSocket, recvSocket, recvConnSocket; + std::thread t; + + ChatUser(); + ~ChatUser(); + + void startTcpThread(bool isClient); + + void initTcpClient(); + void initTcpServer(); + + void send(std::string msg); + void recv(std::string msg); + + void closeSockets(); + void shutdown(); +}; \ No newline at end of file diff --git a/RoboCat/Src/Main.cpp b/RoboCat/Src/Main.cpp index f8c6c7d0..79bbebaf 100644 --- a/RoboCat/Src/Main.cpp +++ b/RoboCat/Src/Main.cpp @@ -5,9 +5,14 @@ #include #include #include +#include "ChatUser.h" #if _WIN32 +ChatUser* chatter = new ChatUser(); +std::thread tr1, tr2; +bool run = true; + int main(int argc, const char** argv) { @@ -22,32 +27,31 @@ int main(int argc, const char** argv) __argv = argv; #endif - SocketUtil::StaticInit(); - - OutputWindow win; - - std::thread t([&win]() - { - int msgNo = 1; - while (true) - { - std::this_thread::sleep_for(std::chrono::milliseconds(250)); - std::string msgIn("~~~auto message~~~"); - std::stringstream ss(msgIn); - ss << msgNo; - win.Write(ss.str()); - msgNo++; - } - }); - - while (true) + std::cout << "User 1 or User 2? Enter number: "; + std::string userNumber = "1"; + std::getline(std::cin, userNumber); + while (userNumber != "1" && userNumber != "2") + { + std::cout << "Invalid entry. Please enter 1 or 2: "; + std::getline(std::cin, userNumber); + } + + chatter->startTcpThread(userNumber == "1"); + std::cout << "Enter \"!EXIT\" at any point to exit. Case sensitive.\n"; + + while (run) { std::string input; std::getline(std::cin, input); - win.WriteFromStdin(input); + + chatter->win.WriteFromStdin("[" + chatter->username + "]: " + input + "\n"); + if (input == "!EXIT") + run = false; + else + chatter->send(input); } - SocketUtil::CleanUp(); + delete chatter; return 0; }