Connect to any port and add quick start

This commit is contained in:
Grayson Riffe 2023-10-09 13:50:17 -05:00
parent d232437f08
commit 8f66edfa04
7 changed files with 74 additions and 32 deletions

Binary file not shown.

View File

@ -22,16 +22,18 @@
#define IDC_BUTTONSEND 1014 #define IDC_BUTTONSEND 1014
#define IDC_BUTTONDISCONNECT 1015 #define IDC_BUTTONDISCONNECT 1015
#define IDC_STATICREMOTEINFO 1017 #define IDC_STATICREMOTEINFO 1017
#define IDC_STATICLISTENPORT 1018
#define ID_FILE_EXIT 40001 #define ID_FILE_EXIT 40001
#define ID_HELP_ABOUT 40002 #define ID_HELP_ABOUT 40002
#define ID_HELP_QUCKSTART 40003
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 113 #define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 40003 #define _APS_NEXT_COMMAND_VALUE 40004
#define _APS_NEXT_CONTROL_VALUE 1018 #define _APS_NEXT_CONTROL_VALUE 1019
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View File

@ -20,17 +20,19 @@ namespace wc {
struct MainDlgOutput { struct MainDlgOutput {
std::wstring address; std::wstring address;
std::wstring port;
std::wstring screenname; std::wstring screenname;
int xPos, yPos; int xPos, yPos;
SOCKET inSock; SOCKET inSock;
}; };
Application::Application(std::string& appName, std::string& appVersion) Application::Application(const std::string& appName, const std::string& appVersion, const int defaultPort)
: m_appName(appName.begin(), appName.end()) : m_appName(appName.begin(), appName.end())
, m_appVersion(appVersion.begin(), appVersion.end()) , m_appVersion(appVersion.begin(), appVersion.end())
, m_running(true) , m_defaultPort(defaultPort)
, m_listenPort()
, m_running(false)
, m_inSocket(INVALID_SOCKET) , m_inSocket(INVALID_SOCKET)
, m_inAddress()
{ {
std::wcout << std::format(L"{} {}\n", m_appName, m_appVersion); std::wcout << std::format(L"{} {}\n", m_appName, m_appVersion);
@ -51,17 +53,21 @@ namespace wc {
//First, start a thread to listen for incoming connections //First, start a thread to listen for incoming connections
std::thread listenThread(&Application::startListen, this); std::thread listenThread(&Application::startListen, this);
//And wait until we are successfully listening
while (!m_running)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
//Then, run the main dialog to get needed input for outgoing connections //Then, run the main dialog to get needed input for outgoing connections
INT_PTR result = NULL; INT_PTR result = NULL;
MainDlgInput* input = new MainDlgInput{ this, -1, -1 }; MainDlgInput* input = new MainDlgInput{ this, -1, -1 };
while (result = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(input))) { while (result = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(input))) {
//Create a Chat with the collected input or the incoming connection //Create a Chat with the collected input or the incoming connection
MainDlgOutput* output = reinterpret_cast<MainDlgOutput*>(result); MainDlgOutput* output = reinterpret_cast<MainDlgOutput*>(result);
const auto [address, screenname, x, y, inSocket] = *output; const auto [address, port, screenname, x, y, inSocket] = *output;
delete output; delete output;
{ {
Chat chat(address, screenname); Chat chat(address, port, screenname);
chat.run(x, y, inSocket); chat.run(x, y, inSocket);
} }
@ -74,9 +80,6 @@ namespace wc {
} }
void Application::startListen() { void Application::startListen() {
addrinfo* hostInfo = nullptr;
getaddrinfo("::", "9430", nullptr, &hostInfo);
SOCKET sock = socket(AF_INET6, SOCK_STREAM, NULL); SOCKET sock = socket(AF_INET6, SOCK_STREAM, NULL);
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR); MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
@ -87,16 +90,23 @@ namespace wc {
unsigned long no = 0; unsigned long no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&no), sizeof(no)); setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&no), sizeof(no));
while (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0) { int tryPort = m_defaultPort - 1;
freeaddrinfo(hostInfo); addrinfo* hostInfo = nullptr;
getaddrinfo("::", "9431", nullptr, &hostInfo);
}
do {
freeaddrinfo(hostInfo);
tryPort++;
getaddrinfo("::", std::to_string(tryPort).c_str(), nullptr, &hostInfo);
} while (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0);
m_listenPort = tryPort;
freeaddrinfo(hostInfo); freeaddrinfo(hostInfo);
listen(sock, 1); listen(sock, 1);
ioctlsocket(sock, FIONBIO, &yes); ioctlsocket(sock, FIONBIO, &yes);
m_running = true;
sockaddr_in6 remoteAddr = { }; sockaddr_in6 remoteAddr = { };
int remoteSize = sizeof(remoteAddr); int remoteSize = sizeof(remoteAddr);
SOCKET conSock = INVALID_SOCKET; SOCKET conSock = INVALID_SOCKET;
@ -160,8 +170,9 @@ namespace wc {
HFONT font = CreateFontIndirect(&lFont); HFONT font = CreateFontIndirect(&lFont);
SendMessage(GetDlgItem(dlg, IDC_STATICTITLE), WM_SETFONT, reinterpret_cast<LPARAM>(font), NULL); SendMessage(GetDlgItem(dlg, IDC_STATICTITLE), WM_SETFONT, reinterpret_cast<LPARAM>(font), NULL);
SetDlgItemText(dlg, IDC_STATICDESC, L"A simple Windows chat app"); SetDlgItemText(dlg, IDC_STATICDESC, L"A simple Windows chat app");
SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Address")); SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Address(/Port)"));
SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User")); SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User"));
SetDlgItemText(dlg, IDC_STATICLISTENPORT, std::format(L"Listening on port {}", app->m_listenPort).c_str());
SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr); SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr);
@ -186,17 +197,43 @@ namespace wc {
screenname.resize(screennameSize); screenname.resize(screennameSize);
GetDlgItemText(dlg, IDC_EDITSCREENNAME, screenname.data(), static_cast<int>(screenname.size() + 1)); GetDlgItemText(dlg, IDC_EDITSCREENNAME, screenname.data(), static_cast<int>(screenname.size() + 1));
//Check port if present
std::wstring port;
size_t portStart = address.find(L"/");
if (portStart != std::wstring::npos) {
port = address.substr(portStart + 1);
address.resize(portStart);
bool isANumber = std::all_of(port.begin(), port.end(), [](wchar_t in) {return std::isdigit(in); });
if (port.empty() || !isANumber || std::stoi(port) < 1024 || std::stoi(port) > 49151) {
EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"An invalid port was entered." };
SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
return TRUE;
}
}
else
port = std::to_wstring(app->m_defaultPort);
RECT dlgRect = { }; RECT dlgRect = { };
GetWindowRect(dlg, &dlgRect); GetWindowRect(dlg, &dlgRect);
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ address, screenname, dlgRect.left, dlgRect.top, INVALID_SOCKET })); EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ address, port, screenname, dlgRect.left, dlgRect.top, INVALID_SOCKET }));
return TRUE; return TRUE;
} }
case ID_HELP_ABOUT: { case ID_HELP_QUCKSTART: {
MessageBox(dlg, std::format(L"{} {}\nGrayson Riffe 2023", app->m_appName, app->m_appVersion).c_str(), L"About", MB_OK); std::wstring helpStr = L"To start a chat, start the program on both computers\n"
"and enter the IP address or hostname of one into the other's address box.\n\n"
"If the program is not listening on the default port of {}, the\n"
"other computer must enter this port when connecting to it.";
std::wstring defPortStr = std::to_wstring(app->m_defaultPort);
MessageBox(dlg, std::vformat(helpStr, std::make_wformat_args(defPortStr)).c_str(), L"Quick Start", MB_OK);
return TRUE; return TRUE;
} }
case ID_HELP_ABOUT:
MessageBox(dlg, std::format(L"{} {}\nGrayson Riffe 2023", app->m_appName, app->m_appVersion).c_str(), L"About", MB_OK);
return TRUE;
case IDC_BUTTONEXIT: case IDC_BUTTONEXIT:
case ID_FILE_EXIT: case ID_FILE_EXIT:
case IDCANCEL: //Escape key press case IDCANCEL: //Escape key press
@ -225,7 +262,7 @@ namespace wc {
MainDlgOutput* out = reinterpret_cast<MainDlgOutput*>(result); MainDlgOutput* out = reinterpret_cast<MainDlgOutput*>(result);
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ app->m_inAddress, out->screenname, dlgRect.left, dlgRect.top, tempSock })); EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ app->m_inAddress, std::wstring(), out->screenname, dlgRect.left, dlgRect.top, tempSock }));
delete out; delete out;
return TRUE; return TRUE;
} }
@ -269,7 +306,7 @@ namespace wc {
std::wstring buffer(inputLength, 0); std::wstring buffer(inputLength, 0);
GetDlgItemText(dlg, IDC_EDITSCREENNAME, buffer.data(), static_cast<int>(buffer.size() + 1)); GetDlgItemText(dlg, IDC_EDITSCREENNAME, buffer.data(), static_cast<int>(buffer.size() + 1));
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ std::wstring(), buffer, NULL, NULL, INVALID_SOCKET })); EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ std::wstring(), std::wstring(), buffer, NULL, NULL, INVALID_SOCKET }));
return TRUE; return TRUE;
} }

