diff --git a/WinChat/WinChat.rc b/WinChat/WinChat.rc
index 6286a4f..f9e0905 100644
Binary files a/WinChat/WinChat.rc and b/WinChat/WinChat.rc differ
diff --git a/WinChat/WinChat.vcxproj b/WinChat/WinChat.vcxproj
index e9ce627..1e4a424 100644
--- a/WinChat/WinChat.vcxproj
+++ b/WinChat/WinChat.vcxproj
@@ -116,6 +116,7 @@
     <ClInclude Include="src\Chat.h" />
     <ClInclude Include="src\pch.h" />
     <ClInclude Include="src\StrConv.h" />
+    <ClInclude Include="src\TSQueue.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="WinChat.rc" />
diff --git a/WinChat/WinChat.vcxproj.filters b/WinChat/WinChat.vcxproj.filters
index 07bc997..961e217 100644
--- a/WinChat/WinChat.vcxproj.filters
+++ b/WinChat/WinChat.vcxproj.filters
@@ -47,6 +47,9 @@
     <ClInclude Include="src\StrConv.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="src\TSQueue.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="WinChat.rc">
diff --git a/WinChat/src/Application.cpp b/WinChat/src/Application.cpp
index 8d7fe87..81e080f 100644
--- a/WinChat/src/Application.cpp
+++ b/WinChat/src/Application.cpp
@@ -3,15 +3,18 @@
 
 #include "../resource.h"
 #include "Chat.h"
+#include "StrConv.h"
 
 //This pragma enables visual styles, which makes dialogs and their controls look modern.
 #pragma comment(linker,"\"/manifestdependency:type='win32' \
 name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
 processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
 
