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_BUTTONDISCONNECT 1015
#define IDC_STATICREMOTEINFO 1017
#define IDC_STATICLISTENPORT 1018
#define ID_FILE_EXIT 40001
#define ID_HELP_ABOUT 40002
#define ID_HELP_QUCKSTART 40003
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 40003
#define _APS_NEXT_CONTROL_VALUE 1018
#define _APS_NEXT_COMMAND_VALUE 40004
#define _APS_NEXT_CONTROL_VALUE 1019
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -20,17 +20,19 @@ namespace wc {
struct MainDlgOutput {
std::wstring address;
std::wstring port;
std::wstring screenname;
int xPos, yPos;
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_appVersion(appVersion.begin(), appVersion.end())
, m_running(true)
, m_defaultPort(defaultPort)
, m_listenPort()
, m_running(false)
, m_inSocket(INVALID_SOCKET)
, m_inAddress()
{
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
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
INT_PTR result = NULL;
MainDlgInput* input = new MainDlgInput{ this, -1, -1 };
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
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;
{
Chat chat(address, screenname);
Chat chat(address, port, screenname);
chat.run(x, y, inSocket);
}
@ -74,9 +80,6 @@ namespace wc {
}
void Application::startListen() {
addrinfo* hostInfo = nullptr;
getaddrinfo("::", "9430", nullptr, &hostInfo);
SOCKET sock = socket(AF_INET6, SOCK_STREAM, NULL);
if (sock == INVALID_SOCKET) {
MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
@ -87,16 +90,23 @@ namespace wc {
unsigned long no = 0;
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) {
freeaddrinfo(hostInfo);
getaddrinfo("::", "9431", nullptr, &hostInfo);
}
int tryPort = m_defaultPort - 1;
addrinfo* hostInfo = nullptr;
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);
listen(sock, 1);
ioctlsocket(sock, FIONBIO, &yes);
m_running = true;
sockaddr_in6 remoteAddr = { };
int remoteSize = sizeof(remoteAddr);
SOCKET conSock = INVALID_SOCKET;
@ -160,8 +170,9 @@ namespace wc {
HFONT font = CreateFontIndirect(&lFont);
SendMessage(GetDlgItem(dlg, IDC_STATICTITLE), WM_SETFONT, reinterpret_cast<LPARAM>(font), NULL);
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"));
SetDlgItemText(dlg, IDC_STATICLISTENPORT, std::format(L"Listening on port {}", app->m_listenPort).c_str());
SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr);
@ -186,17 +197,43 @@ namespace wc {
screenname.resize(screennameSize);
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 = { };
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;
}
case ID_HELP_ABOUT: {
MessageBox(dlg, std::format(L"{} {}\nGrayson Riffe 2023", app->m_appName, app->m_appVersion).c_str(), L"About", MB_OK);
case ID_HELP_QUCKSTART: {
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;
}
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 ID_FILE_EXIT:
case IDCANCEL: //Escape key press
@ -225,7 +262,7 @@ namespace wc {
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;
return TRUE;
}
@ -269,7 +306,7 @@ namespace wc {
std::wstring buffer(inputLength, 0);
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;
}

View File

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

View File

@ -13,8 +13,9 @@ namespace wc {
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_port(port)
, m_screenname(screenname)
, m_remoteScreenname(1000, 0)
, m_connected(false)
@ -45,7 +46,7 @@ namespace wc {
SOCKET sock = inSocket;
if (sock == INVALID_SOCKET) {
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);
m_connectionError = true;
return;
@ -128,7 +129,7 @@ namespace wc {
break;
case WSAECONNREFUSED:
out = L"Connection refused (WinChat may not be running on remote host)";
out = L"Connection refused";
break;
case WSAENETUNREACH:
@ -158,7 +159,7 @@ namespace wc {
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);
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);
SetTimer(dlg, IDT_CHECKCONN, 100, nullptr);
@ -184,7 +185,7 @@ namespace wc {
ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
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))));
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"));
@ -210,7 +211,7 @@ namespace wc {
GetDlgItemText(dlg, IDC_EDITCHATINPUT, buffer.data(), static_cast<int>(buffer.size() + 1));
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"");
@ -233,7 +234,7 @@ namespace wc {
std::wstring remoteStr;
while (!chat->m_recvQueue.empty()) {
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;

View File

@ -8,7 +8,7 @@
namespace wc {
class Chat {
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);
@ -21,6 +21,7 @@ namespace wc {
void addMessage(HWND dlg, std::wstring str);
const std::wstring m_address;
const std::wstring m_port;
const std::wstring m_screenname;
std::wstring m_remoteScreenname;
bool m_connected;

View File

@ -3,13 +3,12 @@
#include "Application.h"
#define APPNAME "WinChat"
#define DEFAULTPORT 9430
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();
}