C++ Serial port with USB hub - c++

I want to connect a seairl port with C++ using USB hub (USB splliter).
I have a code that works perfectly fine with my COM ports, but when I'm trying to use the USB hub, I get an error says the COM port not available (even though I see it in the device manager).
I there a way to connect a USB hub COM port in C++?
I attached my current code if necessary. Thank you!
SerialPort.hpp:
/*
* Author: Manash Kumar Mandal
* Modified Library introduced in Arduino Playground which does not work
* This works perfectly
* LICENSE: MIT
*/
#pragma once
#define ARDUINO_WAIT_TIME 2000
#define MAX_DATA_LENGTH 1024
#include <windows.h>
#include <iostream>
#include <string>
class SerialPort {
private:
std::string readBuffer;
HANDLE handler;
bool connected;
COMSTAT status;
DWORD errors;
public:
explicit SerialPort(const char* portName, DWORD baudRate = CBR_9600);
~SerialPort();
int readSerialPort(const char* buffer, unsigned int buf_size);
int readSerialPortUntil(std::string* payload, std::string until);
bool writeSerialPort(const char* buffer, unsigned int buf_size);
bool writeSerialPort(std::string payload);
bool isConnected();
void closeSerial();
};
SerialPort.cpp:
/*
* Author: Manash Kumar Mandal
* Modified Library introduced in Arduino Playground which does not work
* This works perfectly
* LICENSE: MIT
*/
#include <windows.h>
#include <iostream>
#include <string>
#include "SerialPort.hpp"
SerialPort::SerialPort(const char* portName, DWORD baudRate) {
this->connected = false;
this->handler = CreateFileA(static_cast<LPCSTR>(portName),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (this->handler == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
std::cerr << "ERROR: Handle was not attached.Reason : " << portName << " not available\n";
} else {
std::cerr << "ERROR!!!\n";
}
} else {
DCB dcbSerialParameters = { 0 };
if (!GetCommState(this->handler, &dcbSerialParameters)) {
std::cerr << "ERROR: Failed to get current serial parameters\n";
} else {
dcbSerialParameters.BaudRate = baudRate;
dcbSerialParameters.ByteSize = 8;
dcbSerialParameters.StopBits = ONESTOPBIT;
dcbSerialParameters.Parity = NOPARITY;
dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;
if (!SetCommState(handler, &dcbSerialParameters)) {
std::cout << "ALERT: could not set serial port parameters\n";
} else {
this->connected = true;
PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
SerialPort::~SerialPort() {
if (this->connected) {
this->connected = false;
CloseHandle(this->handler);
}
}
// Reading bytes from serial port to buffer;
// returns read bytes count, or if error occurs, returns 0
int SerialPort::readSerialPort(const char* buffer, unsigned int buf_size) {
DWORD bytesRead{};
unsigned int toRead = 0;
ClearCommError(this->handler, &this->errors, &this->status);
if (this->status.cbInQue > 0) {
if (this->status.cbInQue > buf_size) {
toRead = buf_size;
} else {
toRead = this->status.cbInQue;
}
}
memset((void*)buffer, 0, buf_size);
if (ReadFile(this->handler, (void*)buffer, toRead, &bytesRead, NULL)) {
return bytesRead;
}
return 0;
}
int SerialPort::readSerialPortUntil(std::string* payload, std::string until) {
int untilLen = until.length(), len;
do {
char buffer[MAX_DATA_LENGTH];
int bytesRead = this->readSerialPort(buffer, MAX_DATA_LENGTH);
if (bytesRead == 0) {
int endIndex = this->readBuffer.find(until);
if (endIndex != -1) {
int index = endIndex + untilLen;
*payload = this->readBuffer.substr(0, index);
this->readBuffer = this->readBuffer.substr(index);
}
return bytesRead;
}
this->readBuffer += std::string(buffer);
len = this->readBuffer.length();
} while (this->readBuffer.find(until) == std::string::npos);
int index = this->readBuffer.find(until) + untilLen;
*payload = this->readBuffer.substr(0, index);
this->readBuffer = this->readBuffer.substr(index);
return index;
}
// Sending provided buffer to serial port;
// returns true if succeed, false if not
bool SerialPort::writeSerialPort(const char* buffer, unsigned int buf_size) {
DWORD bytesSend;
if (!WriteFile(this->handler, (void*)buffer, buf_size, &bytesSend, 0)) {
ClearCommError(this->handler, &this->errors, &this->status);
return false;
}
return true;
}
bool SerialPort::writeSerialPort(std::string payload) {
return this->writeSerialPort(payload.c_str(), payload.length());
}
// Checking if serial port is connected
bool SerialPort::isConnected() {
if (!ClearCommError(this->handler, &this->errors, &this->status)) {
this->connected = false;
}
return this->connected;
}
void SerialPort::closeSerial() {
CloseHandle(this->handler);
}

Related

interactive terminal with winsock

I want to Create a terminal which can connect different computer by IP address in windows.
I use CreateProcess because _popen cannot use.
file main.cpp
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <csignal>
#include <cstring>
#include <windows.h>
#include <strsafe.h>
#include <fcntl.h>
#include <io.h>
#include <ctime>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 2048
#define DEFAULT_PORT "7999"
static void ErrorExit(PTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
static const char header[] = "\
HTTP/1.0 200 OK\r\n\
Server: Windows VC++\r\n\
Content-Type: text/html; charset=big-5\r\n\
Access-Control-Allow-Origin: *\r\n\
\r\n";
static int running = true;
void callback(int) {
running = false;
puts("closing");
}
static HANDLE
g_hChildStd_IN_Rd = NULL,
g_hChildStd_IN_Wr = NULL,
g_hChildStd_OUT_Rd = NULL,
g_hChildStd_OUT_Wr = NULL;
#undef TEXT
#define TEXT(a) ((PTSTR)(L##a))
int __cdecl main(void)
{
SECURITY_ATTRIBUTES saAttr = {};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
{
PROCESS_INFORMATION piProcInfo = {};
STARTUPINFO siStartInfo = {};
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
char szCmdline[] = "cmd"; // powershell
if (!CreateProcessA( NULL, szCmdline,
NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo)) {
ErrorExit(TEXT("CreateProcess"));
}
}
signal(SIGINT, callback);
assert(SetConsoleOutputCP(CP_UTF8));
WSADATA wsaData = {};
struct addrinfo* result = NULL;
struct addrinfo hints = {};
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
ErrorExit(TEXT("WSAStartup"));
}
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(NULL, DEFAULT_PORT, &hints, &result) != 0) {
WSACleanup();
ErrorExit(TEXT("getaddrinfo"));
}
SOCKET ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
ErrorExit(TEXT("socket"));
}
if (bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
ErrorExit(TEXT("bind"));
}
freeaddrinfo(result);
if (listen(ListenSocket, 5) == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
ErrorExit(TEXT("listen"));
}
puts("Server ready at http://localhost:" DEFAULT_PORT " [running]");
char recvbuf[DEFAULT_BUFLEN] = {};
while (running) {
SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
ErrorExit(TEXT("accept"));
}
{
struct sockaddr_in sockaddr;
int namelen = sizeof(sockaddr);
if (!getpeername(ClientSocket, (struct sockaddr*)&sockaddr, &namelen))
{
time_t timmer = time(NULL);
printf("%s\t#%s", inet_ntoa((in_addr)(*(in_addr*)&sockaddr.sin_addr.S_un.S_addr)), ctime(&timmer));
}
}
ZeroMemory(recvbuf, DEFAULT_BUFLEN);
intptr_t recvlen = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
if (recvlen == -1) {
fputs("recv error\n", stderr);
goto SH;
}
// fwrite(recvbuf, iResult, 1, stdout);
if (recvbuf[0] == 'G' && recvbuf[1] == 'E' && recvbuf[2] == 'T') { // HTTP GET method
send(ClientSocket, header, sizeof(header) - 1, 0);
DWORD dwRead;
// success on here
if (ReadFile(g_hChildStd_OUT_Rd, recvbuf, DEFAULT_BUFLEN, &dwRead, NULL)) {
send(ClientSocket, recvbuf, (int)dwRead, 0);
}
}
else {
send(ClientSocket, header, sizeof(header) - 1, 0); // HTTP POST method
char *ptr = strstr(recvbuf, "\r\n\r\n");
if (ptr == NULL || ptr[0] == '\0' || ptr[1] == '\0'||ptr[2]=='\0'|| ptr[3] == '\0'|| ptr[4] == '\0') {
fputs("parse error: POST request\n", stderr);
goto SH;
}
ptr += 4;
{
DWORD dwRead=0, dwWritten=0;
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, ptr, (DWORD)((recvlen+recvbuf)-ptr), &dwWritten, NULL);
if (!bSuccess) goto SH;
printf("wrote %lu\n", dwWritten);
// always block on ReadFile
bSuccess = ReadFile(g_hChildStd_OUT_Rd, recvbuf, DEFAULT_BUFLEN, &dwRead, NULL);
printf("read %lu\n", dwRead);
if (!bSuccess || dwRead == 0) goto SH;
send(ClientSocket, ptr, dwRead, 0);
}
}
SH:
if (shutdown(ClientSocket, SD_BOTH) == SOCKET_ERROR) {
puts("shutdown failed");
closesocket(ClientSocket);
WSACleanup();
ErrorExit(TEXT("shutdown"));
}
closesocket(ClientSocket);
}
puts("Exit Server [close]");
WSACleanup();
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
return 0;
}
file index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="big-5">
<title>terminal</title>
</head>
<body>
<style type="text/css">
input {
min-width: 900px;
color: green;
border-width: 0px;
}
span, input, nav {
font-family: Consolas;
font-style: oblique;
}
font{
font-size: 15px;
color: cornflowerblue;
font-family: monospace;
}
</style>
<nav id='version'>waiting for server</nav>
<pre id='shell'>
<input id="new" value="print('Silav gerokê')">
</pre>
<script type="text/javascript">
var _history = [];
var _index = 0;
var shell=document.getElementById('shell');
window.onload=function(){
var x=new XMLHttpRequest();
x.open("GET", "http://127.0.0.1:7999", true);
x.onreadystatechange=function(){
if (x.readyState == 4 && x.status == 200){
document.getElementById('version').innerText=x.response;
document.getElementById('new').focus();
}
}
x.send(null);
}
shell.addEventListener('keydown', function(_){
if(_.keyCode === 13){
_.preventDefault();
var old = document.getElementById('new');
var x=new XMLHttpRequest();
x.open("POST", "http://127.0.0.1:7999", true);
_history.push(old.value);
_index += 1;
x.send(old.value);
x.onreadystatechange=function(){
if (x.readyState == 4 && x.status == 200){
old.removeAttribute('id');
shell.appendChild(document.createElement('br'));
var ne = document.createElement('font');
ne.innerText=x.response;
shell.appendChild(ne);
var input = document.createElement('input');
input.setAttribute('id', 'new');
shell.appendChild(input);
input.focus();
}
}
}else if (_.keyCode==38) {
if (_index){
_index -=1 ;
document.getElementById('new').value=_history[_index];
}
}else if (_.keyCode==40) {
if (_history[_index+1]!=undefined){
_index+=1;
document.getElementById('new').value=_history[_index];
}
}
})
</script>
</body>
</html>
the HTTP GET method works, but POST not.
when I use cmd, block on ReadFile
when I use powershell, server just returns what it reads
it looks like this
What's wrong with my code?
Most Web Terminal use WebSocket for Duplex connection, e.g. Jupyter, WebSSH...
I use IO completion port(IOCP) for reading from child process's pipe, sending text to client, both of them are overlapped operation.
In my question, the pipe was created by CreatePipe, which is synchronous.In my answer, I use named pipe for asynchronous reading.So we don't have to waiting synchronously.
main.cpp
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <mstcpip.h>
#include <crtdbg.h>
#include <wchar.h>
#include <WinInet.h>
#include "Mine.h"
#include "llhttp.h"
#pragma comment(lib, "WS2_32")
#pragma comment(lib, "Mswsock")
#pragma comment(lib, "Wininet")
HANDLE heap, iocp;
#define assert(x) {if (!(x)){printf("error in %s.%d: %s, err=%d\n", __FILE__, __LINE__, #x, WSAGetLastError());}}
#include "types.h"
#include "pipe.cpp"
#include "handshake.cpp"
VOID CloseClient(IOCP* ctx) {
if (ctx->client) {
shutdown(ctx->client, SD_BOTH);
closesocket(ctx->client);
ctx->client = NULL;
ctx->state = State::AfterClose;
#define USEMALLOC 1
#ifdef USEMALLOC
free(ctx);
#else
HeapFree(heap, 0, ctx);
#endif
}
_ASSERT(_CrtCheckMemory());
}
IOCP* initSocket(SOCKET client) {
#ifdef USEMALLOC
IOCP* ctx = (IOCP*)malloc(sizeof(IOCP));
if (ctx == NULL)
return NULL;
ZeroMemory(ctx, sizeof(*ctx));
#else
IOCP* ctx = (IOCP*)HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(IOCP));
if (ctx == NULL)
return NULL;
#endif
ctx->client = client;
HANDLE ret = CreateIoCompletionPort((HANDLE)client, iocp, (ULONG_PTR)ctx, 0);
assert(ret);
_ASSERT(_CrtCheckMemory());
return ctx;
}
#include "frame.cpp"
int http_on_header_field(llhttp_t* parser, const char* at, size_t length) {
Parse_Data* p = (Parse_Data*)parser;
p->length = length;
p->at = at;
return 0;
}
int http_on_header_value(llhttp_t* parser, const char* at, size_t length) {
Parse_Data* p = (Parse_Data*)parser;
p->headers[std::string(p->at, p->length)] = std::string(at, length);
return 0;
}
int http_on_url(llhttp_t* parser, const char* at, size_t length) {
Parse_Data* p = (Parse_Data*)parser;
std::string tmp{ at, length };
DWORD escaped = 1;
char dummy;
HRESULT res = UrlUnescapeA((PSTR)tmp.data(), &dummy, &escaped, 0);
p->uri = (CHAR*)HeapAlloc(heap, 0, escaped + 1);
assert(p->uri);
*(CHAR*)&p->uri[escaped] = '\0';
p->uriLen = escaped;
res = UrlUnescapeA(tmp.data(), (PSTR)p->uri, &escaped, 0);
assert(res == S_OK);
return 0;
}
LPCWSTR encodePath(IOCP* ctx, Parse_Data &parse_data) {
int res = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, parse_data.uri, (int)parse_data.uriLen, (LPWSTR)ctx->buf, sizeof(ctx->buf) / 2);
ctx->state = State::ReadStaticFile;
LPCWSTR file = (LPWSTR)ctx->buf;
if (parse_data.uriLen == 2 && *parse_data.uri == '/') {
file = L"index.html";
}
else {
file += 1;
}
return file;
}
void processRequest(Parse_Data& parse_data, IOCP* ctx, enum llhttp_errno err) {
WSABUF* errBuf = &HTTP_ERR_RESPONCE::internal_server_error;
switch (parse_data.parser.method) {
case llhttp_method::HTTP_GET: {
switch (err) {
case HPE_OK:
{
if (ctx->firstCon) {
ctx->firstCon = false;
auto connection = parse_data.headers.find("Connection");
if (connection != parse_data.headers.end()) {
if (connection->second == "keep-alive" || connection->second == "Keep-Alive") {
ctx->keepalive = true;
DWORD yes = TRUE;
int success = setsockopt(ctx->client, SOL_SOCKET, SO_KEEPALIVE, (char*)&yes, sizeof(yes));
assert(success == 0);
puts("set tcp keep alive(SO_KEEPALIVE)");
auto keepalive = parse_data.headers.find("Keep-Alive");
if (keepalive == parse_data.headers.end())
keepalive = parse_data.headers.find("keep-alive");
if (keepalive != parse_data.headers.end()) {
auto s = keepalive->second.data();
auto timeouts = StrStrA(s, "timeout");
if (timeouts) {
int timeout;
int res = sscanf_s(timeouts + 7, "=%d", &timeout);
if (res > 0) {
printf("set TCP keep alive=%d\n", timeout);
int yes = TRUE;
res = setsockopt(ctx->client, SOL_SOCKET, TCP_KEEPIDLE, (char*)&yes, sizeof yes);
assert(res == 0);
}
else {
puts("Error: failed to parse keepalive seconds...");
}
}
}
}
else {
printf("I got Connection: %s\n", connection->second.data());
}
}
}
auto file = encodePath(ctx, parse_data);
HANDLE hFile = CreateFileW(file,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
errBuf = &HTTP_ERR_RESPONCE::not_found;
goto BAD_REQUEST_AND_RELEASE;
}
HANDLE r = CreateIoCompletionPort(hFile, iocp, (ULONG_PTR)ctx, 0);
assert(r);
const char* mine = getType(file);
ctx->hProcess = hFile;
LARGE_INTEGER fsize{};
BOOL bSuccess = GetFileSizeEx(hFile, &fsize);
assert(bSuccess);
int res = snprintf(ctx->buf, sizeof(ctx->buf),
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %lld\r\n"
"Connection: %s\r\n\r\n", mine, fsize.QuadPart, ctx->keepalive ? "keep-alive" : "close");
assert(res > 0);
ctx->sendBuf->buf = ctx->buf;
ctx->sendBuf->len = (ULONG)res;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
}break;
case HPE_PAUSED_UPGRADE:
{
auto upgrade = parse_data.headers.find("Upgrade");
auto pro = parse_data.headers.find("Sec-WebSocket-Protocol");
if (upgrade != parse_data.headers.end() && pro != parse_data.headers.end()) {
if (upgrade->second == "websocket") {
auto ws_key = parse_data.headers.find("Sec-WebSocket-Key");
if (ws_key != parse_data.headers.end()) {
ctx->state = State::AfterHandShake;
ws_key->second += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char buf[29];
BOOL ret = HashHanshake(ws_key->second.data(), (ULONG)ws_key->second.length(), buf);
assert(ret);
int len;
len = snprintf(ctx->buf, sizeof(ctx->buf),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Protocol: %s\r\n"
"Sec-WebSocket-Accept: %s\r\n\r\n", pro->second.data(), buf);
assert(len > 0);
ctx->process_name = StrDupA(pro->second.data());
ctx->sendBuf[0].buf = ctx->buf;
ctx->sendBuf[0].len = (ULONG)len;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
}
else {
errBuf = &HTTP_ERR_RESPONCE::bad_request;
goto BAD_REQUEST_AND_RELEASE;
}
}
else {
errBuf = &HTTP_ERR_RESPONCE::bad_request;
goto BAD_REQUEST_AND_RELEASE;
}
}
else {
errBuf = &HTTP_ERR_RESPONCE::bad_request;
goto BAD_REQUEST_AND_RELEASE;
}
}break;
DEFAULT_UNREACHABLE;
}
}break;
case llhttp_method::HTTP_HEAD:
{
if (err == llhttp_errno::HPE_OK) {
auto file = encodePath(ctx, parse_data);
HANDLE hFile = CreateFileW(file,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
constexpr const char* msg = "HTTP/1.1 404 Not Found\r\n\r\n";
ctx->sendBuf[0].buf = (CHAR*)msg;
ctx->sendBuf[0].len = cstrlen(msg);
ctx->state = State::AfterSendHTML;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
return;
}
FILETIME ftWrite;
SYSTEMTIME stUTC;
LARGE_INTEGER fsize;
if (GetFileTime(hFile, NULL, NULL, &ftWrite)) {
if (FileTimeToSystemTime(&ftWrite, &stUTC)) {
if (InternetTimeFromSystemTimeA(&stUTC, INTERNET_RFC1123_FORMAT, ctx->sErr.buf, sizeof(ctx->sErr.buf))) {
if (GetFileSizeEx(hFile, &fsize)) {
int len = snprintf(ctx->buf, sizeof(ctx->buf),
"HTTP/1.1 200 OK\r\n" // no "Accept-Ranges: bytes\r\n" now
"Last-Modified: %s\r\n"
"Conetnt-Length: %lld\r\n"
"\r\n", ctx->sErr.buf, fsize.QuadPart);
ctx->sendBuf[0].buf = ctx->buf;
ctx->sendBuf[0].len = len;
ctx->state = State::AfterSendHTML;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
puts("sended");
return;
}
}
}
}
errBuf = &HTTP_ERR_RESPONCE::internal_server_error;
goto BAD_REQUEST_AND_RELEASE;
}
else {
goto BAD_REQUEST_AND_RELEASE;
}
}break;
default:
{
errBuf = &HTTP_ERR_RESPONCE::method_not_allowed;
BAD_REQUEST_AND_RELEASE:
ctx->state = State::AfterSendHTML;
WSASend(ctx->client, errBuf, 1, NULL, 0, &ctx->sendOL, NULL);
}
}
}
DWORD WINAPI WorkerThread(LPVOID WorkThreadContext) {
DWORD dwbytes = 0;
IOCP* ctx = NULL;
OVERLAPPED* ol = NULL;
for (;;) {
_ASSERT(_CrtCheckMemory());
BOOL ret = GetQueuedCompletionStatus((HANDLE)WorkThreadContext, &dwbytes, (PULONG_PTR)&ctx, &ol, INFINITE);
if (ret == FALSE) {
/*
* GetLastError() retrun thread local error
*/
if (ctx) {
int err=GetLastError();
switch (err) {
case ERROR_BROKEN_PIPE:
{
if (ctx->hProcess != NULL) {
*(PWORD)ctx->buf = htons(1000);
DWORD dwExitCode = 0;
const char* msg;
if (GetExitCodeProcess(ctx->hProcess, &dwExitCode)) {
msg = "process exit with code %u";
}
else {
msg = "process was exited(failed to get exit code)";
// ERROR_INVALID_HANDLE on GetExitCodeProcess
}
int n = snprintf(ctx->buf + 2, sizeof(ctx->buf) - 2, msg, dwExitCode);
assert(n > 0);
websocketWrite(ctx, ctx->buf, n + 2, &ctx->sendOL, ctx->sendBuf, Websocket::Opcode::Close);
CloseHandle(ctx->hProcess);
ctx->hProcess = NULL;
}
}break;
case ERROR_HANDLE_EOF:
{
BOOL res = CloseHandle(ctx->hProcess);
assert(res);
if (ctx->keepalive)
{
ctx->state = State::AfterRecv;
ctx->recvBuf[0].buf = ctx->buf;
ctx->recvBuf[0].len = sizeof(ctx->buf);
ctx->recvOL.Offset = ctx->recvOL.OffsetHigh = 0; // reset reading position
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
continue;
}
else {
CloseClient(ctx);
}
}break;
default:
printf("undefined error(GetLastError=%d): I'm going to ignore it\n", err);
}
}
continue;
}
if (ctx == NULL || ol==NULL) {
assert(0);
continue;
}
if (dwbytes == 0) {
CloseClient(ctx);
continue;
}
switch (ctx->state) {
// Accept-Ranges: bytes
case State::AfterRecv:
{
ctx->buf[dwbytes] = '\0';
Parse_Data parse_data{};
enum llhttp_errno err = llhttp_execute(&parse_data.parser, ctx->buf, dwbytes);
if (err != HPE_OK && err != HPE_PAUSED_UPGRADE) {
printf("llhttp_execute error: %s\n", llhttp_errno_name(err));
CloseClient(ctx);
continue;
}
if (parse_data.parser.http_major != 1 || parse_data.parser.http_minor != 1) {
puts("expect HTTP/1.1");
CloseClient(ctx);
continue;
}
printf("%s [%s]\n", llhttp_method_name((llhttp_method)parse_data.parser.method), parse_data.uri);
processRequest(parse_data, ctx, err);
}break; // end case
case State::AfterHandShake:
{
ctx->state = State::WebSocketConnecting;
if (ctx->process_name == NULL) {
CloseClient(ctx);
continue;
}
int size = MultiByteToWideChar(CP_UTF8, 0, ctx->process_name, -1, NULL, 0) * 2;
if (size <= 0) {
assert(0);
CloseClient(ctx);
continue;
}
WCHAR* name = (WCHAR*)HeapAlloc(heap, 0, size+2);
name[size] = L'\0';
size = MultiByteToWideChar(CP_UTF8, 0, ctx->process_name, -1, name, size / 2);
LocalFree(ctx->process_name);
if (size <= 0) {
assert(0);
CloseClient(ctx);
continue;
}
printf("=== spawn process: %ws(%d) ===\n", name, size);
BOOL res = CreateCGI(name, ctx);
HeapFree(heap, 0, name);
if (res == FALSE) {
int n = snprintf(ctx->buf+2, sizeof(ctx->buf)-2, "Error: create process failed, GetLastError()=%d", GetLastError());
*(PWORD)ctx->buf = htons(1000);
assert(n > 0);
websocketWrite(ctx, ctx->buf, n + 2, &ctx->sendOL, ctx->sendBuf, Websocket::Opcode::Close);
continue;
}
ctx->recvBuf[0].buf = ctx->buf;
ctx->recvBuf[0].len = 6;
ctx->dwFlags = MSG_WAITALL;
ctx->Reading6Bytes = true;
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
}break;
case State::ReadStaticFile:
{
(void)ReadFile(ctx->hProcess, ctx->buf, sizeof(ctx->buf), NULL, &ctx->recvOL);
ctx->state = State::SendPartFile;
}break;
case State::SendPartFile:
{
ctx->recvOL.Offset += dwbytes;
ctx->sendBuf->len = dwbytes;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
ctx->state = State::ReadStaticFile;
}break;
case State::WebSocketConnecting: {
if (ol == &ctx->recvOL) {
if (ctx->Reading6Bytes) {
onRead6Complete(ctx);
}
else {
onRecvData(ctx);
}
}
else if (ol == &ctx->sOut.ol) {
websocketWrite(ctx, (CHAR*)ctx->sOut.buf, dwbytes, &ctx->sOut.sendOL, ctx->sOut.wsaBuf);
}
else if (ol == &ctx->sErr.ol) {
websocketWrite(ctx, (CHAR*)ctx->sErr.buf, dwbytes, &ctx->sErr.sendOL, ctx->sErr.wsaBuf);
}
else if (ol == &ctx->sOut.sendOL) {
(void)ReadFile(ctx->sOut.pipe, ctx->sOut.buf, sizeof(ctx->sOut.buf), NULL, &ctx->sOut.ol);
}
else if (ol == &ctx->sErr.sendOL) {
(void)ReadFile(ctx->sErr.pipe, ctx->sErr.buf, sizeof(ctx->sErr.buf), NULL, &ctx->sErr.ol);
}
else if (ol == &ctx->sendOL) {
CloseClient(ctx);
}
}break;
case State::AfterSendHTML: {
CloseClient(ctx);
}break;
case State::AfterClose:
{
assert(0);
}break;
default:
{
assert(0);
}
}
}
}
int main()
{
system("chcp 65001");
{
WSADATA wsaData{};
int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
assert(ret == 0);
}
iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
assert(iocp);
heap = GetProcessHeap();
BOOL ret = initHash();
assert(ret);
sockaddr_in ip4{ .sin_family = AF_INET, .sin_port = htons(80) };
SOCKET server = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (server == INVALID_SOCKET)
return 1;
if (bind(server, (struct sockaddr*)&ip4, sizeof(ip4)) == SOCKET_ERROR)
return 1;
if (listen(server, SOMAXCONN) == SOCKET_ERROR)
return 1;
CreateThread(NULL, 0, WorkerThread, iocp, 0, 0);
puts("server listening at http://localhost/ and ws://localhost/");
for (;;) {
sockaddr_in sIn{};
int sInLen = sizeof(sIn);
_ASSERT(_CrtCheckMemory());
SOCKET client = WSAAccept(server, (sockaddr*)&sIn, &sInLen, NULL, NULL);
IOCP* ctx = initSocket(client);
assert(ctx);
ctx->recvBuf[0].buf = ctx->buf;
ctx->recvBuf[0].len = sizeof(ctx->buf);
ctx->firstCon = true;
WSARecv(client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
}
WSACleanup();
closeHash();
_ASSERT(_CrtCheckMemory());
}
types.cpp
namespace Websocket {
using BIT = BYTE;
enum Opcode : BYTE {
Continuation = 0,
Text = 0x1,
Binary = 0x2,
Close = 0x8,
Ping = 0x9,
Pong = 0xA
};
}
template <ULONG N>
consteval ULONG cstrlen(const char(&)[N]) {
return N - 1;
}
consteval ULONG cstrlen(const char* s) {
ULONG res = 0;
for (; *s; s++) {
res++;
}
return res;
}
enum class State : unsigned __int8 {
AfterRecv, ReadStaticFile, SendPartFile, AfterSendHTML, AfterHandShake, WebSocketConnecting, AfterClose
};
int http_on_header_field(llhttp_t* parser, const char* at, size_t length);
int http_on_header_value(llhttp_t* parser, const char* at, size_t length);
int http_on_url(llhttp_t* parser, const char* at, size_t length);
struct Parse_Data {
Parse_Data() : headers{}, uri{}, at{}, length{}, uriLen{} {
llhttp_settings_init(&settings);
settings.on_url = http_on_url;
settings.on_header_field = http_on_header_field;
settings.on_header_value = http_on_header_value;
llhttp_init(&parser, HTTP_REQUEST, &settings);
};
llhttp_t parser;
std::map<std::string, std::string> headers;
const CHAR* uri;
ULONG uriLen;
size_t length;
const char* at;
llhttp_settings_t settings;
};
struct Stdio {
OVERLAPPED ol, sendOL;
HANDLE pipe;
CHAR buf[100];
WSABUF wsaBuf[2];
};
struct IOCP {
SOCKET client;
State state;
OVERLAPPED recvOL, sendOL;
char buf[4096];
DWORD dwFlags;
WSABUF sendBuf[2], recvBuf[1];
bool Reading6Bytes;
unsigned __int64 payload_len;
BYTE header[4];
struct Stdio sIn, sOut, sErr;
HANDLE hProcess;
Websocket::Opcode op;
char* process_name;
bool keepalive, firstCon;
};
namespace HTTP_ERR_RESPONCE {
static char sinternal_server_error[] = "HTTP/1.1 500 Internal Server Error\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>500 Internal Server Error</title></head><body><h1 align=\"center\">500 Internal Server Error</h1><hr><p style=\"text-align: center\">The server has some error...<p/></body></html>",
snot_found[] =
"HTTP/1.1 404 Not Found\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1 align=\"center\">404 Not Found</h1><hr><p style=\"text-align: center\">The Request File is not found in the server<p/></body></html>",
smethod_not_allowed[] =
"HTTP/1.1 405 Method Not Allowed\r\n"
"Connection: close\r\n"
"Allow: GET, HEAD\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>405 Method Not Allowed</title></head><body><h1 align=\"center\">405 Method Not Allowed</h1><hr><p style=\"text-align: center\">You can only use GET, HEAD request<p/></body></html>",
sbad_request[] =
"HTTP/1.1 400 Bad Request\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>400 Bad Request</title></head><body><h1 align=\"center\">400 Bad Request</h1><hr><p style=\"text-align: center\">Thes server can't process the request<br>Maybe you miss some header(s)<p/></body></html>";
static WSABUF
internal_server_error = {
cstrlen(sinternal_server_error), sinternal_server_error
},
not_found = {
cstrlen(snot_found), snot_found
},
method_not_allowed = {
cstrlen(smethod_not_allowed), smethod_not_allowed
},
bad_request = {
cstrlen(sbad_request), sbad_request
};
}
pipe.cpp
HANDLE newPipe(WCHAR buf[50], DWORD dwOpenMode) {
HANDLE ret = 0;
static __int64 volatile c = 0;
for (;;) {
swprintf(buf, 50, L"\\\\?\\pipe\\child\\%lld-%lld", InterlockedIncrement64(&c), GetTickCount64());
ret = CreateNamedPipeW(
buf,
dwOpenMode,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL);
if (ret != INVALID_HANDLE_VALUE)
return ret;
int err = GetLastError();
if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
printf("CreateNamedPipeW: %d\n", err);
return INVALID_HANDLE_VALUE;
}
}
}
BOOL CreateCGI(WCHAR* cmd, IOCP* ctx) {
{
STARTUPINFOW si{ .cb = sizeof(si), .dwFlags = STARTF_USESTDHANDLES };
{
WCHAR buf[50];
SECURITY_ATTRIBUTES sa{ .nLength = sizeof(SECURITY_ATTRIBUTES), .bInheritHandle = TRUE };
ctx->sOut.pipe = newPipe(buf, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED);
if (ctx->sOut.pipe == INVALID_HANDLE_VALUE) {
return FALSE;
}
si.hStdOutput = CreateFileW(buf, GENERIC_WRITE, 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
return FALSE;
}
ctx->sErr.pipe = newPipe(buf, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED);
if (ctx->sErr.pipe == INVALID_HANDLE_VALUE) {
return FALSE;
}
si.hStdError = CreateFileW(buf, GENERIC_WRITE, 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (si.hStdError == INVALID_HANDLE_VALUE) {
return FALSE;
}
ctx->sIn.pipe = newPipe(buf, PIPE_ACCESS_OUTBOUND);
if (ctx->sIn.pipe == INVALID_HANDLE_VALUE) { return FALSE; }
si.hStdInput = CreateFileW(buf, GENERIC_READ, 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (si.hStdInput == INVALID_HANDLE_VALUE) {
return FALSE;
}
}
PROCESS_INFORMATION pInfo;
if (CreateProcessW(
NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pInfo)==FALSE){
return FALSE;
}
CloseHandle(pInfo.hThread);
ctx->hProcess = pInfo.hProcess;
CloseHandle(si.hStdError);
CloseHandle(si.hStdInput);
CloseHandle(si.hStdOutput);
}
HANDLE hRet;
hRet = CreateIoCompletionPort(ctx->sOut.pipe, iocp, (ULONG_PTR)ctx, 0);
if (hRet == NULL) {
return FALSE;
}
hRet = CreateIoCompletionPort(ctx->sErr.pipe, iocp, (ULONG_PTR)ctx, 0);
if (hRet == NULL) {
return FALSE;
}
(void)ReadFile(ctx->sOut.pipe, ctx->sOut.buf, sizeof(ctx->sOut.buf), NULL, &ctx->sOut.ol);
(void)ReadFile(ctx->sErr.pipe, ctx->sErr.buf, sizeof(ctx->sErr.buf), NULL, &ctx->sErr.ol);
return TRUE;
}
handshake.cpp
#include <bcrypt.h>
#pragma comment (lib, "Crypt32.lib")
#pragma comment (lib, "bcrypt.lib")
static BCRYPT_ALG_HANDLE hAlgorithm=NULL;
static PBYTE hashO = NULL;
static BCRYPT_HASH_HANDLE hHash = NULL;
BOOL initHash(){
DWORD hashLen, dummy;
// we need hash many times
if ((BCryptOpenAlgorithmProvider( &hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_HASH_REUSABLE_FLAG)) < 0)
return FALSE;
if ((BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hashLen, sizeof(hashLen), &dummy, 0)<0))
return FALSE;
hashO = (PBYTE)HeapAlloc(heap, 0, hashLen);
if (NULL == hashO)
{
return FALSE;
}
if ((BCryptCreateHash(
hAlgorithm,
&hHash,
hashO,
hashLen,
NULL,
0,
0))<0)
{
return FALSE;
}
return TRUE;
}
void closeHash(){
if (hAlgorithm)
{
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
if (hHash)
{
BCryptDestroyHash(hHash);
}
if (hashO)
{
HeapFree(heap, 0, hashO);
}
}
BOOL HashHanshake(void* key, ULONG length, char *buf/*29 bytes*/){
BYTE sha1_o[20]{};
// SHA-1 20 bytes hash
if ((BCryptHashData(hHash, (PUCHAR)key, length, 0)) < 0)
return FALSE;
if ((BCryptFinishHash(hHash, sha1_o, sizeof(sha1_o), 0)) < 0)
return FALSE;
DWORD size=29;
// base64 encoding
return CryptBinaryToStringA(sha1_o, sizeof(sha1_o), CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF , buf, &size);
}
frame.cpp
void websocketWrite(IOCP* ctx, const char* msg, ULONG length, OVERLAPPED* ol, WSABUF wsaBuf[2], Websocket::Opcode op = Websocket::Binary) {
ULONG hsize = 2;
ctx->header[0] = 0b10000000 | BYTE(op);
if (length < 126){
ctx->header[1] = (BYTE)length;
}else if (length < 0b10000000000000000){
hsize += 2;
ctx->header[1] = 126;
ctx->header[2] = (BYTE)(length >> 8);
ctx->header[3] = (BYTE)length;
}else{
puts("error: data too long");
return;
}
wsaBuf[0].buf = (char*)ctx->header;
wsaBuf[0].len = hsize;
wsaBuf[1].buf = (char*)msg;
wsaBuf[1].len = length;
WSASend(ctx->client, wsaBuf, 2, NULL, 0, ol, NULL);
_ASSERT(_CrtCheckMemory());
}
void onRecvData(IOCP* ctx) {
PBYTE mask = (PBYTE)ctx->buf;
PBYTE payload = mask + 4;
for (unsigned __int64 i = 0; i < ctx->payload_len; ++i) {
payload[i] = payload[i] ^ mask[i % 4];
}
{
switch (ctx->op) {
case Websocket::Text:
case Websocket::Binary:
{
(void)WriteFile(ctx->sIn.pipe, payload, (DWORD)ctx->payload_len, NULL, &ctx->sIn.ol);
}break;
case Websocket::Close:
{
if (ctx->payload_len >= 2) {
WORD code = ntohs(*(PWORD)payload);
printf("Websocket: closed frame: (code: %u, reason: %.*s)\n", code, (int)(ctx->payload_len-2), payload+2);
websocketWrite(ctx, (const char*)payload, (ULONG)ctx->payload_len, &ctx->sendOL, ctx->sendBuf, Websocket::Close);
}
}return;
case Websocket::Ping:
{
websocketWrite(ctx, (const char*)payload, (ULONG)ctx->payload_len, &ctx->sIn.ol, ctx->sIn.wsaBuf, Websocket::Pong);
}return;
default: {
CloseClient(ctx);
}return;
}
}
ctx->recvBuf[0].len = 6;
ctx->dwFlags = MSG_WAITALL;
ctx->recvBuf[0].buf = ctx->buf;
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
ctx->Reading6Bytes = true;
}
void onRead6Complete(IOCP *ctx) {
using namespace Websocket;
PBYTE data = (PBYTE)ctx->buf;
BIT FIN = data[0] & 0b10000000;
if (!FIN) {
puts("FIN MUST be 1");
CloseClient(ctx);
return;
}
ctx->op = Websocket::Opcode(data[0] & 0b00001111);
if (data[0] & 0b01110000) {
puts("RSV is not zero");
CloseClient(ctx);
return;
}
BIT hasmask = data[1] & 0b10000000;
if (!hasmask) {
puts("client MUST mask data");
CloseClient(ctx);
return;
}
ctx->payload_len = data[1] & 0b01111111;
PBYTE precv;
ULONG offset;
switch (ctx->payload_len) {
default:
offset = 0;
data[0] = data[2];
data[1] = data[3];
data[2] = data[4];
data[3] = data[5];
precv = data + 4;
break;
case 126:
ctx->payload_len = ((WORD)(data[2]) << 8) | (WORD)(data[3]);
offset = 2;
data[0] = data[4];
data[1] = data[5];
precv = data + 2;
break;
case 127:
offset = 8;
ctx->payload_len = ntohll(*(unsigned __int64*)&data[2]);
precv = data + 0;
break;
}
if (ctx->payload_len == 0) {
ctx->Reading6Bytes = true;
return;
}
if (ctx->payload_len+6 > sizeof(ctx->buf)) {
puts("Error: data too large!");
CloseClient(ctx);
return;
}
ctx->Reading6Bytes = false;
ctx->recvBuf[0].len = (ULONG)ctx->payload_len + offset;
ctx->recvBuf[0].buf = (char*)precv;
ctx->dwFlags = MSG_WAITALL;
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
}
Mine.cpp
#include <map>
#include <string>
std::map<std::wstring, const char*> mineTypeData = {
{L"html", "text/html"},
{L"css", "text/css"},
// skip
};
#include <shlwapi.h>
#pragma comment (lib, "Shlwapi.lib")
const char* getType(const WCHAR* name) {
auto s = PathFindExtensionW(name);
if (s == NULL) {
return "text/plain; charset=utf-8";
}
s++;
auto r = mineTypeData.find(s);
if (r == mineTypeData.end())
return "text/plain; charset=utf-8";
return r->second;
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Terminal</title>
<link rel="stylesheet" href="https://unpkg.com/xterm#4.18.0/css/xterm.css" />
<script src="https://unpkg.com/xterm#4.18.0/lib/xterm.js"></script>
<script src="https://unpkg.com/xterm-addon-webgl#0.12.0-beta.15/lib/xterm-addon-webgl.js"></script>
<script src="https://unpkg.com/xterm-addon-fit#0.5.0/lib/xterm-addon-fit.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght#600&display=swap" rel="stylesheet">
</head>
<body style="height: 100%;width: 100%;">
<script src="index.js"></script>
</body>
</html>
index.js
var terminal;
this.onerror = alert;
cmd = prompt("process name(e.g. wsl, bash, cmd, powershell)", "cmd");
if (!cmd) {
throw "error: user refuse to enter process name";
}
var ws = new WebSocket('ws://' + location.host, cmd);
ws.onopen = () => {
terminal.write('* * * connection established * * *\r\n');
};
terminal = new Terminal({
fontFamily: "'Source Sans Pro', 'Lucida Console','Source Code Pro', 'monospace'",
cursorBlink: true,
scrollback: 1000,
windowsMode: true,
bellStyle: "sound",
});
terminal.open(document.body);
ws.onclose = e => {
if (e.reason != '') {
terminal.write("\r\n* * * connection closed * * *\r\n"+e.reason);
}
else {
terminal.write('\r\n* * *connection closed...* * *\r\n' + e.code);
}
};
ws.onerror = console.error;
ws.onmessage = (m) => {
m.data.startsWith && terminal.write(m.data);
m.data.text && m.data.text().then((t) => {
terminal.write(t);
});
};
var buf = String();
terminal.onData((e) => {
switch (e) {
case '\u0003':
terminal.write('^C');
terminal.write("\r\n");
ws.send(e);
break;
case '\u0004':
terminal.write('^D');
terminal.write("\r\n");
ws.send(e);
break;
case '\r':
ws.send(buf + '\n');
terminal.write("\r\n");
buf = "";
break;
case '\u007F':
if (terminal._core.buffer.x > 2) {
terminal.write('\b \b');
if (buf.length > 0) {
buf = buf.substr(0, buf.length - 1);
}
}
break;
default:
if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') {
buf += e;
terminal.write(e);
}
}
});
const fitAddon = new FitAddon.FitAddon();
terminal.loadAddon(fitAddon);
fitAddon.fit();
terminal.focus();

multi-serial port communication on windows

I am working on a project which need to operate two serial port as the same time on windows, but whatever I try I can only open one serial port. Unless repeat open and close, I can operate the two ports but it is too slow. I need to write and read first port and formalize it by protocol and sent it to another port, but I can not initial two port. I was using open and close two ports repeatly to do that before but it was toooooooo slow. Anybody know how to open two port in the same time; Thx!!!
here are my code here:
#include <iostream>
#include <iomanip>
#include "udp.h"
#include "Serial.h"
#include "Formalized.h"
using namespace std;
bool sthread = false;
unsigned char status[6] = {};
HANDLE hMutex = NULL;
DWORD WINAPI ULTRAread(LPVOID lpParamter)
{
Cserial ultra;
unsigned char input0[7] = { 0x00,0x01,0x00,0x01,0x01,0xE5,0xAC };
unsigned char input1[7] = { 0x00,0x02,0x00,0x01,0x01,0xE5,0xE8 };
unsigned char input2[7] = { 0x00,0x03,0x00,0x01,0x01,0xE4,0x14 };
unsigned char input3[7] = { 0x00,0x04,0x00,0x01,0x01,0xE5,0x60 };
unsigned char input4[7] = { 0x00,0x05,0x00,0x01,0x01,0xE4,0x9C };
unsigned char input5[7] = { 0x00,0x06,0x00,0x01,0x01,0xE4,0xD8 };
unsigned char* input[6] = { input0,input1,input2,input3,input4,input5 };
unsigned char pool[8] = {};
if (!ultra.Initcom("COM10")) //ultrasonic COM interface
{
cout << "Init ultrasonic failure \n";
getchar();
sthread = false;
}
else
{
sthread = true;
for (;;)
{
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < 6; i++)
{
if (ultra.Write(input[i], 7))
{
ultra.Read(pool, 8);
}
switch (pool[5])
{
case 0x01:
status[i] = 0x01;
break;
case 0x00:
status[i] = 0x00;
break;
}
}
Sleep(5000);
ReleaseMutex(hMutex);
}
}
}
int main()
{
HANDLE uThread = CreateThread(NULL, 0, ULTRAread, NULL, 0, NULL);
hMutex = CreateMutex(NULL, FALSE, "ultrasonics status");
Cserial zigbee;
cUDP UWB;
char buff[300];
char data[256];
if (!UWB.Initial(6685)) //latitude and longitude port
{
cout << "UWB Port connecting failure \n";
getchar();
}
else
{
cout << "UWB Com connecting success \n";
}
if (!zigbee.Initcom("COM7")) //zigbee access port
{
cout << "Zigbee Initial Failure! \n";
getchar();
}
else
{
cout << "Zigbee Initial Success! \n";
}
for (;;)
{
WaitForSingleObject(hMutex, INFINITE);
switch (sthread)
{
case TRUE: //ultrasonics trustful
WaitForSingleObject(hMutex, INFINITE);
cout << "1";
ReleaseMutex(hMutex);
break;
case FALSE:
cout << "2";
break;
}
Sleep(5000);
}
CloseHandle(uThread);
getchar();
return 0;
}
#include"Serial.h"
Cserial::Cserial()
{
hcom = INVALID_HANDLE_VALUE;
}
Cserial::~Cserial()
{
}
bool Cserial::Initcom(LPCSTR Port)
{
hcom = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL, 0);
if (hcom == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "fail serial1\n");
return false;
}
SetupComm(hcom, 1024, 1024);
DCB dcb;
GetCommState(hcom, &dcb);
dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = 0;
dcb.StopBits = 1;
SetCommState(hcom, &dcb);
COMMTIMEOUTS CommTimeouts;
GetCommTimeouts(hcom, &CommTimeouts);
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 10;
CommTimeouts.ReadTotalTimeoutConstant = 100;
CommTimeouts.WriteTotalTimeoutMultiplier = 1;
CommTimeouts.WriteTotalTimeoutConstant = 10;
if (!SetCommTimeouts(hcom, &CommTimeouts))
{
fprintf(stderr, "fail serial2\n");
return FALSE;
}
return true;
}
void Cserial::Uninitcom()
{
CloseHandle(hcom);
hcom = INVALID_HANDLE_VALUE;
}
bool Cserial::Clearcom()
{
PurgeComm(hcom, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT);
return TRUE;
}
bool Cserial::Write(unsigned char* buf, int len)
{
if (hcom == INVALID_HANDLE_VALUE)
{
return FALSE;
}
DWORD dwWrittenLen = 0;
if (!WriteFile(hcom, buf, len, &dwWrittenLen, NULL))
{
return FALSE;
}
else
{
return TRUE;
}
}
bool Cserial::Read(unsigned char* buf, int len)
{
DWORD dwReadLen = 0;
if (hcom == INVALID_HANDLE_VALUE)
{
return FALSE;
}
if (!ReadFile(hcom, buf, len, &dwReadLen, NULL))
{
return FALSE;
}
else
{
return TRUE;
}
}

