From c3d99939e58859f449eeccc9fbca9364e73fb402 Mon Sep 17 00:00:00 2001 From: Clementine Hassouni Date: Fri, 29 Apr 2022 00:40:53 -0400 Subject: [PATCH 1/3] basic logic setup -- socket crashing for some reason --- RoboCat/Chapter3.vcxproj | 2 + RoboCat/Inc/OutputWindow.h | 1 + RoboCat/Inc/RoboCatPCH.h | 2 +- RoboCat/Inc/RoboCatShared.h | 2 + RoboCat/Inc/SocketAddress.h | 2 + RoboCat/Inc/SocketAddressFactory.h | 1 + RoboCat/Inc/SocketUtil.h | 2 + RoboCat/Inc/StringUtils.h | 2 + RoboCat/Inc/TCPSocket.h | 3 + RoboCat/Inc/UDPSocket.h | 2 +- RoboCat/Src/ChatUser.cpp | 226 +++++++++++++++++++++++++++++ RoboCat/Src/ChatUser.h | 26 ++++ RoboCat/Src/Main.cpp | 64 +++++--- 13 files changed, 313 insertions(+), 22 deletions(-) create mode 100644 RoboCat/Src/ChatUser.cpp create mode 100644 RoboCat/Src/ChatUser.h 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..769e3280 --- /dev/null +++ b/RoboCat/Src/ChatUser.cpp @@ -0,0 +1,226 @@ +#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() +{ + closeSockets(); +} + +void ChatUser::initTcpClient(std::string sendPort, std::string recvPort) +{ + 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", sendPort.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:"+recvPort); + 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!"); + + 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); + } + }); + + std::cout << "Press enter to exit at any time!\n"; + std::cin.get(); + quit = true; +// recvConnSocket->~TCPSocket(); // Forcibly close socket (shouldn't call destructors like this -- make a new function for it! + receiveNewThread.join(); +} + +void ChatUser::initTcpServer(std::string listenPort) +{ + 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:"+ listenPort); + 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); + } + + 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 disconnect + } + if (bytesReceived < 0) + { + SocketUtil::ReportError("Receiving"); + return; + } + + std::string receivedMsg(buffer, bytesReceived); + recv(receivedMsg); + } + }); + + std::cout << "Press enter to exit at any time!\n"; + std::cin.get(); + quit = true; +// recvConnSocket->~TCPSocket(); // Forcibly close socket (shouldn't call destructors like this -- make a new function for it! + receiveThread.join(); +} + +void ChatUser::send(std::string msg) +{ + std::string toSend = username + "¦" + msg; + (sendRecvFlag == 0 ? sendSocket : recvConnSocket)->Send(toSend.c_str(), toSend.length()); +} + +void ChatUser::recv(std::string msg) +{ + std::vector str = split(msg, "¦"); + win.Write("[" + 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..f6a17199 --- /dev/null +++ b/RoboCat/Src/ChatUser.h @@ -0,0 +1,26 @@ +#pragma once + +#include "RoboCatPCH.h" + + +struct ChatUser +{ + OutputWindow win; + std::string username; + int sendRecvFlag = -1; + bool quit; + + TCPSocketPtr sendSocket, recvSocket, recvConnSocket; + + ChatUser(); + ~ChatUser(); + + void initTcpClient(std::string sendPort, std::string recvPort); + void initTcpServer(std::string listenPort); + + 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..926a6bb1 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(); +const std::string SEND_PORT = "7000", RECV_PORT = "8080"; +bool run = true; + int main(int argc, const char** argv) { @@ -22,32 +27,51 @@ 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); + } + OutputWindow win1, win2; + if (userNumber == "1") + { + chatter->username = "Chat User 1"; + std::thread c1([&win1]() + { + chatter->initTcpClient(SEND_PORT, RECV_PORT); + }); + } + else + { + chatter->username = "Chat User 2"; + std::thread c2([&win2]() + { + chatter->initTcpServer(RECV_PORT); + }); + } + + + + while (run) { std::string input; std::getline(std::cin, input); - win.WriteFromStdin(input); + if (input != "!EXIT") + { + chatter->win.WriteFromStdin("[" + chatter->username + "]: " + input + "\n"); + chatter->send(input); + } + else + { + run = false; + } } SocketUtil::CleanUp(); + delete chatter; return 0; } From 97c37e33750f03fc0e4577e4e99a789c81149979 Mon Sep 17 00:00:00 2001 From: Clementine Hassouni Date: Fri, 29 Apr 2022 21:10:19 -0400 Subject: [PATCH 2/3] proj1 done --- RoboCat/Src/ChatUser.cpp | 61 +++++++++++++++++++++++++++------------- RoboCat/Src/ChatUser.h | 11 ++++++-- RoboCat/Src/Main.cpp | 38 ++++++------------------- 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/RoboCat/Src/ChatUser.cpp b/RoboCat/Src/ChatUser.cpp index 769e3280..ddcac2a6 100644 --- a/RoboCat/Src/ChatUser.cpp +++ b/RoboCat/Src/ChatUser.cpp @@ -26,10 +26,23 @@ ChatUser::ChatUser() ChatUser::~ChatUser() { + send("©DISCONNECT"); + shutdown(); closeSockets(); + SocketUtil::CleanUp(); } -void ChatUser::initTcpClient(std::string sendPort, std::string recvPort) +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; @@ -45,7 +58,7 @@ void ChatUser::initTcpClient(std::string sendPort, std::string recvPort) // Bind() - "Bind" socket -> tells OS we want to use a specific address - std::string address = StringUtils::Sprintf("127.0.0.1:%s", sendPort.c_str()); + std::string address = StringUtils::Sprintf("127.0.0.1:%s", SEND_PORT.c_str()); SocketAddressPtr clientAddress = SocketAddressFactory::CreateIPv4FromString(address.c_str()); if (clientAddress == nullptr) { @@ -64,7 +77,7 @@ void ChatUser::initTcpClient(std::string sendPort, std::string recvPort) // Connect() -> Connect socket to remote host - SocketAddressPtr servAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:"+recvPort); + SocketAddressPtr servAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:"+RECV_PORT); if (servAddress == nullptr) { SocketUtil::ReportError("Creating server address"); @@ -78,7 +91,8 @@ void ChatUser::initTcpClient(std::string sendPort, std::string recvPort) } 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! @@ -99,15 +113,10 @@ void ChatUser::initTcpClient(std::string sendPort, std::string recvPort) recv(receivedMsg); } }); - - std::cout << "Press enter to exit at any time!\n"; - std::cin.get(); - quit = true; -// recvConnSocket->~TCPSocket(); // Forcibly close socket (shouldn't call destructors like this -- make a new function for it! receiveNewThread.join(); } -void ChatUser::initTcpServer(std::string listenPort) +void ChatUser::initTcpServer() { sendRecvFlag = 1; // Create socket @@ -124,7 +133,7 @@ void ChatUser::initTcpServer(std::string listenPort) // Bind() - "Bind" socket -> tells OS we want to use a specific address - SocketAddressPtr listenAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:"+ listenPort); + SocketAddressPtr listenAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:"+ RECV_PORT); if (listenAddress == nullptr) { SocketUtil::ReportError("Creating listen address"); @@ -163,7 +172,8 @@ void ChatUser::initTcpServer(std::string listenPort) // SocketUtil::ReportError("Accepting connection"); // ExitProcess(1); } - + hasConnected = true; + send("©CONNECT"); LOG("Accepted connection from %s", incomingAddress.ToString().c_str()); quit = false; @@ -187,23 +197,34 @@ void ChatUser::initTcpServer(std::string listenPort) } }); - std::cout << "Press enter to exit at any time!\n"; - std::cin.get(); - quit = true; -// recvConnSocket->~TCPSocket(); // Forcibly close socket (shouldn't call destructors like this -- make a new function for it! receiveThread.join(); } void ChatUser::send(std::string msg) { - std::string toSend = username + "¦" + msg; - (sendRecvFlag == 0 ? sendSocket : recvConnSocket)->Send(toSend.c_str(), toSend.length()); + 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) { - std::vector str = split(msg, "¦"); - win.Write("[" + str[0] + "]: " + str[1] + "\n"); + 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() diff --git a/RoboCat/Src/ChatUser.h b/RoboCat/Src/ChatUser.h index f6a17199..324a8455 100644 --- a/RoboCat/Src/ChatUser.h +++ b/RoboCat/Src/ChatUser.h @@ -1,6 +1,7 @@ #pragma once #include "RoboCatPCH.h" +#include struct ChatUser @@ -9,14 +10,20 @@ struct ChatUser 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 initTcpClient(std::string sendPort, std::string recvPort); - void initTcpServer(std::string listenPort); + void startTcpThread(bool isClient); + + void initTcpClient(); + void initTcpServer(); void send(std::string msg); void recv(std::string msg); diff --git a/RoboCat/Src/Main.cpp b/RoboCat/Src/Main.cpp index 926a6bb1..79bbebaf 100644 --- a/RoboCat/Src/Main.cpp +++ b/RoboCat/Src/Main.cpp @@ -10,7 +10,7 @@ #if _WIN32 ChatUser* chatter = new ChatUser(); -const std::string SEND_PORT = "7000", RECV_PORT = "8080"; +std::thread tr1, tr2; bool run = true; @@ -35,42 +35,22 @@ int main(int argc, const char** argv) std::cout << "Invalid entry. Please enter 1 or 2: "; std::getline(std::cin, userNumber); } - OutputWindow win1, win2; - if (userNumber == "1") - { - chatter->username = "Chat User 1"; - std::thread c1([&win1]() - { - chatter->initTcpClient(SEND_PORT, RECV_PORT); - }); - } - else - { - chatter->username = "Chat User 2"; - std::thread c2([&win2]() - { - chatter->initTcpServer(RECV_PORT); - }); - } - - + + 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); - if (input != "!EXIT") - { - chatter->win.WriteFromStdin("[" + chatter->username + "]: " + input + "\n"); - chatter->send(input); - } - else - { + + chatter->win.WriteFromStdin("[" + chatter->username + "]: " + input + "\n"); + if (input == "!EXIT") run = false; - } + else + chatter->send(input); } - SocketUtil::CleanUp(); delete chatter; return 0; From 48cbe614cad4f2d2bd945c5c48963da5c2937e22 Mon Sep 17 00:00:00 2001 From: Clementine Hassouni Date: Fri, 29 Apr 2022 21:19:20 -0400 Subject: [PATCH 3/3] test --- RoboCat/Src/ChatUser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RoboCat/Src/ChatUser.cpp b/RoboCat/Src/ChatUser.cpp index ddcac2a6..4bdcc30a 100644 --- a/RoboCat/Src/ChatUser.cpp +++ b/RoboCat/Src/ChatUser.cpp @@ -184,7 +184,7 @@ void ChatUser::initTcpServer() int32_t bytesReceived = recvConnSocket->Receive(buffer, 4096); if (bytesReceived == 0) { - // handle disconnect + // handle disconnec } if (bytesReceived < 0) {