+#define IDT_CHECKINCONN 1
+
 namespace wc {
 	struct MainDlgInput {
-		Application* app;
+		Application* appPtr;
 		int xPos, yPos;
 	};
 
@@ -19,12 +22,15 @@ namespace wc {
 		std::wstring address;
 		std::wstring screenname;
 		int xPos, yPos;
+		SOCKET inSock;
 	};
 
 	Application::Application(std::string& appName, std::string& appVersion)
 		: m_appName(appName.begin(), appName.end())
 		, m_appVersion(appVersion.begin(), appVersion.end())
 		, m_running(true)
+		, m_inSocket(INVALID_SOCKET)
+		, m_inAddress()
 	{
 		std::wcout << std::format(L"{} {}\n", m_appName, m_appVersion);
 
@@ -45,18 +51,18 @@ namespace wc {
 		//First, start a thread to listen for incoming connections
 		std::thread listenThread(&Application::startListen, this);
 
-		//Then, run the main dialog to get needed input
+		//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))) {
-			//Then create a Chat with the collected input
+			//Create a Chat with the collected input or the incoming connection
 			MainDlgOutput* output = reinterpret_cast<MainDlgOutput*>(result);
-			const auto [address, screenname, x, y] = *output;
+			const auto [address, screenname, x, y, inSocket] = *output;
 			delete output;
 
 			{
 				Chat chat(address, screenname);
-				chat.run(x, y);
+				chat.run(x, y, inSocket);
 			}
 
 			//And repeat until the user exits from the main dialog
@@ -81,9 +87,9 @@ namespace wc {
 		unsigned long no = 0;
 		setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&no), sizeof(no));
 
-		if (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0) {
-			MessageBox(nullptr, L"Error: could not bind network socket!", L"Error", MB_ICONERROR);
-			std::exit(1);
+		while (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0) {
+			freeaddrinfo(hostInfo);
+			getaddrinfo("::", "9431", nullptr, &hostInfo);
 		}
 
 		freeaddrinfo(hostInfo);
@@ -106,22 +112,12 @@ namespace wc {
 			if (errorCode == WSAEWOULDBLOCK)
 				continue;
 
-			ioctlsocket(conSock, FIONBIO, &no);
-
 			std::string remoteStr(INET6_ADDRSTRLEN, 0);
 			inet_ntop(AF_INET6, &remoteAddr.sin6_addr, remoteStr.data(), INET6_ADDRSTRLEN);
 			remoteStr.resize(remoteStr.find_first_of('\0', 0));
-			std::cout << std::format("Connected to: {}\n", remoteStr);
 
-			std::string buf(1000, 0);
-			int bytesRecvd = 0;
-			while (bytesRecvd = recv(conSock, buf.data(), 1000, NULL)) {
-				buf.resize(bytesRecvd);
-				std::cout << std::format("Remote: {}\n", buf);
-				buf.resize(1000);
-			}
-
-			closesocket(conSock);
+			m_inAddress = toWideStr(remoteStr);
+			m_inSocket = conSock;
 		}
 
 		closesocket(sock);
@@ -133,9 +129,9 @@ namespace wc {
 		switch (msg) {
 			case WM_INITDIALOG: {
 				MainDlgInput* in = reinterpret_cast<MainDlgInput*>(lParam);
-				auto [appTemp, xPos, yPos] = *in;
+				auto [appPtr, xPos, yPos] = *in;
 				delete in;
-				app = appTemp;
+				app = appPtr;
 
 				SetWindowText(dlg, app->m_appName.c_str());
 				SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
@@ -160,7 +156,7 @@ namespace wc {
 				SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Address"));
 				SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User"));
 
-				RegisterHotKey(dlg, 1, MOD_NOREPEAT, VK_ESCAPE);
+				SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr);
 
 				return TRUE;
 			}
@@ -185,8 +181,7 @@ namespace wc {
 
 						RECT dlgRect = { };
 						GetWindowRect(dlg, &dlgRect);
-						MainDlgOutput* out = new MainDlgOutput{ address, screenname, dlgRect.left, dlgRect.top };
-						EndDialog(dlg, reinterpret_cast<INT_PTR>(out));
+						EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ address, screenname, dlgRect.left, dlgRect.top, INVALID_SOCKET}));
 						return TRUE;
 					}
 
@@ -197,16 +192,26 @@ namespace wc {
 
 					case IDC_BUTTONEXIT:
 					case ID_FILE_EXIT:
+					case IDCANCEL: //Escape key press
 						PostMessage(dlg, WM_CLOSE, NULL, NULL);
 						return TRUE;
 				}
 
 				return FALSE;
 
-			case WM_HOTKEY:
-				if (wParam != 1 || GetForegroundWindow() != dlg)
-					return FALSE;
-				[[fallthrough]];
+			case WM_TIMER:
+				if (wParam == IDT_CHECKINCONN && app->m_inSocket != INVALID_SOCKET) {
+					KillTimer(dlg, IDT_CHECKINCONN);
+					//Ask if to connect and for screen name here
+					RECT dlgRect = { };
+					GetWindowRect(dlg, &dlgRect);
+					EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ app->m_inAddress, L"Temp screen name", dlgRect.left, dlgRect.top, app->m_inSocket }));
+					app->m_inSocket = INVALID_SOCKET;
+					return TRUE;
+				}
+
+				return FALSE;
+
 			case WM_CLOSE:
 				EndDialog(dlg, 0);
 				return TRUE;
diff --git a/WinChat/src/Application.h b/WinChat/src/Application.h
index a0d5c20..08d8f74 100644
--- a/WinChat/src/Application.h
+++ b/WinChat/src/Application.h
@@ -1,5 +1,6 @@
 #pragma once
 #include <string>
+#include <atomic>
 
 #include <Windows.h>
 
@@ -18,5 +19,8 @@ namespace wc {
 		const std::wstring m_appName;
 		const std::wstring m_appVersion;
 		bool m_running;
+
+		std::atomic<SOCKET> m_inSocket;
+		std::wstring m_inAddress;
 	};
 }
\ No newline at end of file
diff --git a/WinChat/src/Chat.cpp b/WinChat/src/Chat.cpp
index 14b08b6..af4685a 100644
--- a/WinChat/src/Chat.cpp
+++ b/WinChat/src/Chat.cpp
@@ -4,11 +4,12 @@
 #include "StrConv.h"
 #include "../resource.h"
 
