461 lines
16 KiB
C++
461 lines
16 KiB
C++
// websocket
|
|
#include <boost/beast/core.hpp>
|
|
#include <boost/beast/websocket.hpp>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <functional>
|
|
|
|
// json
|
|
#include <nlohmann/json.hpp>
|
|
|
|
// windows child process
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <tchar.h>
|
|
|
|
// windows terminate process
|
|
#include <tlhelp32.h>
|
|
|
|
//hash
|
|
#include <boost/functional/hash.hpp>
|
|
|
|
// time
|
|
#include <chrono>
|
|
#include <ctime>
|
|
|
|
// fixed int
|
|
#include <cstdint>
|
|
|
|
// user
|
|
#include "user.h"
|
|
#include <vector>
|
|
|
|
// session
|
|
#include "sessionManager.h"
|
|
|
|
namespace beast = boost::beast;
|
|
namespace http = beast::http;
|
|
namespace websocket = beast::websocket;
|
|
namespace net = boost::asio;
|
|
using tcp = boost::asio::ip::tcp;
|
|
|
|
|
|
void EndProc(DWORD procId)
|
|
{
|
|
PROCESSENTRY32 pe;
|
|
|
|
memset(&pe, 0, sizeof(PROCESSENTRY32));
|
|
pe.dwSize = sizeof(PROCESSENTRY32);
|
|
|
|
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
|
|
if (::Process32First(hSnap, &pe))
|
|
{
|
|
BOOL bContinue = TRUE;
|
|
|
|
// kill child processes
|
|
while (bContinue)
|
|
{
|
|
// only kill child processes
|
|
if (pe.th32ParentProcessID == procId)
|
|
{
|
|
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
|
|
|
if (hChildProc)
|
|
{
|
|
::TerminateProcess(hChildProc, 1);
|
|
::CloseHandle(hChildProc);
|
|
}
|
|
}
|
|
|
|
bContinue = ::Process32Next(hSnap, &pe);
|
|
}
|
|
|
|
// kill the main process
|
|
HANDLE hProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
|
|
|
|
if (hProc)
|
|
{
|
|
::TerminateProcess(hProc, 1);
|
|
::CloseHandle(hProc);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CreateProc(
|
|
std::string path,
|
|
std::string args,
|
|
PROCESS_INFORMATION* processInfo
|
|
)
|
|
{
|
|
std::wstring path_ws = std::wstring(path.begin(), path.end());
|
|
const wchar_t* path_cw = path_ws.c_str();
|
|
|
|
std::wstring cmd_ws = std::wstring(args.begin(), args.end());
|
|
wchar_t* cmd_w = const_cast<wchar_t*>(cmd_ws.c_str());
|
|
|
|
// creating process
|
|
STARTUPINFO startupInfo = { sizeof(startupInfo) };
|
|
if (!CreateProcess(path_cw, cmd_w, NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, processInfo))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
uint32_t hash6(T arg)
|
|
{
|
|
boost::hash<T> h;
|
|
return h(arg) % 1000000;
|
|
}
|
|
|
|
LRESULT CALLBACK GLWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (msg == WM_DESTROY) PostQuitMessage(0);
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
// disable console input
|
|
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
DWORD prev_mode;
|
|
GetConsoleMode(hInput, &prev_mode);
|
|
SetConsoleMode(hInput, ENABLE_EXTENDED_FLAGS |
|
|
(prev_mode & ~ENABLE_QUICK_EDIT_MODE));
|
|
|
|
|
|
|
|
|
|
// args
|
|
// 1 - copies amount
|
|
// 2 - path_to_node.js
|
|
// 3 - start_http_port
|
|
// 4 - start_streamer_port
|
|
|
|
// 5 - path_to_application
|
|
// 6 - application_args (without -pixelStreamingPort cos its argv[3])
|
|
|
|
// sessions
|
|
uint16_t sessionLimit = std::atoi(argv[1]);
|
|
sessionManager sessManager;
|
|
sessManager.setlimit(sessionLimit);
|
|
|
|
// merging app arguments
|
|
std::string appPath = argv[5];
|
|
std::string appArgs;
|
|
for (int i = 6; i < argc; ++i)
|
|
appArgs += argv[i] + std::string(" ");
|
|
|
|
appArgs += "-PixelStreamingPort=";
|
|
|
|
// first app ports
|
|
uint16_t portHttp = std::atoi(argv[3]);
|
|
uint16_t portStream = std::atoi(argv[4]);
|
|
|
|
// node js
|
|
/*std::string nodePath = "C:\\Program Files\\nodejs\\node.exe";
|
|
std::string nodeFilePath = argv[2];*/
|
|
|
|
// starting node js servers
|
|
for (uint16_t i = 0; i < sessionLimit; ++i)
|
|
{
|
|
system((std::string("start node ") + argv[2] + " " + std::to_string(portHttp + i) + " " + std::to_string(portStream + i)).c_str());
|
|
}
|
|
|
|
// ip and socket init
|
|
std::string ip("192.168.1.115");
|
|
uint16_t sess_port = 13002;
|
|
auto const address = net::ip::make_address(ip);
|
|
auto const port = static_cast<unsigned short>(std::atoi("13001"));
|
|
net::io_context ioc{ 1 };
|
|
tcp::acceptor acceptor{ ioc, {address, port} };
|
|
|
|
// stats count
|
|
uint64_t user_n = 0;
|
|
|
|
while (true)
|
|
{
|
|
tcp::socket socket(ioc);
|
|
acceptor.accept(socket);
|
|
|
|
std::thread([
|
|
sock = std::move(socket),
|
|
&sessManager,
|
|
&ip,
|
|
&sess_port,
|
|
&user_n,
|
|
appPath,
|
|
appArgs,
|
|
portStream,
|
|
portHttp
|
|
//nodePath,
|
|
//nodeFilePath
|
|
]() mutable
|
|
{
|
|
std::string user_ip = sock.remote_endpoint().address().to_string();
|
|
uint64_t user_id = std::chrono::system_clock::now().time_since_epoch().count();
|
|
user_id = hash6(user_ip + std::to_string(user_id));
|
|
|
|
uint64_t thisUserNum = ++user_n;
|
|
|
|
std::cout << "user(" << thisUserNum << ") connected (ip: " << user_ip << "), (id: " << user_id << ")" << std::endl;
|
|
websocket::stream<tcp::socket> ws(std::move(const_cast<tcp::socket&>(sock)));
|
|
ws.accept();
|
|
// Set a decorator to change the Server of the
|
|
/*ws.set_option(websocket::stream_base::decorator(
|
|
[](websocket::response_type& res)
|
|
{
|
|
res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-server-sync");
|
|
}));*/
|
|
|
|
//session sessTmp;
|
|
session* sessCur = nullptr;
|
|
uint32_t sessId = 0;
|
|
uint16_t sessMinPort = sessManager.getMinPort();
|
|
uint16_t sessHttpPort = sessMinPort ? sessMinPort + 1 : portHttp;
|
|
uint16_t sessStreamPort = portStream + (sessHttpPort - portHttp);
|
|
|
|
//std::cout << "minPort: " << sessMinPort << std::endl;
|
|
|
|
//std::cout << "httpPortDefault: " << portHttp << std::endl
|
|
// << "streamPortDefault: " << portStream << std::endl;
|
|
|
|
//std::cout << "httpPort: " << sessHttpPort << std::endl
|
|
// << "streamPort: " << sessStreamPort << std::endl;
|
|
|
|
|
|
nlohmann::json jsonData;
|
|
std::string message;
|
|
std::string content;
|
|
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
// This buffer will hold the incoming message
|
|
// buffer types https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/using_io/buffer_types.html
|
|
// check for the best one
|
|
// beast::multi_buffer buffer;
|
|
beast::flat_buffer buffer;
|
|
|
|
// Read a message
|
|
ws.read(buffer);
|
|
|
|
// cout buffer to string
|
|
//std::cout << beast::buffers_to_string(buffer.data()) << std::endl;
|
|
|
|
// validate errors
|
|
try
|
|
{
|
|
jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data()));
|
|
message = jsonData.at("message");
|
|
}
|
|
catch (nlohmann::json::parse_error& e)
|
|
{
|
|
std::cout << "Parse error:" << e.what() << std::endl;
|
|
}
|
|
catch (nlohmann::json::out_of_range& e)
|
|
{
|
|
std::cout << "Out of range:" << e.what() << std::endl;
|
|
}
|
|
catch (nlohmann::json::exception& e)
|
|
{
|
|
std::cout << "JSON exception: " << e.what() << std::endl;
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::cout << "Unknown exception" << e.what() << std::endl;
|
|
}
|
|
|
|
|
|
|
|
|
|
// validate message
|
|
if (message == "NEW_USER")
|
|
{
|
|
buffer.clear();
|
|
boost::beast::ostream(buffer) << "connected";
|
|
ws.write(buffer.data());
|
|
|
|
}
|
|
else if (message == "NEW_SESS")
|
|
{
|
|
buffer.clear();
|
|
|
|
sessManager.getMinPort();
|
|
|
|
if (sessManager.isLimitReached())
|
|
{
|
|
boost::beast::ostream(buffer)
|
|
<< std::string("{\"message\" : \"SESS_LIMIT\"}").c_str();
|
|
ws.write(buffer.data());
|
|
continue;
|
|
}
|
|
if (sessManager.existsOwnerIp(user_ip))
|
|
{
|
|
boost::beast::ostream(buffer)
|
|
<< std::string("{\"message\" : \"PERSON_LIMIT\"}").c_str();
|
|
ws.write(buffer.data());
|
|
continue;
|
|
}
|
|
|
|
//sessManager
|
|
|
|
boost::beast::ostream(buffer)
|
|
<< std::string("{\"message\" : \"CREATING_SESSION\", \"content\" : \"true\"}").c_str();
|
|
ws.write(buffer.data());
|
|
|
|
// creating process
|
|
PROCESS_INFORMATION appProcInfo;
|
|
//PROCESS_INFORMATION nodeProcInfo;
|
|
|
|
// create app process
|
|
if (!CreateProc(appPath, appPath + " " + appArgs + std::to_string(sessStreamPort), &appProcInfo))
|
|
{
|
|
std::cout << "CreateProcess failed (" << GetLastError() << ")." << std::endl;
|
|
continue;
|
|
}
|
|
|
|
// bind app process close listen
|
|
std::thread([appProcInfo]()
|
|
{
|
|
WaitForSingleObject(appProcInfo.hProcess, INFINITE);
|
|
CloseHandle(appProcInfo.hProcess);
|
|
CloseHandle(appProcInfo.hThread);
|
|
}).detach();
|
|
|
|
//std::string nodeArgs = nodePath + " " + nodeFilePath + " "
|
|
// + std::to_string(sessHttpPort) + " "
|
|
// + std::to_string(sessStreamPort);
|
|
|
|
//// create node js server proc
|
|
//if (!CreateProc(nodePath, nodePath + " " + nodeFilePath + " "
|
|
// + std::to_string(sessHttpPort) + " "
|
|
// + std::to_string(sessStreamPort), &nodeProcInfo))
|
|
//{
|
|
// std::cout << "CreateProcess failed (" << GetLastError() << ")." << std::endl;
|
|
// continue;
|
|
//}
|
|
|
|
////bind node js close listen
|
|
//std::thread([nodeProcInfo]()
|
|
// {
|
|
// WaitForSingleObject(nodeProcInfo.hProcess, INFINITE);
|
|
// CloseHandle(nodeProcInfo.hProcess);
|
|
// CloseHandle(nodeProcInfo.hThread);
|
|
// }).detach();
|
|
|
|
sessId = hash6(user_id);
|
|
|
|
// create session
|
|
sessManager.add(
|
|
session(
|
|
sessId,
|
|
ip + std::to_string(sess_port),
|
|
user_ip,
|
|
sessHttpPort
|
|
));
|
|
|
|
if (sessManager.getById(sessId, &sessCur))
|
|
{
|
|
// add user
|
|
sessCur->addUser(user(user_id));
|
|
|
|
// add process ids
|
|
sessCur->addProcId(GetProcessId(appProcInfo.hProcess));
|
|
//sessCur->addProcId(GetProcessId(nodeProcInfo.hProcess));
|
|
}
|
|
|
|
std::cout << "user(" << thisUserNum << ") created session (id: " << sessId << ")" << std::endl;
|
|
|
|
// send respond
|
|
buffer.clear();
|
|
boost::beast::ostream(buffer)
|
|
<< (std::string("{\"message\" : \"SESS_CREATION\", \"id\" : \"")
|
|
+ std::to_string(sessId) + "\", \"port\" : \"" + std::to_string(sessHttpPort) + "\"}");
|
|
ws.write(buffer.data());
|
|
}
|
|
else if (message == "SESS_CONNECT")
|
|
{
|
|
std::cout << "sess_connect" << std::endl;
|
|
// read session id
|
|
try
|
|
{
|
|
jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data()));
|
|
content = jsonData.at("content");
|
|
}
|
|
catch (nlohmann::json::parse_error& e)
|
|
{
|
|
std::cout << "Parse error:" << e.what() << std::endl;
|
|
}
|
|
catch (nlohmann::json::out_of_range& e)
|
|
{
|
|
std::cout << "Out of range:" << e.what() << std::endl;
|
|
}
|
|
catch (nlohmann::json::exception& e)
|
|
{
|
|
std::cout << "JSON exception: " << e.what() << std::endl;
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::cout << "Unknown exception" << e.what() << std::endl;
|
|
}
|
|
|
|
// connect to session
|
|
if (sessManager.getById(atoi(content.c_str()), &sessCur))
|
|
{
|
|
sessCur->addUser(user(user_id));
|
|
std::cout << "user(" << thisUserNum << ") connected to session (id: " << sessCur->getId()
|
|
<< "), (users: " << sessCur->getUsersCount() << ")" << std::endl;
|
|
|
|
buffer.clear();
|
|
boost::beast::ostream(buffer)
|
|
<< (std::string("{\"message\" : \"SESS_CONNECT\", \"content\" : \"") + std::to_string(sessCur->getPort()) + "\"}");
|
|
ws.write(buffer.data());
|
|
}
|
|
else
|
|
{
|
|
std::cout << "no session (id: " << content << ")" << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Echo the message back
|
|
// ws.text(ws.got_text());
|
|
// boost::beast::ostream(buffer) << "something";
|
|
// ws.write(buffer.data());
|
|
|
|
}
|
|
catch (beast::system_error const& se)
|
|
{
|
|
std::cout << "user(" << thisUserNum << ") disconnected" << std::endl;
|
|
if (sessCur)
|
|
{
|
|
sessCur->removeUserById(user_id);
|
|
|
|
if (!sessCur->getUsersCount())
|
|
{
|
|
std::cout << "destroy session (id: " << sessCur->getId() << ")" << std::endl;
|
|
std::vector<DWORD> procIds(sessCur->getProcIds());
|
|
for (auto& id : procIds)
|
|
EndProc(id);
|
|
sessManager.remove(sessId);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
|
|
/* if (se.code() != websocket::error::closed)
|
|
{
|
|
std::cerr << "Error: " << se.code().message() << std::endl;
|
|
break;
|
|
}*/
|
|
}
|
|
}
|
|
}).detach();
|
|
}
|
|
return 0;
|
|
} |