Arduino Serial printing stops at a certain value output from joystick module

I'm making an application that moves that mouse via the joystick module. I'm using C++ because my board doesn't support Mouse.move from the Arduino IDE.
I've run into an issue where if I push the joystick all the way back to where the X value is 1024 the command console that's printing the values out stops printing at all, no empty strings just paused. But if I look at the Serial monitor from the Arduino IDE I see it's continuously printing 1024, 513 printed as it should.
I find that when I do this with the Y value it prints 1024 to both Arduino IDE and C++ Serial Monitors just fine. e.g 513, 1024
I'm fairly new to using C++ with the Arduino, it might be something simple.
JoystickModule.ino (Arduino code)
#define BAUD 9600
const int SW_pin = 2;
const int X_pin = 0;
const int Y_pin = 1;
void setup(){
Serial.begin(BAUD);
pinMode(SW_pin, INPUT);
digitalWrite(SW_pin, HIGH);
}
void loop(){
Serial.println((String)analogRead(X_pin) + "," + (String)analogRead(Y_pin));
delay(100);
}
main.cpp
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <string>
#include <Windows.h>
#include "SerialPort.h"
#include <vector>
using namespace std;
//portname must contain these backslashes, and remember to replace the following com port
char *port_name = "\\\\.\\COM4";
//String for incoming data
char incomingData[MAX_DATA_LENGTH];
int main() {
SerialPort arduino(port_name);
if (arduino.isConnected()) cout << "Connection Established" << endl;
else cout << "ERROR, check port name";
while (arduino.isConnected()) {
int read_result = arduino.readSerialPort(incomingData, MAX_DATA_LENGTH);
//store into array for later use by splitting by "," e.g. "513, 508"
//result[0] will store the X val and result[1] will store the Y val
string result[2];
int xVal = 0;
int yVal = 0;
string data = (string)incomingData;
size_t pos = data.find(",");
result[0] = data.substr(0, pos);
result[1] = data.substr(pos + 1, 4);
cout << data << endl;
Sleep(100);
}
return 0;
}
Header Files and other resources from this tutorial
SerialPort.h
#pragma once
#ifndef SERIALPORT_H
#define SERIALPORT_H
#define ARDUINO_WAIT_TIME 2000
#define MAX_DATA_LENGTH 255
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
class SerialPort
{
private:
HANDLE handler;
bool connected;
COMSTAT status;
DWORD errors;
public:
SerialPort(char *portName);
~SerialPort();
int readSerialPort(char *buffer, unsigned int buf_size);
bool writeSerialPort(char *buffer, unsigned int buf_size);
bool isConnected();
};
#endif // SERIALPORT_H
SerialPort.cpp
#include "stdafx.h"
#include "SerialPort.h"
SerialPort::SerialPort(char *portName)
{
this->connected = false;
this->handler = CreateFileA(static_cast<LPCSTR>(portName),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (this->handler == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
}
else
{
printf("ERROR!!!");
}
}
else {
DCB dcbSerialParameters = { 0 };
if (!GetCommState(this->handler, &dcbSerialParameters)) {
printf("failed to get current serial parameters");
}
else {
dcbSerialParameters.BaudRate = CBR_9600;
dcbSerialParameters.ByteSize = 8;
dcbSerialParameters.StopBits = ONESTOPBIT;
dcbSerialParameters.Parity = NOPARITY;
dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;
if (!SetCommState(handler, &dcbSerialParameters))
{
printf("ALERT: could not set Serial port parameters\n");
}
else {
this->connected = true;
PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
SerialPort::~SerialPort()
{
if (this->connected) {
this->connected = false;
CloseHandle(this->handler);
}
}
int SerialPort::readSerialPort(char *buffer, unsigned int buf_size)
{
DWORD bytesRead;
unsigned int toRead;
ClearCommError(this->handler, &this->errors, &this->status);
if (this->status.cbInQue > 0) {
if (this->status.cbInQue > buf_size) {
toRead = buf_size;
}
else toRead = this->status.cbInQue;
}
if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;
return 0;
}
bool SerialPort::writeSerialPort(char *buffer, unsigned int buf_size)
{
DWORD bytesSend;
if (!WriteFile(this->handler, (void*)buffer, buf_size, &bytesSend, 0)) {
ClearCommError(this->handler, &this->errors, &this->status);
return false;
}
else return true;
}
bool SerialPort::isConnected()
{
return this->connected;
}
Thank you in advance.

how to correct convert char to byte in C++

I have some problem.
I write next code.
z=recv(conn,buff,512,0);//"Hi VahagnAAAAAAA" - but encrypted for example "zЖWЙЇ%ЂАЊ"S]яАAЧ0АбЯ.Щk5S¤Oц", length 32
BYTE messageLen = (BYTE)strlen(buff);// messageLen = 32
BYTE encryptedMessage[32];
memcpy(encryptedMessage, buff, messageLen);//!!!!!!!!!!!
DWORD encryptedMessageLen = messageLen;
CryptDecrypt(hSessionKeyRSA_2,NULL,TRUE,0,encryptedMessage, &encryptedMessageLen);
cout<<encryptedMessage<<endl;
I recv to buffer char array 32 length.
Where I copy encrypted text
"zЖWЙЇ%ЂАЊ"S]яАAЧ0АбЯ.Щk5S¤Oц"
to byte array, on the encryptedMessage have next value
"zЖWЙЇ%ЂАЊ"S]яАAЧ0АбЯ.Щk5S¤OцMMMMMMMMMMMMMMMMMMM"
where I decrypted I don't get start text, I get
"Ik VqagnеAAcS]‰МММММММММММ ММММММММ"
How I can fix it? please help me.
UPDATE
Client main()
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
const char* servername="127.0.0.1";
Sleep(2000);
setlocale(LC_ALL, "Russian");
WSADATA wsaData;
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
int wsaret=WSAStartup(0x101,&wsaData);
if(wsaret)
return 0;
SOCKET conn;
conn=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(conn==INVALID_SOCKET)
return 0;
if(inet_addr(servername)==INADDR_NONE)
{
hp=gethostbyname(servername);
}
else
{
addr=inet_addr(servername);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL)
{
closesocket(conn);
return 0;
}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(20248);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return 0;
}
std::cout<<"Connected to server";
char buff[512];
memset(buff,'\0',512);
int z;
z=recv(conn,(char*)exportRSAKey,140,0);//Import RSA key
z=recv(conn,(char*)exportAESKey,140,0);//Import AES key
z=recv(conn,buff,512,0);//Get encryption text
importKey();//import key to client
BYTE messageLen = (BYTE)strlen(buff);
BYTE encryptedMessage[33];
memcpy(encryptedMessage, buff, messageLen);
DWORD encryptedMessageLen = messageLen;
CryptDecrypt(hSessionKeyRSA_2,NULL,FALSE,0,encryptedMessage, &encryptedMessageLen);
cout<<encryptedMessage<<endl;
// buff[z]=0;
}
Import key to client
if (CryptAcquireContext(&hCryptProv_RSA_2, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
{
printf("A cryptographic provider has been acquired.\r\n");
}
else
{
DWORD d = GetLastError();
return -1;
}
int iii = CryptImportKey(hCryptProv_RSA_2,(BYTE *)&exportAESKey,140,NULL,NULL,&hSessionKeyRSA_2);
if(CryptSetKeyParam(hSessionKeyRSA_2, KP_IV, exportRSAKey, 0))
{
cout<<"ok";
}
Server main()
std::cout<<"Client connected... "<<pParam<<std::endl;
char buff[512];
CString cmd;
CString params;
int n;
int x;
BOOL auth=false;
SOCKET client=(SOCKET)pParam;
strcpy(buff,"#Server Ready.\r\n");
char keybuff[1024];
createRSAPublicKey();//create enc_dec key
//keybuff = exportRSAKey;
//memset(rec,'\0',512);
const char *p = reinterpret_cast<const char*>(exportRSAKey);
send(client,p,140,0);//send RSA
const char *pp = reinterpret_cast<const char*>(exportAESKey);
send(client,pp,140,0);//Send AES
const char *ppp = reinterpret_cast<const char*>(encryptedMessage);
send(client,ppp,512,0);//Send encrypt text
createRSAPublicKey()
BOOL createRSAPublicKey()
{
if (CryptAcquireContext(&hCryptProv_AES, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
{
printf("A cryptographic provider has been acquired.\r\n");
}
else
{
DWORD d = GetLastError();
return -1;
}
HCRYPTKEY hSessionKey_AES;
if (!CryptGenKey(hCryptProv_AES, CALG_AES_256, CRYPT_EXPORTABLE, &hSessionKey_AES))
{
DWORD d = GetLastError();
return -1;
}
// Create RSA key to encrypt AES one
HCRYPTKEY hSessionKey;
if (!CryptGenKey(hCryptProv_AES, AT_KEYEXCHANGE, 1024 << 16, &hSessionKey))
{
DWORD d = GetLastError();
return -1;
}
// Export key
DWORD keylen;
BOOL ok = CryptExportKey(hSessionKey_AES, hSessionKey, SIMPLEBLOB, 0, exportRSAKey, &keylen);
if (ok == FALSE)
{
DWORD d = GetLastError();
return -1;
}
BYTE *encKey = (BYTE *)malloc(keylen);
ok = CryptExportKey(hSessionKey_AES, hSessionKey, SIMPLEBLOB, 0, exportAESKey, &keylen);
if (ok == FALSE)
{
DWORD d = GetLastError();
return -1;
}
else
printf("A cryptographic key export succeeded.\r\n");
BYTE messageLen = (BYTE)strlen(mess);
memcpy(encryptedMessage, mess, messageLen);
DWORD encryptedMessageLen = messageLen;
CryptEncrypt(hSessionKey_AES, NULL, TRUE, 0, encryptedMessage, &encryptedMessageLen, sizeof(encryptedMessage));
}
You are using strlen() to get the length of buff, but recv() does not null-terminate the buffer unless a null terminator was actually transmitted and read. You should instead be using the return value of recv(), which is the number of bytes actually read:
z=recv(conn,buff,512,0);
messageLen = z;//(BYTE)strlen(buff);
That being said, TCP is a byte stream, it has no concept of message boundaries. There is no 1-to-1 relationship between send() and recv() in TCP, like there is in UDP, so recv() above could read as little as 1 byte or as many as 512 bytes, and buff could contain a full message, a partial message, pieces of multiple messages, etc. You can't just blindly read and expect to receive everything in one go. You need to take all of that into account.
Design your TCP protocol to delimit messages, either with a preceding header that specifies the message length, or a trailing delimiter that never appears in the message body. Call recv() as many times as it takes, buffering any received data, and only process/decrypt complete messages that are in your buffer, leaving partial message data in the buffer to be completed by later reads.
Try something more like this:
Client main()
int readBuffer(SOCKET s, void *buffer, int buflen)
{
unsigned char *pbuf = (unsigned char*) buffer;
int total = 0;
while (total < buflen)
{
int num = recv(s, pbuf+total, buflen-total, 0);
if (num < 0)
return SOCKET_ERROR;
if (num == 0)
return 0;
total += num;
}
return total;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
const char* servername="127.0.0.1";
setlocale(LC_ALL, "Russian");
WSADATA wsaData;
memset(&wsaData, 0, sizeof(wsaData));
int wsaret = WSAStartup(0x101, &wsaData);
if (wsaret != 0)
return 0;
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_addr.s_addr = inet_addr(servername);
if (server.sin_addr.s_addr == INADDR_NONE)
{
struct hostent *hp = gethostbyname(servername);
if (hp == NULL)
return 0;
server.sin_addr = *((in_addr*)hp->h_addr);
}
server.sin_family = AF_INET;
server.sin_port = htons(20248);
SOCKET conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn == INVALID_SOCKET)
return 0;
if (connect(conn, (struct sockaddr*)&server, sizeof(server)) != 0)
{
closesocket(conn);
return 0;
}
std::cout << "Connected to server";
if (readBuffer(conn, exportRSAKey, 140) <= 0) //Import RSA key
{
closesocket(conn);
return 0;
}
if (readBuffer(conn, exportAESKey, 140) <= 0) //Import AES key
{
closesocket(conn);
return 0;
}
importKey();//import key to client
DWORD messageLen;
if (readBuffer(conn, &messageLen, sizeof(messageLen)) <= 0) //Get encryption text length
{
closesocket(conn);
return 0;
}
messageLen = ntohl(messageLen);
std::vector<BYTE> buff(messageLen);
if (messageLen > 0)
{
if (readBuffer(conn, &buff[0], messageLen) <= 0) //Get encryption text
{
closesocket(conn);
return 0;
}
if (!CryptDecrypt(hSessionKeyRSA_2, NULL, FALSE, 0, &buff[0], &messageLen))
{
closesocket(conn);
return 0;
}
}
std::cout << std::string((char*)buff.data(), messageLen) << std::endl;
}
Server main()
int sendBuffer(SOCKET s, void *buffer, int buflen)
{
unsigned char *pbuf = (unsigned char*) buffer;
int total = 0;
while (total < buflen)
{
int num = send(s, pbuf+total, buflen-total, 0);
if (num < 0)
return SOCKET_ERROR;
if (num == 0)
return 0;
total += num;
}
return total;
}
...
SOCKET client = (SOCKET)pParam;
std::cout << "Client connected... " << pParam << std::endl;
...
createRSAPublicKey();//create enc_dec key
...
if (sendBuffer(client, exportRSAKey, 140) <= 0) //send RSA
{
closesocket(client);
return;
}
if (sendBuffer(client, exportAESKey, 140) <= 0) //Send AES
{
closesocket(client);
return;
}
...
DWORD tmpMessageLen = htonl(messageLen);
if (sendBuffer(client, &tmpMessageLen, sizeof(tmpMessageLen)); //Send encrypt text length
{
closesocket(client);
return;
}
if (sendBuffer(client, encryptedMessage, messageLen) <= 0) //Send encrypt text
{
closesocket(client);
return;
}
...

send data between two client sockets

I have to make an app using C sockets on Mac-OS that sends data from one socket to other socket, like this.
Server waits for connections
Client connect to server(from 1). -> socket1
Server connects to an external server and obtains an socket. -> socket2
From now on the server job is finish. The data exchange should be made only between the client socket (from 2) and socket obtained from 3.
Current implementation:
Server makes the connection and then reads data from one socket and sends to other.
Any ides how after step 3 to pipe line the two sockets socket1 and socket2.
Well your problem can be solved in two ways:
1) You need to code the part related to the connection formation between client and external server. But this puts an extra overload on the client, because it needs to make two connections, to both the servers (and I strongly feel the middle server in this case is useless).
2) Second way of solving it is passing the sockets between the servers:
Client connects to the server, this middle server sends this socket to the external server. Now external server starts communication with the client. This can be done only if both the server processes run on the same machine. And the file-descriptors are usually passed using Unix Domain Sockets.
Here is the code which I have. You can use these two functions to either send or receive the file-descriptors. It works on my Linux machine. I don't know about Mac-OS.
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
/* this function passes 'fd_to_send'
file descriptor via
a unix domain socket 'sfd'...
*/
void pass_fd(int sfd, int fd_to_send)
{
struct msghdr msg;
/*allocate memory to 'msg_control' field in msghdr struct */
char buf[CMSG_SPACE(sizeof(int))];
/*the memory to be allocated should include data + header..
this is calculated by the above macro...(it merely adds some
no. of bytes and returs that number..*/
struct cmsghdr *cmsg;
struct iovec ve;
/*must send/receive atleast one byte...
main purpose is to have some error
checking.. but this is completely
irrelevant in the current context..*/
char *st ="I";
/*jst let us allocate 1 byte for formality
and leave it that way...*/
ve.iov_base = st;
ve.iov_len =1;
/*attach this memory to our main msghdr struct...*/
msg.msg_iov = &ve;
msg.msg_iovlen = 1;
/*these are optional fields ..
leave these fields with zeros..
to prevent unnecessary SIGSEGVs..*/
msg.msg_name = NULL;
msg.msg_namelen = 0;
/*here starts the main part..*/
/*attach the 'buf' to msg_control..
and fill in the size field correspondingly..
*/
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
/*actually msg_control field must
point to a struct of type 'cmsghdr'
we just allocated the memory, yet we need to
set all the corresponding fields..
It is done as follows:
*/
cmsg = CMSG_FIRSTHDR(&msg);
/* this macro returns the address in the buffer..
from where the first header starts..
*/
/*set all the fields appropriately..*/
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
/*in the above field we need to store
the size of header + data(in this case 4 bytes(int) for our fd..
this is returned by the 'CMSG_LEN' macro..*/
*(int*)CMSG_DATA(cmsg) = fd_to_send;
/*after the above three fields we keep the actual data..
the macro 'CMSG_DATA' returns pointer to this location
and we set it to the file descriptor to be sent..
*/
msg.msg_controllen = cmsg->cmsg_len;
/*now that we have filled the 'cmsg' struct
we store the size of this struct..*/
/*this one isn't required when you
pass a single fd..
but useful when u pass multiple fds.*/
msg.msg_flags = 0;
/*leave the flags field zeroed..*/
if(sendmsg( sfd, &msg, 0)==-1){ perror("snd:\n"); exit(1); }
/*send this over the UNIX deomain socoket..*/
printf("sent fd:%d\n", fd_to_send);
close(fd_to_send);
/*close the fd which was sent..*/
}
/*returns the received fd over the unix domain socket 'sfd'..*/
int recv_fd(int sfd)
{
struct msghdr msg;
/*do all the unwanted things first...
same as the send_fd function..*/
struct iovec io;
char ptr[1];
io.iov_base = ptr;
io.iov_len = 1;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &io;
msg.msg_iovlen = 1;
/*-----------------------*/
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
/*reasoning is same..as above*/
/*now here comes the main part..*/
if(recvmsg( sfd, &msg, 0)==-1)
{
/*some shit has happened*/
perror("recv\n");
exit(1);
}
struct cmsghdr *cm;
cm = CMSG_FIRSTHDR(&msg);
/*get the first message header..*/
if(cm->cmsg_type != SCM_RIGHTS)
{
/*again some shit has happened..*/
perror("unknown type..\n");
exit(1);
}
/*if control has reached here.. this means
we have got the correct message..and when you
extract the fd out of this message
this need not be same as the one which was sent..
allocating a new fd is all done by the kernel
and our job is jst to use it..*/
printf("received fd:%d\n", *(int*)CMSG_DATA(cm));
return *(int*)CMSG_DATA(cm);
}
In the below example:
ClientOne and ClientTwo connect to the server.
When the server receives both ClientOne and ClientTwo's socket descriptor, it sends ClientOne's information to ClientTwo and vice-versa.
The information it sends is the IP and the client is coming from. Server shuts down.
When each client receives their info, a socket is created and they connect to eachother. The server socket is then shutdown.
Socket Class:
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <windows.h>
#include <cstdint>
#include <string>
#include <stdexcept>
#include <iostream>
#include <thread>
#include <vector>
class Socket
{
private:
SOCKET socket;
std::uint32_t Port;
std::string Address;
bool Listen, Initialized, Asynchronous;
void Swap(Socket &S);
void UnInitialized();
public:
Socket();
Socket(std::uint32_t Port, std::string Address, bool Listen = false, bool Asynchronous = false);
Socket(const Socket &S) = delete;
Socket(Socket && S);
~Socket();
Socket& operator = (const Socket &S) = delete;
Socket& operator = (Socket && S);
int Recv(void* Buffer, std::uint32_t BufferLength);
int Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength);
std::uint32_t RecvEx(void* Buffer, std::uint32_t BufferLength);
std::uint32_t RecvEx(SOCKET S, void* Buffer, std::uint32_t BufferLength);
int Send(void* Buffer, std::size_t BufferSize);
int Send(SOCKET S, void* Buffer, std::size_t BufferSize);
void Connect();
void Connect(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous);
SOCKET Accept(sockaddr* ClientInfo, int* ClientInfoSize);
void Close();
SOCKET GetSocket() const;
};
Socket::~Socket()
{
Close();
}
void Socket::Close()
{
if (socket)
{
shutdown(socket, SD_BOTH);
closesocket(socket);
socket = 0;
}
if (Initialized)
{
WSACleanup();
}
}
SOCKET Socket::GetSocket() const
{
return this->socket;
}
Socket::Socket(Socket && S) : socket(std::move(S.socket)), Port(std::move(S.Port)), Address(std::move(S.Address)), Listen(std::move(S.Listen)), Initialized(std::move(S.Initialized)), Asynchronous(std::move(S.Asynchronous)) {}
Socket::Socket() : socket(0), Port(0), Address(std::string()), Listen(false), Initialized(false), Asynchronous(false) {}
Socket::Socket(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous) : socket(0), Port(Port), Address(Address), Listen(Listen), Initialized(true), Asynchronous(Asynchronous)
{
Connect(Port, Address, Listen, Asynchronous);
}
void Socket::Connect()
{
UnInitialized();
Connect(Port, Address, Listen, Asynchronous);
}
void Socket::Connect(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous)
{
if (!socket)
{
this->Port = Port;
this->Address = Address;
this->Asynchronous = Asynchronous;
this->Initialized = true;
WSADATA wsaData;
struct sockaddr_in* sockaddr_ipv4;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
throw std::runtime_error("Error: WSAStartup Failed");
}
if (Address != "INADDR_ANY")
{
if (Address.find("http://") != std::string::npos)
{
Address = Address.substr(7);
}
std::size_t Position = Address.find("/");
if (Position != std::string::npos)
{
Address = Address.substr(0, Position);
}
struct addrinfo *it = nullptr, *result = nullptr;
getaddrinfo(Address.c_str(), nullptr, nullptr, &result);
for (it = result; it != nullptr; it = it->ai_next)
{
sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
Address = inet_ntoa(sockaddr_ipv4->sin_addr);
if (Address != "0.0.0.0") break;
}
freeaddrinfo(result);
}
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
this->Close();
throw std::runtime_error("Error: Failed to create socket");
}
struct sockaddr_in SockAddr;
memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_port = htons(Port);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = (Address == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(Address.c_str()));
if (Listen && (bind(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: Socket binding failed");
}
if (Listen && (listen(this->socket, SOMAXCONN) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: Socket Listening Failed");
}
if(!Listen && (connect(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
if(Asynchronous && WSAGetLastError() != WSAEWOULDBLOCK)
{
this->Close();
throw std::runtime_error("Error: Socket Connection failed");
}
else if (!Asynchronous)
{
this->Close();
throw std::runtime_error("Error: Socket Connection failed");
}
}
}
}
SOCKET Socket::Accept(sockaddr* ClientInfo, int* ClientInfoSize)
{
static int Size = sizeof(sockaddr);
return accept(this->socket, ClientInfo, (ClientInfo && ClientInfoSize ? ClientInfoSize : &Size));
}
Socket& Socket::operator = (Socket && S)
{
S.Swap(*this);
return *this;
}
int Socket::Recv(void* Buffer, std::uint32_t BufferLength)
{
return recv(this->socket, reinterpret_cast<char*>(Buffer), BufferLength, 0);
}
int Socket::Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength)
{
return recv(S, reinterpret_cast<char*>(Buffer), BufferLength, 0);
}
std::uint32_t Socket::RecvEx(void* Buffer, std::uint32_t BufferLength)
{
return this->RecvEx(this->socket, Buffer, BufferLength);
}
std::uint32_t Socket::RecvEx(SOCKET S, void* Buffer, std::uint32_t BufferLength)
{
UnInitialized();
char* Pointer = reinterpret_cast<char*>(Buffer);
std::uint32_t TotalRead = 0;
while (BufferLength > 0)
{
int BytesRead = recv(S, Pointer, std::min(1024 * 1024, static_cast<int>(BufferLength)), 0);
if (BytesRead < 0)
{
if ((BytesRead == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
continue;
throw std::runtime_error("Error! RecvEx: Failed To Read Bytes.");
}
if (BytesRead == 0) break;
Pointer += BytesRead;
BufferLength -= BytesRead;
TotalRead += BytesRead;
}
return TotalRead;
}
int Socket::Send(void* Buffer, std::size_t BufferSize)
{
return send(this->socket, reinterpret_cast<char*>(Buffer), BufferSize, 0);
}
int Socket::Send(SOCKET S, void* Buffer, std::size_t BufferSize)
{
return send(S, reinterpret_cast<char*>(Buffer), BufferSize, 0);
}
void Socket::Swap(Socket &S)
{
using std::swap;
swap(socket, S.socket);
swap(Port, S.Port);
swap(Address, S.Address);
swap(Listen, S.Listen);
swap(Initialized, S.Initialized);
swap(Asynchronous, S.Asynchronous);
}
void Socket::UnInitialized()
{
if (!Initialized)
{
throw std::runtime_error("\nError! Socket Not Constructed!");
std::cout << "Socket Not Constructed!\n";
ExitProcess(0);
}
}
Server.cpp:
#include "Sockets.hpp"
#define PORT 27015
#define ADDRESS INADDR_ANY
#define CLIENTCOUNT 2
typedef struct
{
std::string ip;
int port;
SOCKET sock;
} ClientInfo;
template <typename T>
inline T ReadPointer(TCHAR* &Pointer)
{
T Result = *(reinterpret_cast<T*>(Pointer));
Pointer += sizeof(T) / sizeof(TCHAR);
return Result;
}
template <typename T>
inline void WritePointer(TCHAR* &Pointer, const T& Value)
{
*(reinterpret_cast<T*>(Pointer)) = Value;
Pointer += sizeof(T) / sizeof(TCHAR);
}
bool SendClient(ClientInfo* client, ClientInfo* receiver)
{
int datasize = sizeof(client->ip.size()) + client->ip.size() + sizeof(client->port);
std::vector<char> buffer(datasize, 0);
char* ptr = &buffer[0];
WritePointer(ptr, client->ip.size());
for (std::size_t i = 0; i < client->ip.size(); ++i)
WritePointer(ptr, client->ip[i]);
WritePointer(ptr, client->port);
std::cout << "Sending: " << &buffer[0] << "\n";
return send(receiver->sock, &buffer[0], datasize, 0) == datasize;
}
bool ReadClient(SOCKET sock, ClientInfo* client)
{
std::size_t ip_size = 0;
recv(sock, (char*) &ip_size, sizeof(client->ip.size()), 0);
client->ip.resize(ip_size);
recv(sock, &client->ip[0], ip_size, 0);
recv(sock, (char*) &client->port, sizeof(int), 0);
std::cout<<client->ip<<"\n";
return true;
}
int main()
{
Socket s;
s.Connect(PORT, "localhost", true, false);
char buffer[1024] = {0};
std::vector<ClientInfo> clients;
while(true)
{
if (clients.size() < CLIENTCOUNT)
{
sockaddr_in ClientAddressInfo = {0};
SOCKET sock = s.Accept(reinterpret_cast<sockaddr*>(&ClientAddressInfo), nullptr);
char* ip = inet_ntoa(ClientAddressInfo.sin_addr);
int port = (int) ntohs(ClientAddressInfo.sin_port);
ClientInfo info = {ip, port, sock};
clients.push_back(info);
std::cout << "Client Connected From: " << ip << " on port: " << port << "\n";
}
if (ReadAsync(s, buffer))
{
std::cout << "Connected\n";
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (clients.size() >= CLIENTCOUNT)
{
SendClient(&clients[0], &clients[1]);
SendClient(&clients[1], &clients[0]);
return 0;
}
}
}
Client.cpp:
#define PORT 27015
#define ADDRESS INADDR_ANY
#define CLIENTCOUNT 2
typedef struct
{
std::string ip;
int port;
SOCKET sock;
} ClientInfo;
template <typename T>
inline T ReadPointer(TCHAR* &Pointer)
{
T Result = *(reinterpret_cast<T*>(Pointer));
Pointer += sizeof(T) / sizeof(TCHAR);
return Result;
}
template <typename T>
inline void WritePointer(TCHAR* &Pointer, const T& Value)
{
*(reinterpret_cast<T*>(Pointer)) = Value;
Pointer += sizeof(T) / sizeof(TCHAR);
}
bool SendClient(ClientInfo* client, ClientInfo* receiver)
{
int datasize = sizeof(client->ip.size()) + client->ip.size() + sizeof(client->port);
std::vector<char> buffer(datasize, 0);
char* ptr = &buffer[0];
WritePointer(ptr, client->ip.size());
for (std::size_t i = 0; i < client->ip.size(); ++i)
WritePointer(ptr, client->ip[i]);
WritePointer(ptr, client->port);
std::cout << "Sending: " << &buffer[0] << "\n";
return send(receiver->sock, &buffer[0], datasize, 0) == datasize;
}
bool ReadClient(SOCKET sock, ClientInfo* client)
{
std::size_t ip_size = 0;
recv(sock, (char*) &ip_size, sizeof(client->ip.size()), 0);
client->ip.resize(ip_size);
recv(sock, &client->ip[0], ip_size, 0);
recv(sock, (char*) &client->port, sizeof(int), 0);
return true;
}
bool ReadAsync(const Socket &sock, ClientInfo* client)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock.GetSocket(), &rfds);
for (int i = 0; i < 600; ++i)
{
if (select(sock.GetSocket(), &rfds, &rfds, NULL, &tv))
{
return ReadClient(sock.GetSocket(), client);
}
tv.tv_sec = 0;
tv.tv_usec = 100000;
}
return false;
}
int main()
{
Socket s;
s.Connect(PORT, "localhost", false, false);
std::vector<SOCKET> clients;
ClientInfo client = {};
while(true)
{
if (ReadAsync(s, &client))
{
std::cout<<"IP: "<<client.ip<<" PORT: "<<client.port<<"\n";
s = std::move(Socket(client.port, client.ip, true, false));
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
system("CLS");
std::cout<<"Connecting..\n";
}
}