-#define IDT_CHECKCONN 1
+#define IDT_CHECKCONN 2
+#define IDT_UPDATECHAT 3
 
 namespace wc {
-	struct ConnDlgInput {
-		Chat* chat;
+	struct ChatDlgInput {
+		Chat* chatPtr;
 		int xPos, yPos;
 	};
 
@@ -21,55 +22,81 @@ namespace wc {
 
 	}
 
-	void Chat::run(int xPos, int yPos) {
+	void Chat::run(int xPos, int yPos, SOCKET socket) {
 		//Run net thread to connect and communicate
-		std::thread netThread(&Chat::runNetThread, this);
+		std::thread netThread(&Chat::runNetThread, this, socket);
 
-		//Show connection dialog until net thread connects
-		ConnDlgInput* input = new ConnDlgInput{ this, xPos, yPos };
-		DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCONNECTING), nullptr, reinterpret_cast<DLGPROC>(connDlgProc), reinterpret_cast<LPARAM>(input));
+		//Show connection dialog until net thread connects unless the listen thread already did that
+		ChatDlgInput input = { this, xPos, yPos };
+		if (!m_connected)
+			DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCONNECTING), nullptr, reinterpret_cast<DLGPROC>(connDlgProc), reinterpret_cast<LPARAM>(&input));
 
-		//If we're connected, open the chat window
+		//Either way now, if we're connected, open the chat window
 		if (m_connected)
-			DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCHAT), nullptr, reinterpret_cast<DLGPROC>(chatDlgProc), reinterpret_cast<LPARAM>(this));
+			DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCHAT), nullptr, reinterpret_cast<DLGPROC>(chatDlgProc), reinterpret_cast<LPARAM>(&input));
 
 		netThread.join();
 	}
 
-	void Chat::runNetThread() {
+	void Chat::runNetThread(SOCKET inSocket) {
 		std::this_thread::sleep_for(std::chrono::milliseconds(100));
 
-		addrinfo* destInfo = nullptr;
-		if (getaddrinfo(toStr(m_address).c_str(), "9430", nullptr, &destInfo) != 0) {
-			MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR);
-			m_connectionError = true;
-			return;
-		}
-
-		SOCKET sock = socket(destInfo->ai_family, SOCK_STREAM, NULL);
+		SOCKET sock = inSocket;
 		if (sock == INVALID_SOCKET) {
-			MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
+			addrinfo* destInfo = nullptr;
+			if (getaddrinfo(toStr(m_address).c_str(), "9430", nullptr, &destInfo) != 0) {
+				MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR);
+				m_connectionError = true;
+				return;
+			}
+
+			sock = socket(destInfo->ai_family, SOCK_STREAM, NULL);
+			if (sock == INVALID_SOCKET) {
+				MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
+				freeaddrinfo(destInfo);
+				m_connectionError = true;
+				return;
+			}
+
+			if (connect(sock, destInfo->ai_addr, static_cast<int>(destInfo->ai_addrlen)) != 0) {
+				std::wstring errorStr = getErrorString();
+				MessageBox(nullptr, std::format(L"Error: Could not connect - {}", errorStr).c_str(), L"Error", MB_ICONERROR);
+				freeaddrinfo(destInfo);
+				closesocket(sock);
+				m_connectionError = true;
+				return;
+			}
+
 			freeaddrinfo(destInfo);
-			m_connectionError = true;
-			return;
 		}
 
-		if (connect(sock, destInfo->ai_addr, static_cast<int>(destInfo->ai_addrlen)) != 0) {
-			std::wstring errorStr = getErrorString();
-			MessageBox(nullptr, std::format(L"Error: Could not connect - {}", errorStr).c_str(), L"Error", MB_ICONERROR);
-			freeaddrinfo(destInfo);
-			closesocket(sock);
-			m_connectionError = true;
-			return;
-		}
-
-		freeaddrinfo(destInfo);
+		unsigned long yes = 1;
+		ioctlsocket(sock, FIONBIO, &yes);
 		m_connected = true;
 