View File

@ -7,7 +7,7 @@
namespace wc { namespace wc {
class Application { class Application {
public: public:
Application(std::string& appName, std::string& appVersion); Application(const std::string& appName, const std::string& appVersion, const int defaultPort);
void run(); void run();
@ -19,6 +19,8 @@ namespace wc {
const std::wstring m_appName; const std::wstring m_appName;
const std::wstring m_appVersion; const std::wstring m_appVersion;
const int m_defaultPort;
int m_listenPort;
bool m_running; bool m_running;
std::atomic<SOCKET> m_inSocket; std::atomic<SOCKET> m_inSocket;

View File

@ -13,8 +13,9 @@ namespace wc {
int xPos, yPos; int xPos, yPos;
}; };
Chat::Chat(std::wstring address, std::wstring screenname) Chat::Chat(std::wstring address, std::wstring port, std::wstring screenname)
: m_address(address) : m_address(address)
, m_port(port)
, m_screenname(screenname) , m_screenname(screenname)
, m_remoteScreenname(1000, 0) , m_remoteScreenname(1000, 0)
, m_connected(false) , m_connected(false)
@ -45,7 +46,7 @@ namespace wc {
SOCKET sock = inSocket; SOCKET sock = inSocket;
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
addrinfo* destInfo = nullptr; addrinfo* destInfo = nullptr;
if (getaddrinfo(toStr(m_address).c_str(), "9430", nullptr, &destInfo) != 0) { if (getaddrinfo(toStr(m_address).c_str(), toStr(m_port).c_str(), nullptr, &destInfo) != 0) {
MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR); MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR);
m_connectionError = true; m_connectionError = true;
return; return;
@ -128,7 +129,7 @@ namespace wc {
break; break;
case WSAECONNREFUSED: case WSAECONNREFUSED:
out = L"Connection refused (WinChat may not be running on remote host)"; out = L"Connection refused";
break; break;
case WSAENETUNREACH: case WSAENETUNREACH:
@ -158,7 +159,7 @@ namespace wc {
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN)))); SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
SetWindowPos(dlg, nullptr, in->xPos + 100, in->yPos + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER); SetWindowPos(dlg, nullptr, in->xPos + 100, in->yPos + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SetDlgItemText(dlg, IDC_STATICADDRESS, chat->m_address.c_str()); SetDlgItemText(dlg, IDC_STATICADDRESS, std::format(L"{} on port {}", chat->m_address, chat->m_port).c_str());
SendDlgItemMessage(dlg, IDC_PROGRESS, PBM_SETMARQUEE, TRUE, NULL); SendDlgItemMessage(dlg, IDC_PROGRESS, PBM_SETMARQUEE, TRUE, NULL);
SetTimer(dlg, IDT_CHECKCONN, 100, nullptr); SetTimer(dlg, IDT_CHECKCONN, 100, nullptr);
@ -184,7 +185,7 @@ namespace wc {
ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam); ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
chat = in->chatPtr; chat = in->chatPtr;
SetWindowText(dlg, std::format(L"{} - WinChat", chat->m_remoteScreenname).c_str()); SetWindowText(dlg, std::format(L"Chat with {}", chat->m_remoteScreenname).c_str());
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN)))); SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
SetWindowPos(dlg, nullptr, in->xPos - 50, in->yPos - 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER); SetWindowPos(dlg, nullptr, in->xPos - 50, in->yPos - 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Message")); SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Message"));
@ -210,7 +211,7 @@ namespace wc {
GetDlgItemText(dlg, IDC_EDITCHATINPUT, buffer.data(), static_cast<int>(buffer.size() + 1)); GetDlgItemText(dlg, IDC_EDITCHATINPUT, buffer.data(), static_cast<int>(buffer.size() + 1));
chat->m_sendQueue.push(buffer); chat->m_sendQueue.push(buffer);
chat->addMessage(dlg, std::format(L"{} - {}", chat->m_screenname, buffer).c_str()); chat->addMessage(dlg, std::format(L"{} - {}", chat->m_screenname, buffer));
SetDlgItemText(dlg, IDC_EDITCHATINPUT, L""); SetDlgItemText(dlg, IDC_EDITCHATINPUT, L"");
@ -233,7 +234,7 @@ namespace wc {
std::wstring remoteStr; std::wstring remoteStr;
while (!chat->m_recvQueue.empty()) { while (!chat->m_recvQueue.empty()) {
remoteStr = chat->m_recvQueue.pop(); remoteStr = chat->m_recvQueue.pop();
chat->addMessage(dlg, std::format(L"{} - {}", chat->m_remoteScreenname, remoteStr).c_str()); chat->addMessage(dlg, std::format(L"{} - {}", chat->m_remoteScreenname, remoteStr));
} }
return TRUE; return TRUE;

View File

@ -8,7 +8,7 @@
namespace wc { namespace wc {
class Chat { class Chat {
public: public:
Chat(std::wstring address, std::wstring screenname); Chat(std::wstring address, std::wstring port, std::wstring screenname);
void run(int xPos, int yPos, SOCKET socket); void run(int xPos, int yPos, SOCKET socket);
@ -21,6 +21,7 @@ namespace wc {
void addMessage(HWND dlg, std::wstring str); void addMessage(HWND dlg, std::wstring str);
const std::wstring m_address; const std::wstring m_address;
const std::wstring m_port;
const std::wstring m_screenname; const std::wstring m_screenname;
std::wstring m_remoteScreenname; std::wstring m_remoteScreenname;
bool m_connected; bool m_connected;

View File

@ -3,13 +3,12 @@
#include "Application.h" #include "Application.h"
#define APPNAME "WinChat" #define APPNAME "WinChat"
#define DEFAULTPORT 9430
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::string appName = APPNAME;
std::string appVer = APPVERSION;
{ {
wc::Application app(appName, appVer); wc::Application app(APPNAME, APPVERSION, DEFAULTPORT);
app.run(); app.run();
} }