-		std::string data;
-		while (data != "end") {
-			std::getline(std::cin, data);
-			send(sock, data.data(), static_cast<int>(data.size()), NULL);
+		std::wstring recvBuffer(2000, 0);
+		std::wstring sendStr;
+		int bytesRecvd = 0;
+		while (m_connected) {
+			while (!m_sendQueue.empty()) {
+				sendStr = m_sendQueue.pop();
+				send(sock, reinterpret_cast<const char*>(sendStr.c_str()), static_cast<int>(sendStr.size()) * sizeof(wchar_t), NULL);
+			}
+
+			bytesRecvd = recv(sock, reinterpret_cast<char*>(recvBuffer.data()), 2000, NULL);
+
+			if (!bytesRecvd) {
+				m_connected = false;
+				break;
+			}
+
+			if (WSAGetLastError() != WSAEWOULDBLOCK) {
+				recvBuffer.resize(bytesRecvd / 2);
+				m_recvQueue.push(recvBuffer);
+				recvBuffer.assign(2000, 0);
+			}
+
+			std::this_thread::sleep_for(std::chrono::milliseconds(10));
 		}
 
 		closesocket(sock);
@@ -109,11 +136,10 @@ namespace wc {
 
 		switch (msg) {
 			case WM_INITDIALOG: {
-				ConnDlgInput* in = reinterpret_cast<ConnDlgInput*>(lParam);
-				auto [chatTemp, x, y] = *in;
-				delete in;
-				chat = chatTemp;
-				SetWindowPos(dlg, nullptr, x + 100, y + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+				ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
+				chat = in->chatPtr;
+
+				SetWindowPos(dlg, nullptr, in->xPos + 100, in->yPos + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
 				SetDlgItemText(dlg, IDC_STATICADDRESS, chat->m_address.c_str());
 				SendDlgItemMessage(dlg, IDC_PROGRESS, PBM_SETMARQUEE, TRUE, NULL);
 				SetTimer(dlg, IDT_CHECKCONN, 100, nullptr);
@@ -135,20 +161,93 @@ namespace wc {
 		static Chat* chat = nullptr;
 
 		switch (msg) {
-			case WM_INITDIALOG:
-				chat = reinterpret_cast<Chat*>(lParam);
+			case WM_INITDIALOG: {
+				ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
+				chat = in->chatPtr;
+
 				SetWindowText(dlg, std::format(L"remote screenname at {} - WinChat", chat->m_address).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"));
+
+				SetDlgItemText(dlg, IDC_EDITCHATDISPLAY, std::format(L"Connected to remote screenname at {}", chat->m_address).c_str());
+
+				SetTimer(dlg, IDT_UPDATECHAT, 100, nullptr);
+
 				return TRUE;
+			}
+
+			case WM_COMMAND:
+				switch (LOWORD(wParam)) {
+					case IDC_BUTTONSEND: {
+						int inputLength = GetWindowTextLength(GetDlgItem(dlg, IDC_EDITCHATINPUT));
+						if (!inputLength) {
+							EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"Enter your message here." };
+							SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
+							return TRUE;
+						}
+
+						std::wstring buffer(inputLength, 0);
+						GetDlgItemText(dlg, IDC_EDITCHATINPUT, buffer.data(), static_cast<int>(buffer.size() + 1));
+						chat->m_sendQueue.push(buffer);
+
+						chat->addMessage(dlg, buffer);
+
+						SetDlgItemText(dlg, IDC_EDITCHATINPUT, L"");
+
+						return TRUE;
+					}
+
+					case IDC_BUTTONDISCONNECT:
+					case IDCANCEL:
+						PostMessage(dlg, WM_CLOSE, NULL, NULL);
+						return TRUE;
+				}
+
+				return FALSE;
+
+			case WM_TIMER:
+				if (wParam == IDT_UPDATECHAT) {
+					if (!chat->m_connected)
+						EndDialog(dlg, 0);
+
+					std::wstring remoteStr;
+					while (!chat->m_recvQueue.empty()) {
+						remoteStr = chat->m_recvQueue.pop();
+						chat->addMessage(dlg, remoteStr);
+					}
+
+					return TRUE;
+				}
+
+				return FALSE;
 
 			case WM_CLOSE:
-				EndDialog(dlg, 0);
+				chat->m_connected = false;
 				return TRUE;
 		}
 
 		return FALSE;
 	}
 
+	void Chat::addMessage(HWND dlg, std::wstring& in) {
+		SCROLLINFO si = { .cbSize = sizeof(si), .fMask = SIF_POS | SIF_PAGE | SIF_RANGE };
+		GetScrollInfo(GetDlgItem(dlg, IDC_EDITCHATDISPLAY), SB_VERT, &si);
+		bool shouldScroll = static_cast<int>(si.nPage) + si.nPos > si.nMax;
+
+		std::wstring buffer(GetWindowTextLength(GetDlgItem(dlg, IDC_EDITCHATDISPLAY)), 0);
+		GetDlgItemText(dlg, IDC_EDITCHATDISPLAY, buffer.data(), static_cast<int>(buffer.size() + 1));
+		buffer += std::format(L"\r\n{}", in);
+		SetDlgItemText(dlg, IDC_EDITCHATDISPLAY, buffer.c_str());
+
+		if (shouldScroll) {
+			SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_SETSEL, 0, -1);
+			SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_SETSEL, -1, -1);
+			SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_SCROLLCARET, 0, 0);
+		}
+	}
 
 	Chat::~Chat() {
 
diff --git a/WinChat/src/Chat.h b/WinChat/src/Chat.h
index 9a8d74b..4763f2e 100644
--- a/WinChat/src/Chat.h
+++ b/WinChat/src/Chat.h
@@ -3,23 +3,29 @@
 
 #include <Windows.h>
 
+#include "TSQueue.h"
+
 namespace wc {
 	class Chat {
 	public:
 		Chat(std::wstring address, std::wstring screenname);
 
-		void run(int xPos, int yPos);
+		void run(int xPos, int yPos, SOCKET socket);
 
 		~Chat();
 	private:
-		void runNetThread();
+		void runNetThread(SOCKET inSocket);
 		std::wstring getErrorString();
 		static BOOL CALLBACK connDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
 		static BOOL CALLBACK chatDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
+		void addMessage(HWND dlg, std::wstring& str);
 
 		const std::wstring m_address;
 		const std::wstring m_screenname;
 		bool m_connected;
 		bool m_connectionError;
+
+		TSQueue<std::wstring> m_sendQueue;
+		TSQueue<std::wstring> m_recvQueue;
 	};
 }
\ No newline at end of file
diff --git a/WinChat/src/TSQueue.h b/WinChat/src/TSQueue.h
new file mode 100644
index 0000000..cf00c0b
--- /dev/null
+++ b/WinChat/src/TSQueue.h
@@ -0,0 +1,38 @@
+#pragma once
+#include <mutex>
+#include <queue>
+
+namespace wc {
+	template<typename T>
+	class TSQueue {
+	public:
+		TSQueue()
+			: m_m()
+			, m_q()
+		{
+
+		}
+
+		void push(T t) {
+			std::lock_guard lock(m_m);
+			m_q.push(t);
+		}
+
+		T pop() {
+			std::lock_guard lock(m_m);
+			T t = std::move(m_q.front());
+			m_q.pop();
+			return t;
+		}
+
+		bool empty() {
+			std::lock_guard lock(m_m);
+			return m_q.empty();
+		}
+
+		~TSQueue() { }
+	private:
+		std::mutex m_m;
+		std::queue<T> m_q;
+	};
+}
\ No newline at end of file
diff --git a/WinChat/src/pch.h b/WinChat/src/pch.h
index b6e077a..4999a58 100644
--- a/WinChat/src/pch.h
+++ b/WinChat/src/pch.h
@@ -4,6 +4,9 @@
 #include <iostream>
 #include <format>
 #include <thread>
+#include <mutex>
+#include <atomic>
+#include <queue>
 
 //Windows
 #define WIN32_LEAN_AND_MEAN