interactive terminal with winsock - c++

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();

Related

Connection between DTLS server and client not working

I am developing a DTLS server and client. But there is no handshake between them.
PS I made a cut from the project in order to reduce the amount of code. If you forgot something - do not kick ...
a common part
static int dtls_verify_callback(int ok, X509_STORE_CTX* ctx) {
return ok;
}
static int generate_cookie(SSL* ssl, unsigned char* cookie, unsigned int* cookie_len) {
unsigned int i;
for (i = 0; i < COOKIE_LEN; i++, cookie++)
*cookie = i;
*cookie_len = COOKIE_LEN;
return 1;
}
static int verify_cookie(SSL* ssl,
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
const
#endif
unsigned char* cookie,
unsigned int cookie_len) {
unsigned int i;
if (cookie_len != COOKIE_LEN)
return 0;
for (i = 0; i < COOKIE_LEN; i++, cookie++) {
if (*cookie != i)
return 0;
}
return 1;
}
Server side
int main() {
int iTimeout = 1000;
ADDRINFOW* pAddressInfo;
ADDRINFOW AddressHints;
WSADATA WinsockData;
LPCWSTR lpwszAddress = L"";
SOCKET socket;
union {
struct sockaddr_storage ss;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
} server_addr, client_addr;
const TIMEVAL Timeout = { iTimeout / 1000, (iTimeout % 1000) * 1000 };
if (1 != OPENSSL_init_ssl(0
| OPENSSL_INIT_NO_LOAD_CONFIG
| OPENSSL_INIT_ASYNC
| OPENSSL_INIT_ENGINE_DYNAMIC
| OPENSSL_INIT_ENGINE_ALL_BUILTIN
| OPENSSL_INIT_NO_ATEXIT
| OPENSSL_INIT_LOAD_SSL_STRINGS
| OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, nullptr))
{
return 1;
}
WSAStartup(MAKEWORD(2, 2), &WinsockData);
auto ctx = unique_ptr_c<SSL_CTX>(SSL_CTX_new(DTLS_server_method()), SSL_CTX_free);
auto cert_store = unique_ptr_c<X509_STORE>(X509_STORE_new(), X509_STORE_free);
X509_STORE_load_locations(cert_store.get(), "certs/ca-cert.pem", nullptr);
SSL_CTX_set1_verify_cert_store(ctx.get(), cert_store.get());
if (!SSL_CTX_use_certificate_chain_file(ctx.get(), "certs/server-cert.pem"))
{
ERR_print_errors_fp(stderr);
}
if (!SSL_CTX_use_PrivateKey_file(ctx.get(), "certs/server-key.pem", SSL_FILETYPE_PEM))
{
ERR_print_errors_fp(stderr);
}
if (!SSL_CTX_check_private_key(ctx.get()))
{
ERR_print_errors_fp(stderr);
}
SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, dtls_verify_callback);
SSL_CTX_set_read_ahead(ctx.get(), 1);
SSL_CTX_set_cookie_generate_cb(ctx.get(), generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx.get(), verify_cookie);
memset(&AddressHints, 0, sizeof(AddressHints));
AddressHints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
AddressHints.ai_family = AF_UNSPEC;
AddressHints.ai_socktype = SOCK_DGRAM;
AddressHints.ai_protocol = 0;
iResult = GetAddrInfoW(lpwszAddress, 12345, &AddressHints, &pAddressInfo);
if (0 != iResult)
{
}
else
{
for (const ADDRINFOW* pCurrentAddressInfo = pAddressInfo; nullptr != pCurrentAddressInfo; pCurrentAddressInfo = pCurrentAddressInfo->ai_next)
{
socket = socket(pCurrentAddressInfo->ai_family, pCurrentAddressInfo->ai_socktype, pCurrentAddressInfo->ai_protocol);
if (INVALID_SOCKET == CurrentSocket) { continue; }
if (SOCKET_ERROR == bind(CurrentSocket, pCurrentAddressInfo->ai_addr, static_cast<int>(pCurrentAddressInfo->ai_addrlen)))
{
(void)closesocket(CurrentSocket);
continue;
}
if (SOCKET_ERROR == setsockopt(CurrentSocket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&iTimeout), sizeof(iTimeout))) { }
break;
}
}
for (;;)
{
if (SOCKET_ERROR == select(0, &socket, nullptr, nullptr, &Timeout)) {}
memset(&client_addr, 0, sizeof(struct sockaddr_storage));
auto bio = BIO_new_dgram(socket, BIO_NOCLOSE);
auto ssl = unique_ptr_c<SSL>(SSL_new(ctx.get()), SSL_close_and_free);
SSL_set_bio(ssl.get(), bio, bio);
SSL_set_options(ssl.get(), SSL_OP_COOKIE_EXCHANGE);
while (iResult <= 0) {
iResult = DTLSv1_listen(ssl.get(), (BIO_ADDR*)&client_addr);
if (iResult <= 0) { }
}
BIO_set_fd(SSL_get_rbio(ssl.get()), socket, BIO_NOCLOSE);
BIO_ctrl(SSL_get_rbio(ssl.get()), BIO_CTRL_DGRAM_SET_CONNECTED, 0, &client_addr);
iResult = SSL_accept(ssl.get());
if (iResult != 1) {
printf("%s\n", ERR_error_string(ERR_peek_last_error(), NULL));
printf("%s\n", ERR_error_string(ERR_get_error(), buf));
}
else
{
// I didn't cut the code further, since it never goes here
}
}
}
Client side
int main() {
ADDRINFOW AddressHints;
SOCKET ClientSocket = INVALID_SOCKET;
memset(&AddressHints, 0, sizeof(AddressHints));
AddressHints.ai_family = AF_UNSPEC;
AddressHints.ai_socktype = SOCK_DGRAM;
AddressHints.ai_protocol = 0;
int iResult = GetAddrInfoW(lpwszHost, wszPort, &AddressHints, &pAddressInfo);
if (0 != iResult)
{
}
else
{
unique_ptr_c<SSL_CTX> ctx;
unique_ptr_c<SSL> ssl;
BIO* bio;
if (1 != OPENSSL_init_ssl(0
| OPENSSL_INIT_NO_LOAD_CONFIG
| OPENSSL_INIT_ASYNC
| OPENSSL_INIT_ENGINE_DYNAMIC
| OPENSSL_INIT_ENGINE_ALL_BUILTIN
| OPENSSL_INIT_NO_ATEXIT
| OPENSSL_INIT_LOAD_SSL_STRINGS
| OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, nullptr))
{
return 1;
}
ctx = unique_ptr_c<SSL_CTX>(SSL_CTX_new(DTLS_client_method()), SSL_CTX_free);
auto cert_store = unique_ptr_c<X509_STORE>(X509_STORE_new(), X509_STORE_free);
X509_STORE_load_locations(cert_store.get(), "certs/ca-cert.crt", nullptr);
SSL_CTX_set1_verify_cert_store(ctx.get(), cert_store.get());
iResult = SSL_CTX_use_certificate_chain_file(ctx.get(), "certs/client-cert.pem");
if (!iResult)
{
ERR_print_errors_fp(stderr);
}
iResult = SSL_CTX_use_PrivateKey_file(ctx.get(), "certs/client-key.pem", SSL_FILETYPE_PEM);
if (!iResult)
{
ERR_print_errors_fp(stderr);
}
if (!SSL_CTX_check_private_key(ctx.get()))
{
ERR_print_errors_fp(stderr);
}
SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, dtls_verify_callback);
SSL_CTX_set_read_ahead(ctx.get(), 1);
SSL_CTX_set_cookie_generate_cb(ctx.get(), generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx.get(), verify_cookie);
ssl = unique_ptr_c<SSL>(SSL_new(ctx.get()), SSL_close_and_free);
for (pCurrentAddressInfo = pAddressInfo; nullptr != pCurrentAddressInfo; pCurrentAddressInfo = pCurrentAddressInfo->ai_next)
{
ClientSocket = socket(pCurrentAddressInfo->ai_family, pCurrentAddressInfo->ai_socktype, pCurrentAddressInfo->ai_protocol);
if (INVALID_SOCKET == ClientSocket)
{
continue;
}
iResult = connect(ClientSocket, pCurrentAddressInfo->ai_addr, static_cast<int>(pCurrentAddressInfo->ai_addrlen));
if (SOCKET_ERROR == iResult)
{
}
else
{
break;
}
if (SOCKET_ERROR == closesocket(ClientSocket))
{
}
}
if (nullptr != pCurrentAddressInfo)
{
if (SOCKET_ERROR == setsockopt(ClientSocket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&iTimeout), sizeof(iTimeout)))
{
iResult = WSAGetLastError();
}
else
{
bio = BIO_new_dgram(ClientSocket, BIO_NOCLOSE);
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, remote_address.get());
SSL_set_bio(ssl.get(), bio, bio);
iResult = SSL_connect(ssl.get()); // Trying 5 handshakes fails with an SSL_ERROR_SYSCALL error
if (iResult <= 0) {
char buf[BUFFER_SIZE];
//print_errors_stderr();
ERR_print_errors_fp(stderr);
printf("%s\n", ERR_error_string(ERR_peek_last_error(), NULL));
printf("%s\n", ERR_error_string(ERR_get_error(), buf));
switch (SSL_get_error(ssl.get(), iResult)) {
...
case SSL_ERROR_SYSCALL:
ERR_print_errors_fp(stderr);
DBG_MSG((DBG_ERR, L"DTLSClient::Send: SSL_connect failed with SSL_ERROR_SYSCALL"));
break;
...
}
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
ERR_print_errors_fp(stderr);
}
else
{
// I didn't cut the code further, since it never goes here
}
}
// Close the socket
if (SOCKET_ERROR == closesocket(ClientSocket))
{
}
}
}
}
The client tries 5 times to make a handshake with the server but it doesn't work.
An error occurs from the client side on the line iResult = SSL_connect(ssl.get()) - an error occurs:
error:00000000:lib(0)::reason(0)
ERR: DTLSClient::Send: SSL_connect failed with SSL_ERROR_SYSCALL
An error occurs on the server at the line iResult = SSL_accept(ssl.get()) - with the error:
SSL_accept: No error
error:00000000:lib(0)::reason(0)
Based on traffic
enter image description here
Client sends - Client Hello
The server replies to this - Hello verify Request
And then the client tries to answer something back. Now I don't know what to do next. Are there any ideas?
I tried to do it as in the examples from OpenLL and other examples on github.

Some files send on a HLS Server using Http Put request are partially uploaded

I have to upload audio chunks continuously on a HLS Server as a part of my project. I am able to upload chunks sucessfully using HTTP Put method with wininet API in C++ using following code.
bool CHTTP::HttpPut(char *szFile,int fileType)
{
bool bErrorFlag = false;
if(m_hInternet == NULL)
{
int retStatus = OpenHTTPSession();
if(retStatus < 1)
{
return true;
}
}
char szPostURL[256];
INTERNET_BUFFERS BufferIn = {0};
DWORD dwBytesRead;
DWORD dwBytesWritten;
BYTE pBuffer[350000];
BOOL bRead, bRet;
static int flag = 1;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS );
char szLocalFilePath[256];
if(fileType == AUDIO_CHUNK)
sprintf(szLocalFilePath,"%s/%s",m_strFilePath,szFile);
else
strcpy(szLocalFilePath,szFile);
int iFileSize = 0;
if(fileType == AUDIO_CHUNK)
{
strcpy(szPostURL,m_strPostPath);
strcat(szPostURL,"/");
strcat(szPostURL,szFile);
}
else if(fileType == M3U8)
strcpy(szPostURL,m_szM3U8FileToPost);
else if(fileType == AUTO_M3U8)
strcpy(szPostURL,m_szM3U8AutoPost);
DWORD dwFlags =
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD |INTERNET_FLAG_SECURE;
m_hRequest = HttpOpenRequest(m_hHttpSession, (const char*)"PUT",szPostURL, "HTTP/1.1",NULL , (const char**)"*/*\0",dwFlags, 1);
if(m_hRequest==NULL)
{
bErrorFlag = true;
CloseHTTPSession();
return bErrorFlag;
}
else
{
bErrorFlag = false;
}
int num_of_try = 0;
while(num_of_try < 3)
{
char logDump[1000];
num_of_try++;
HANDLE hFile = CreateFile (szLocalFilePath, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
bErrorFlag = true;
break;
}
else if(!m_bLogFlagCreateErr)
{
m_bLogFlagCreateErr = true;
sprintf(logDump,"CreateFile success %s",szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = false;
}
BufferIn.dwBufferTotal = GetFileSize (hFile, NULL);
iFileSize = BufferIn.dwBufferTotal;
if(!HttpSendRequestEx( m_hRequest, &BufferIn, NULL, HSR_INITIATE, 0))
{
bErrorFlag = true;
m_bLogFlagSend = false;
sprintf(logDump,"Error on HttpSendRequestEx %lu %s",GetLastError(),szFile);
WriteLog(mLogFile,logDump);
break;
}
else
{
bErrorFlag = false;
sprintf(logDump,"HttpSendRequest success %s",szFile);
WriteLog(mLogFile,logDump);
}
DWORD sum = 0;
int size = 0;
do
{
bRead = ReadFile (hFile, pBuffer,iFileSize,
&dwBytesRead, NULL);
if(dwBytesRead != iFileSize)
{
sprintf(logDump,"dwBytesRead %d iFileSize %d %s",dwBytesRead,iFileSize,szFile);
WriteLog(mLogFile,logDump);
}
if(dwBytesRead > 0)
{
bRet=InternetWriteFile( m_hRequest, pBuffer, dwBytesRead,
&dwBytesWritten);
while(dwBytesRead < dwBytesWritten && bRet)
{
sprintf(logDump,"dwBytesRead %d dwBytesWritten %d %s",dwBytesRead,dwBytesWritten,szFile);
WriteLog(mLogFile,logDump);
bRet=InternetWriteFile( m_hRequest, pBuffer+dwBytesWritten, dwBytesRead - dwBytesWritten ,&dwBytesWritten);
}
if(!bRet)
{
int error = GetLastError();
sprintf(logDump,"InternetWriteFile %lu %s",error,szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = true;
break;
}
else
{
sprintf(logDump,"InternetWriteFile buffer success %s",szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = false;
}
}
}
while (dwBytesRead == iFileSize);
CloseHandle (hFile);
if(!HttpEndRequest(m_hRequest, NULL, 0, 0))
{
int error = GetLastError();
if(error != 12032)
{
sprintf(logDump,"HttpEndRequest %lu %s",error,szFile);
WriteLog(mLogFile,logDump)
bErrorFlag = true;
break;
}
else
{
bErrorFlag = true;
continue;
}
}
else
{
sprintf(logDump,"HttpEndRequest success %s",szFile);
WriteLog(mLogFile,logDump);
DWORD dwCode, dwCodeSize;
dwCodeSize = sizeof(DWORD);
if(!HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwCodeSize, NULL))
{
int error = GetLastError();
char tmp[256];
sprintf(tmp,"HttpQueryfails %d %s",error, szFile);
WriteLog(mLogFile,tmp);
}
else
{
if(dwCode != 201 && dwCode != 204)
{
char tmp[256];
sprintf(tmp,"dwcode %d is not 201 or 204 %s",dwCode,szFile);
WriteLog(mLogFile,tmp);
bErrorFlag = true;
break;
}
}
bErrorFlag = false;
break;
}
}
CloseHTTPSession();
return bErrorFlag;
}
Most of the times chunks uploaded are of full size. Randomly chunks uploaded on server are not of full size as shown in following image.
sample image
In such case I am not getting any error messages, even dwBytesWritten returned by InternetWriteFile function is also correct. I am unable to understand what should I do to solve it. Any help in this regard is appreciated.

Is it possible to have 2 WinSock servers on 2 different ports?

i have a quick question, i have 1 tcp proxy server wich helps me redirect data from 1 port to another, ie: 55500 to 55510, now i want to make the server to listen to another port, let`s say 55520, all this at the same time, is it possible?
I have tried examples found here to bind to another port, but maybe i am doing something wrong :(
I have tried to create a IOCP server after i startup the proxy server, but it crashes... any ideas? or maybe a example? Any help would be apreciated!
This is my proxytcp:
#include "ProxyTCP.h"
#include <process.h>
#include "GInclude.h"
using namespace std;
CProxyTCP::CProxyTCP() :
_guid(0),
_started(false),
_hevent_start(NULL),
_hevent_stop(NULL),
_serv_sock(INVALID_SOCKET),
_connect_callback(NULL),
_connect_param(NULL),
_close_callback(NULL),
_close_param(NULL),
_send_callback(NULL),
_send_param(NULL),
_recv_callback(NULL),
_recv_param(NULL)
{
InitializeCriticalSection(&_csect);
InitializeCriticalSection(&_csect_dbg);
}
CProxyTCP::~CProxyTCP()
{
Stop();
DeleteCriticalSection(&_csect);
DeleteCriticalSection(&_csect_dbg);
}
bool CProxyTCP::Start(const char *src_addr, unsigned short src_port, const char *dest_addr, unsigned short dest_port)
{
sockaddr_in addr = {};
int res;
if (_started) {
_DBG_OUTPUT("Error, server already started!");
return false;
}
RemoveAllConnInfo();
_removed_conn.clear();
_hthr_pool.clear();
if (!CreateSockAddr(src_addr, src_port, &addr)) {
_DBG_OUTPUT("Error, incorrect source address");
return false;
}
if (!dest_addr || !CreateSockAddr(dest_addr, dest_port, &_serv_addr)) {
_DBG_OUTPUT("Error, incorrect destination address");
return false;
}
_serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_serv_sock == INVALID_SOCKET) {
_DBG_OUTPUT("Error, socket() failed with code " << WSAGetLastError());
return false;
}
res = bind(_serv_sock, (sockaddr *)&addr, sizeof(addr));
if (res == SOCKET_ERROR) {
_DBG_OUTPUT("Error, bind() failed with code " << WSAGetLastError());
closesocket(_serv_sock);
return false;
}
_hevent_start = CreateEvent(NULL, true, false, NULL);
if (!_hevent_start) {
_DBG_OUTPUT("Error, CreateEvent() failed with code " << GetLastError());
closesocket(_serv_sock);
return false;
}
_hevent_stop = CreateEvent(NULL, true, true, NULL);
if (!_hevent_start) {
_DBG_OUTPUT("Error, CreateEvent() failed with code " << GetLastError());
CloseHandle(_hevent_start);
closesocket(_serv_sock);
return false;
}
_started = true;
_beginthreadex(NULL, 0, proxy_conn_gate, this, 0, NULL);
if (WaitForSingleObject(_hevent_start, 10000) != WAIT_OBJECT_0) {
_DBG_OUTPUT("Error, WaitForSingleObject() failed");
Stop();
return false;
}
return true;
}
void CProxyTCP::Stop()
{
bool retn = false;
HANDLE *pthr_pool;
unsigned int count;
EnterCriticalSection(&_csect);
if (!_started) {
retn = true;
} else {
_started = false;
}
LeaveCriticalSection(&_csect);
if (retn) {
return;
}
if (_serv_sock != INVALID_SOCKET) {
closesocket(_serv_sock);
_serv_sock = INVALID_SOCKET;
}
WaitForSingleObject(_hevent_stop, INFINITE);
EnterCriticalSection(&_csect);
count = _hthr_pool.size() * 2;
if (count != 0) {
try {
pthr_pool = new HANDLE[count];
} catch (...) {
pthr_pool = NULL;
}
map<unsigned int, std::pair<HANDLE, HANDLE>>::iterator it = _hthr_pool.begin();
for (unsigned int i = 0; i < count; i += 2, it++) {
pthr_pool[i] = it->second.first;
pthr_pool[i + 1] = it->second.second;
}
list<PProxy_Client>::iterator it_conn = _conn.begin();
PProxy_Client pelem;
for (unsigned int i = 0; i < _conn.size(); i++, it_conn++) {
pelem = *it_conn;
closesocket(pelem->client);
closesocket(pelem->server);
}
}
LeaveCriticalSection(&_csect);
if (count == 0) {
return;
}
if (pthr_pool == NULL) {
Sleep(2000); //hmm...
} else {
WaitForMultipleObjects(count, pthr_pool, true, 2000);
}
RemoveAllConnInfo();
}
bool CProxyTCP::IsStarted()
{
return _started;
}
void CProxyTCP::RegConnectFilter(tcp_proxy_connect_filter callback, void *param)
{
_connect_callback = callback;
_connect_param = param;
}
void CProxyTCP::UnregConnectFilter()
{
_connect_callback = NULL;
_connect_param = NULL;
}
void CProxyTCP::RegCloseFilter(tcp_proxy_close_filter callback, void *param)
{
_close_callback = callback;
_close_param = param;
}
void CProxyTCP::UnregCloseFilter()
{
_close_callback = NULL;
_close_param = NULL;
}
void CProxyTCP::RegSendFilter(tcp_proxy_traffic_filter callback, void *param)
{
_send_callback = callback;
_send_param = param;
}
void CProxyTCP::UnregSendFilter()
{
_send_callback = NULL;
_send_param = NULL;
}
void CProxyTCP::RegRecvFilter(tcp_proxy_traffic_filter callback, void *param)
{
_recv_callback = callback;
_recv_param = param;
}
void CProxyTCP::UnregRecvFilter()
{
_recv_callback = NULL;
_recv_param = NULL;
}
bool CProxyTCP::CreateSockAddr(const char *addr, unsigned short port, sockaddr_in *psaddr)
{
psaddr->sin_family = AF_INET;
psaddr->sin_port = htons(port);
if (!addr) {
psaddr->sin_addr.s_addr = 0;
} else {
psaddr->sin_addr.s_addr = inet_addr(addr);
if (psaddr->sin_addr.s_addr == INADDR_NONE) {
HOSTENT *host;
host = gethostbyname(addr);
if (!host) {
return false;
}
psaddr->sin_addr.s_addr = host->h_addr[0];
}
}
return true;
}
unsigned int CProxyTCP::AddConnInfo(SOCKET client)
{
PProxy_Client pelem = new Proxy_Client;
EnterCriticalSection(&_csect);
__try {
pelem->id = GenGuid();
pelem->client = client;
pelem->server = INVALID_SOCKET;
pelem->client_init = false;
pelem->server_init = false;
pelem->started = false;
pelem->hevent_init = CreateEvent(NULL, false, false, NULL);
if (!pelem->hevent_init) {
delete pelem;
return INVALID_CONN_ID;
}
pelem->hevent_sync = CreateEvent(NULL, false, false, NULL);
if (!pelem->hevent_sync) {
CloseHandle(pelem->hevent_init);
delete pelem;
return INVALID_CONN_ID;
}
_conn.push_back(pelem);
} __finally {
LeaveCriticalSection(&_csect);
}
return pelem->id;
}
CProxyTCP::PProxy_Client CProxyTCP::GetFreeClientConnInfo()
{
list<PProxy_Client>::iterator it;
PProxy_Client pelem = NULL;
EnterCriticalSection(&_csect);
it = _conn.begin();
while (it != _conn.end()) {
if ((*it)->client_init == false) {
pelem = (*it);
pelem->client_init = true;
break;
}
it++;
}
LeaveCriticalSection(&_csect);
return pelem;
}
CProxyTCP::PProxy_Client CProxyTCP::GetFreeServerConnInfo()
{
list<PProxy_Client>::iterator it;
PProxy_Client pelem = NULL;
EnterCriticalSection(&_csect);
it = _conn.begin();
while (it != _conn.end()) {
if ((*it)->server_init == false) {
pelem = (*it);
pelem->server_init = true;
break;
}
it++;
}
LeaveCriticalSection(&_csect);
return pelem;
}
bool CProxyTCP::RemoveConnInfo(unsigned int conn_id)
{
list<PProxy_Client>::iterator it;
bool res = false;
EnterCriticalSection(&_csect);
it = _conn.begin();
while (it != _conn.end()) {
if ((*it)->id == conn_id) {
(*it)->started = false;
if ((*it)->client) {
closesocket((*it)->client);
}
if ((*it)->server) {
closesocket((*it)->server);
}
if ((*it)->hevent_sync) {
SetEvent((*it)->hevent_sync);
CloseHandle((*it)->hevent_sync);
}
if ((*it)->hevent_init) {
SetEvent((*it)->hevent_init);
CloseHandle((*it)->hevent_init);
}
_conn.erase(it);
res = true;
break;
}
it++;
}
LeaveCriticalSection(&_csect);
return res;
}
void CProxyTCP::RemoveAllConnInfo()
{
list<PProxy_Client>::iterator it;
EnterCriticalSection(&_csect);
it = _conn.begin();
while (it != _conn.end()) {
(*it)->started = false;
if ((*it)->client) {
closesocket((*it)->client);
}
if ((*it)->server) {
closesocket((*it)->server);
}
if ((*it)->hevent_sync) {
SetEvent((*it)->hevent_sync);
CloseHandle((*it)->hevent_sync);
}
if ((*it)->hevent_init) {
SetEvent((*it)->hevent_init);
CloseHandle((*it)->hevent_init);
}
it++;
}
LeaveCriticalSection(&_csect);
}
void CProxyTCP::ClearClosedResources()
{
list<unsigned int>::iterator it;
EnterCriticalSection(&_csect);
it = _removed_conn.begin();
while (it != _removed_conn.end()) {
_hthr_pool.erase(*it);
it++;
}
LeaveCriticalSection(&_csect);
}
void CProxyTCP::ConnectionCtrl()
{
SOCKET client_sock = INVALID_SOCKET;
HANDLE hthr_client, hthr_server;
sockaddr_in saddr;
unsigned int id;
int res;
ResetEvent(_hevent_stop);
SetEvent(_hevent_start);
while (_started) {
res = listen(_serv_sock, SOMAXCONN);
if (res == SOCKET_ERROR) {
_DBG_OUTPUT("Error, ConnectionCtrl::listen() failed with code " << res);
break;
}
client_sock = accept(_serv_sock, (sockaddr *)&saddr, NULL);
if (client_sock == INVALID_SOCKET) {
continue;
}
id = AddConnInfo(client_sock);
if (id == INVALID_CONN_ID) {
_DBG_OUTPUT("Error, ConnectionCtrl::AddConnInfo() failed");
continue;
}
if (_connect_callback) {
if (!_connect_callback(id, &saddr, _connect_param)) {
RemoveConnInfo(id);
closesocket(client_sock);
continue;
}
}
//SOCKADDR_IN client_info = {0};
//int addrsize = sizeof(client_info);
//getpeername(client_sock, &saddr, sizeof(saddr));
// char *ip = inet_ntoa(saddr.sin_addr);
//g_Log.LogAdd(g_Colors.Gold(), "test %s", ip);
//_DBG_OUTPUT(">>>> Client #" << id << " connected");
EnterCriticalSection(&_csect);
hthr_client = (HANDLE)_beginthreadex(NULL, 0, proxy_send_gate, this, 0, NULL);
hthr_server = (HANDLE)_beginthreadex(NULL, 0, proxy_recv_gate, this, 0, NULL);
_hthr_pool.insert(
pair<unsigned int, pair<HANDLE, HANDLE>>(
id, pair<HANDLE, HANDLE>(hthr_client, hthr_server)
)
);
LeaveCriticalSection(&_csect);
ClearClosedResources();
}
SetEvent(_hevent_stop);
}
void CProxyTCP::SendCtrl(PProxy_Client client)
{
enum { MAX_BUF_LEN = 2048 };
char recvbuf[MAX_BUF_LEN];
int res;
res = WaitForSingleObject(client->hevent_sync, 10000);
if (res != WAIT_OBJECT_0) {
_DBG_OUTPUT("Error, SendCtrl::WaitForSingleObject() failed with code " << GetLastError());
return;
}
//init
/*for(ptr = _serv_addr; ptr != NULL; ptr = ptr->ai_next) {
client->server = socket(_serv_addr->ai_family, _serv_addr->ai_socktype, _serv_addr->ai_protocol);
if (client->server == INVALID_SOCKET) {
_DBG_OUTPUT("Error, SendCtrl::socket() failed with code " << WSAGetLastError());
return;
}
res = connect(client->server, ptr->ai_addr, (int)ptr->ai_addrlen);
if (res == SOCKET_ERROR) {
closesocket(client->server);
client->server = INVALID_SOCKET;
continue;
}
break;
}
if (client->server == INVALID_SOCKET) {
return;
}*/
client->server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client->server == INVALID_SOCKET) {
_DBG_OUTPUT("Error, SendCtrl::socket() failed with code " << WSAGetLastError());
return;
}
res = connect(client->server, (sockaddr *)&_serv_addr, sizeof(_serv_addr));
if (res == SOCKET_ERROR) {
_DBG_OUTPUT("Error, SendCtrl::connect() failed with code " << WSAGetLastError());
closesocket(client->server);
client->server = INVALID_SOCKET;
return;
}
client->started = true;
SetEvent(client->hevent_init);
//worked cycle
while (client->started && _started) {
res = recv(client->client, recvbuf, MAX_BUF_LEN, 0);
if (res == 0) {
break;
} else if (res < 0) {
break;
}
if (_send_callback) {
res = _send_callback(client->id, recvbuf, res, MAX_BUF_LEN, _send_param);
if (res == 0) {
break;
} else if (res < 0) {
break;
}
}
res = send(client->server, recvbuf, res, 0);
if (res == SOCKET_ERROR) {
break;
}
}
client->started = false;
closesocket(client->client);
closesocket(client->server);
WaitForSingleObject(client->hevent_sync, 10000);
if (_close_callback) {
_close_callback(client->id, _close_param);
}
EnterCriticalSection(&_csect);
_removed_conn.insert(_removed_conn.begin(), client->id);
LeaveCriticalSection(&_csect);
//_DBG_OUTPUT("<<<< Client #" << client->id << " closed");
}
void CProxyTCP::RecvCtrl(PProxy_Client client)
{
enum { MAX_BUF_LEN = 2048 };
char recvbuf[MAX_BUF_LEN];
int res;
SetEvent(client->hevent_sync);
res = WaitForSingleObject(client->hevent_init, 10000);
if (res != WAIT_OBJECT_0) {
_DBG_OUTPUT("Error, RecvCtrl::WaitForSingleObject() failed with code " << GetLastError());
return;
}
//worked cycle
while (client->started && _started) {
res = recv(client->server, recvbuf, MAX_BUF_LEN, 0);
if (res == 0) {
break;
} else if (res < 0) {
break;
}
if (_recv_callback) {
res = _recv_callback(client->id, recvbuf, res, MAX_BUF_LEN, _recv_param);
if (res == 0) {
break;
} else if (res < 0) {
break;
}
}
res = send(client->client, recvbuf, res, 0);
if (res == SOCKET_ERROR) {
break;
}
}
client->started = false;
SetEvent(client->hevent_sync);
}
unsigned int __stdcall CProxyTCP::proxy_conn_gate(void *param)
{
CProxyTCP *pthis = (CProxyTCP *)param;
pthis->ConnectionCtrl();
pthis->Stop();
_endthreadex(0);
return 0;
}
unsigned int __stdcall CProxyTCP::proxy_send_gate(void *param)
{
CProxyTCP *pthis = (CProxyTCP *)param;
PProxy_Client client;
client = pthis->GetFreeServerConnInfo();
if (!client) {
_endthreadex(1);
return 1;
}
pthis->SendCtrl(client);
pthis->RemoveConnInfo(client->id);
_endthreadex(0);
return 0;
}
unsigned int __stdcall CProxyTCP::proxy_recv_gate(void *param)
{
CProxyTCP *pthis = (CProxyTCP *)param;
PProxy_Client client;
client = pthis->GetFreeClientConnInfo();
if (!client) {
_endthreadex(1);
return 1;
}
pthis->RecvCtrl(client);
_endthreadex(0);
return 0;
}

winhttp client and openssl server communication error

I have encounted a strange communication problem when using winhttp to program http client and using openssl to program http server.
I use wireshark to analyze the communication. Everything seems ok when handshake between the two, and the handshake procedure is really quick. But after the handshake, winhttp client taken a really long time send a http request, then server received the request (the received packet is right) then server attempted to send response with ssl_write, but client cannot receive this packet.
please see notes in my code for advanced info.
Below is client's main communication code.
BOOL CHttpClient :: _Synchronize(BYTE* pbRequest, DWORD dwRequestCb,
BYTE** pbResponse, DWORD* dwResponseCb)
{
DWORD dwErrorCode = 0;
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
DWORD dwLastStatusCode = 0;
BOOL bDone = FALSE;
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
while (!bDone)
{
if (!WinHttpSendRequest(m_hRequest, NULL, 0, pbRequest, dwRequestCb, dwRequestCb, NULL))
{
dwErrorCode = GetLastError();
switch (dwErrorCode)
{
case ERROR_WINHTTP_CANNOT_CONNECT:
case ERROR_WINHTTP_TIMEOUT:
m_dwDefaultAddrIndex = (m_dwDefaultAddrIndex + 1) % m_lpConfig->dwHttpAddrNum;
case ERROR_WINHTTP_CONNECTION_ERROR:
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
case 12175:
continue;
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
continue;
default:
return FALSE;
}
}
// client always wait here for server's response and get a error code as 12002--ERROR_INTERNET_TIMEOUT
if (!WinHttpReceiveResponse(m_hRequest, NULL))
{
dwErrorCode = GetLastError(); // get error code 12002--ERROR_INTERNET_TIMEOUT
switch (dwErrorCode)
{
case ERROR_WINHTTP_RESEND_REQUEST:
continue;
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
continue;
default:
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
}
}
if (!DoServerAuth())
{
m_dwDefaultAddrIndex = (m_dwDefaultAddrIndex + 1) % m_lpConfig->dwHttpAddrNum;
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
}
if (!WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
NULL, &dwStatusCode, &dwSize, NULL))
{
return FALSE;
}
switch (dwStatusCode)
{
// success
case 200:
bDone = TRUE;
break;
// proxy need authentication
case 407:
if (dwLastStatusCode == 407)
{
bDone = TRUE;
}
else if (!DoProxyAuth())
return FALSE;
break;
default:
return FALSE;
}
dwLastStatusCode = dwStatusCode;
}
DWORD dwBytesAvailable = 0;
DWORD dwBytesTransfered = 0;
BYTE* pbTmpBuf = new BYTE [GetMaxTransferCb()];
// read http server reply
*dwResponseCb = 0;
do
{
if (!WinHttpQueryDataAvailable(m_hRequest, &dwBytesAvailable))
{
return FALSE;
}
if (!WinHttpReadData(m_hRequest, pbTmpBuf + *dwResponseCb, dwBytesAvailable,
&dwBytesTransfered))
{
return FALSE;
}
*dwResponseCb += dwBytesTransfered;
} while (dwBytesAvailable > 0);
*pbResponse = new BYTE [*dwResponseCb];
memcpy(*pbResponse, pbTmpBuf, *dwResponseCb);
RELEASE_ARRAY(pbTmpBuf);
// all done
return TRUE;
}
BOOL CHttpClient :: DoConnectAndOpenRequest(BOOL bGetVerb)
{
BOOL bOK = FALSE;
DWORD dwReqObjNameCch = (DWORD)rand() % (20 - 5) + 5;
BYTE* lpReqObjName = new BYTE [(dwReqObjNameCch + 6) * sizeof(TCHAR)];
DWORD dwOptionCode = 0;
__try
{
// release old handles
RELEASE_HINTERNET(m_hConnect);
RELEASE_HINTERNET(m_hRequest);
// connect to default address, change of default address is not my duty
if (NULL == (m_hConnect = WinHttpConnect(m_hSession,
m_lpConfig->lpHttpAddrs[m_dwDefaultAddrIndex].tszAddr,
m_lpConfig->lpHttpAddrs[m_dwDefaultAddrIndex].wPort, 0)))
__leave;
// random generate request object name
// now this method is slow and bad, we can improve later
if (!CryptGenRandom(m_hCryptProv, dwReqObjNameCch * sizeof(TCHAR), lpReqObjName))
return FALSE;
for (DWORD i = 0; i < dwReqObjNameCch * sizeof(TCHAR); i += sizeof(TCHAR))
{
lpReqObjName[i] = (lpReqObjName[i] % 26) + 97;
lpReqObjName[i + 1] = 0;
}
((LPTSTR)lpReqObjName)[dwReqObjNameCch] = _T('.');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 1] = _T('h');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 2] = _T('t');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 3] = _T('m');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 4] = _T('l');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 5] = 0;
// open request
if (NULL == (m_hRequest = WinHttpOpenRequest(m_hConnect, bGetVerb ? L"GET" : L"POST",
(LPTSTR)lpReqObjName, L"HTTP/1.1", WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE)))
__leave;
// set cert options
if (m_lpConfig->bSsl)
{
dwOptionCode = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_UNKNOWN_CA;
if (!WinHttpSetOption(m_hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwOptionCode,
sizeof(DWORD)))
__leave;
}
// all done
bOK = TRUE;
}
__finally
{
if (!bOK)
{
RELEASE_HINTERNET(m_hConnect);
RELEASE_HINTERNET(m_hRequest);
}
RELEASE_ARRAY(lpReqObjName);
}
return bOK;
}
Below is server's main communication code
#include <strsafe.h>
BOOL CSslServer :: StartService(SSL_CONFIG* lpSslServerConfig)
{
WSADATA lpWSAData;
WSAStartup(MAKEWORD(2, 2), &lpWSAData);
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
m_lpConfig = lpSslServerConfig;
m_SslCtx = SSL_CTX_new(SSLv3_server_method());
SSL_CTX_set_default_passwd_cb_userdata(m_SslCtx, (void*)"123456");
if (1 > SSL_CTX_use_certificate_file(m_SslCtx, m_lpConfig->szCertFileDir, SSL_FILETYPE_PEM))
{
return FALSE;
}
if (1 > SSL_CTX_use_PrivateKey_file(m_SslCtx, m_lpConfig->szSkeyFileDir, SSL_FILETYPE_PEM))
{
return FALSE;
}
SSL_CTX_set_cipher_list(m_SslCtx, "ALL");
SSL_CTX_set_verify(m_SslCtx, SSL_VERIFY_NONE, NULL);
if (NULL == (m_hServerQuitEvt = CreateEvent(NULL, FALSE, FALSE, NULL)))
return FALSE;
if (NULL == (m_hWorkThread = CreateThread(NULL, 0, SslServerWorkThread, this, 0, NULL)))
{
StopService();
return FALSE;
}
return TRUE;
}
BOOL CSslServer :: StopService()
{
DWORD dwExitCode = 0;
if (m_hWorkThread != NULL)
{
SetEvent(m_hServerQuitEvt);
WaitForSingleObject(m_hWorkThread, 30000);
if(GetExitCodeThread(m_hWorkThread, &dwExitCode))
{
if(dwExitCode == STILL_ACTIVE)
TerminateThread(m_hWorkThread, 0);
}
}
SSL_CTX_free(m_SslCtx);
RELEASE_HANDLE(m_hServerQuitEvt);
RELEASE_HANDLE(m_hWorkThread);
m_WorkThreadList.empty();
return TRUE;
}
DWORD WINAPI SslServerWorkThread(LPVOID lpParam)
{
CSslServer* lpServer = (CSslServer*)lpParam;
SOCKET hListenSocket = INVALID_SOCKET;
TIMEVAL* lpWaitTime = new TIMEVAL;
fd_set ListenFds;
DWORD dwWaitForQuit = 0;
int nSelectRel = 0;
int nSize = 0;
SESSION_CONTEXT* lpSessionCtx = NULL;
WORK_THREAD_CONTEXT* lpWorkThreadCtx = NULL;
hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(hListenSocket, (sockaddr*)&lpServer->m_lpConfig->SslServerAddr,
sizeof(sockaddr_in));
if (SOCKET_ERROR == listen(hListenSocket, SOMAXCONN))
{
RELEASE_SOCKET(hListenSocket);
RELEASE(lpWaitTime);
return 0;
}
FD_ZERO(&ListenFds);
FD_SET(hListenSocket, &ListenFds);
lpWaitTime->tv_sec = 30;
lpWaitTime->tv_usec = 0;
do
{
nSelectRel = select(0, &ListenFds, NULL, NULL, lpWaitTime);
switch (nSelectRel)
{
case SOCKET_ERROR:
goto END;
case 1:
lpSessionCtx = new SESSION_CONTEXT;
nSize = sizeof(sockaddr_in);
if (INVALID_SOCKET == (lpSessionCtx->hClientSocket = accept(hListenSocket,
(struct sockaddr *)&lpSessionCtx->ClientAddress, &nSize)))
{
RELEASE(lpSessionCtx);
goto END;
}
lpSessionCtx->lpServer = lpServer;
lpSessionCtx->lpWorkConfig = lpServer->m_lpConfig->lpWorkConfig;
lpSessionCtx->SslSession = SSL_new(lpServer->m_SslCtx);
SSL_set_fd(lpSessionCtx->SslSession, lpSessionCtx->hClientSocket);
SSL_accept(lpSessionCtx->SslSession);
lpWorkThreadCtx = new WORK_THREAD_CONTEXT;
lpWorkThreadCtx->hWorkThreadQuitEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
lpWorkThreadCtx->hWorkThread = CreateThread(NULL, 0, WorkMain, lpSessionCtx,
0, NULL);
// WorkMain is not provided here, the function just analyze the received packet and
// invoke SendPacket to send response.
lpSessionCtx->lpWorkThreadCtx = lpWorkThreadCtx;
lpServer->m_WorkThreadList.push_back(lpWorkThreadCtx);
break;
default:
FD_SET(hListenSocket, &ListenFds);
}
dwWaitForQuit = WaitForSingleObject(lpServer->m_hServerQuitEvt, 0);
if (dwWaitForQuit == WAIT_FAILED || dwWaitForQuit == WAIT_ABANDONED)
{
goto END;
}
} while (dwWaitForQuit != WAIT_OBJECT_0);
END:
RELEASE_SOCKET(hListenSocket);
RELEASE(lpWaitTime);
return 0;
}
// I have examined the received packet. This function is ok
BOOL CSslServer :: ParseClientPacket(SSL* ssl, BYTE** lpBuf, DWORD* dwTransCb)
{
char* lpHeader = NULL;
BOOL bOk = FALSE;
int nRet = 0;
int nHeaderBufCb = 1024;
int nIndex = 0;
DWORD dwPackBufCb = 0;
__try
{
*dwTransCb = 0;
lpHeader = new char [nHeaderBufCb];
memset(lpHeader, 0, nHeaderBufCb);
if (0 >= (nRet = SSL_read(ssl, lpHeader, nHeaderBufCb)))
__leave;
nHeaderBufCb = lstrlenA(lpHeader);
for (nIndex = 20; nIndex < nHeaderBufCb - 15; nIndex++)
{
if (0 == memcmp(lpHeader + nIndex, "Content-Length: ", 16))
{
sscanf_s(lpHeader + nIndex + 16, "%d", &dwPackBufCb);
break;
}
}
if (nIndex == nHeaderBufCb - 15)
__leave;
for (nIndex += 16; nIndex < nHeaderBufCb - 4; nIndex++)
{
if (0 == memcmp(lpHeader + nIndex, "\r\n\r\n", 4))
break;
}
if (nIndex == nHeaderBufCb - 4)
__leave;
*lpBuf = new BYTE [dwPackBufCb];
if (nRet - nIndex - 4 > 0)
{
memcpy(*lpBuf, lpHeader + nIndex + 4, nRet - nIndex - 4);
*dwTransCb = nRet - nIndex - 4;
}
while (*dwTransCb < dwPackBufCb)
{
if (0 >= SSL_read(ssl, *lpBuf + *dwTransCb, dwPackBufCb - *dwTransCb))
{
bOk = TRUE;
__leave;
}
*dwTransCb += nRet;
}
bOk = TRUE;
}
__finally
{
RELEASE_ARRAY(lpHeader);
}
return bOk;
}
BOOL CSslServer :: SendPacket(SSL* ssl, BYTE* lpBuf, DWORD cb)
{
LPSTR lpHttpPacket = NULL;
DWORD dwHeaderLen = 0;
BOOL bOk = FALSE;
char szTime[50] = {0};
time_t lTime;
struct tm GmtTime;
__try
{
lpHttpPacket = new char [200 + cb];
memset(lpHttpPacket, 0, 200 + cb);
time(&lTime);
_gmtime64_s(&GmtTime, &lTime);
strftime(szTime, 50, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", &GmtTime);
StringCchPrintfA(lpHttpPacket, 200,
"HTTP/1.1 200 OK\r\nServer: Microsoft-IIS/8.0\r\nConnection: Keep-Alive\r\n%sContent-Type: text/html\r\nContent-Length: %d\r\n\r\n",
szTime, cb);
dwHeaderLen = lstrlenA(lpHttpPacket);
memcpy(lpHttpPacket + dwHeaderLen, lpBuf, cb);
if (0 >= SSL_write(ssl, lpHttpPacket, cb)) // the packet send by this sentence cannot be received by client
__leave; // observe by wireshark, this sentence send a ssl reassembled pdu
bOk = TRUE;
}
__finally
{
RELEASE_ARRAY(lpHttpPacket);
}
return bOk;
}
And below is wireshark snap.
NO. Time Source Destination Protocol Length Info
15951 3691.1 .23 .98 SSL 126 client hello
15952 3691.1 .98 .23 SSLv3 1109 server hello
15953 3691.1 .23 .98 SSLv3 386 client key exchange, change cipher spec, finished
15954 3691.1 .98 .23 SSLv3 121 change cipher spec, finished
16029 3706.6 .23 .98 http 301 POST ...... HTTP/1.1
16060 3711.9 .98 .23 SSLv3 83 [SSL segment of a ressembled PDU]
It takes me really a long time to solve this problem. I really hope someone and handle this.
best wishes

Winsock2 - How to use IOCP on client side

I’ve recently started learning IOCP on Windows and been reading the following article:
http://www.codeproject.com/Tips/95363/Another-TCP-echo-server-using-IOCP
You can download the sample for the article from:
http://dl.dropbox.com/u/281215/documentation/iocp-1.00.html
The sample contains two simple applications – iocp_echo_server and TcpEchoClient.
I understand that IOCP is usually used on the server side of the client/server model but I’d like to create a client using IOCP.
I’ve so far tried modifying the client sample above so that whenever the server sends a response to the client, it gets picked up automatically, however it doesn’t work.
I’ve left iocp_echo_server.c as is. My modified version of TcpEchoClient.c looks like:
//TcpEchoClient.c - a minimalistic echo client
// -----------------------------------------------------------------------------
// C language includes
#include <stdio.h>
#include <winsock2.h>
#include "mswsock.h" // for AcceptEx
#include <stdlib.h> // exit
#include <string.h>
// Windows includes
#include <windows.h>
#pragma warning(disable: 4996) // sprintf
// -----------------------------------------------------------------------------
// configuration
enum
{
BUFLEN = 1000,
SERVICE_PORT = 4000,
SERVER_ADDRESS = INADDR_LOOPBACK
};
enum // socket operations
{
OP_NONE,
OP_ACCEPT,
OP_READ,
OP_WRITE
};
typedef struct _SocketState // socket state & control
{
char operation;
SOCKET socket;
DWORD length;
char buf[1024];
} SocketState;
// variables
static HANDLE cpl_port;
static SOCKET sock;
static SocketState sock_state;
static WSAOVERLAPPED sock_ovl;
static LPFN_ACCEPTEX pfAcceptEx;
static GUID GuidAcceptEx = WSAID_ACCEPTEX;
static int msgNumber;
static char msgBuf[BUFLEN];
static struct sockaddr_in sin;
// prototypes
static void createConnection(void);
static void createSocket(void);
static void init(void);
static void initWinsock(void);
static void prepareEndpoint(void);
static void recvBuffer(void);
static void run(void);
static void sendBuffer(void);
static SOCKET create_accepting_socket(void);
static void create_io_completion_port(void);
static BOOL get_completion_status(DWORD*, SocketState**,WSAOVERLAPPED**);
// -----------------------------------------------------------------------------
void main(void)
{
init();
run();
}
// -----------------------------------------------------------------------------
static void createConnection(void)
{
printf("* connecting\n");
if (WSAConnect(sock, (LPSOCKADDR)&sin, sizeof(sin), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
{
int err = WSAGetLastError();
printf("* error %d in connect\n", err);
exit(1);
}
printf("* connected\n");
}
// -----------------------------------------------------------------------------
static void createSocket(void)
{
sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET)
{
int err = WSAGetLastError();
printf("* error %d creating socket\n", err);
exit(1);
}
// for use by AcceptEx
sock_state.socket = 0; // to be updated later
sock_state.operation = OP_ACCEPT;
if (CreateIoCompletionPort((HANDLE)sock, cpl_port, (ULONG_PTR)&sock_state, 0) != cpl_port)
{
int err = WSAGetLastError();
printf("* error %d in listener\n", err);
exit(1);
}
}
// -----------------------------------------------------------------------------
static void init(void)
{
initWinsock();
create_io_completion_port();
createSocket();
prepareEndpoint();
createConnection();
}
// -----------------------------------------------------------------------------
static void initWinsock(void)
{
WSADATA wsaData;
if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR)
{
int err = WSAGetLastError();
printf("* error %d in WSAStartup\n", err);
exit(1);
}
}
// -----------------------------------------------------------------------------
static void prepareEndpoint(void)
{
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(SERVER_ADDRESS);
sin.sin_port = htons(SERVICE_PORT);
// bind_listening_socket()
{
//if (bind(sock, (SOCKADDR*)&sin, sizeof(sin)) == SOCKET_ERROR)
//{
// printf("* error in bind!\n");
// exit(1);
//}
}
// start_listening()
{
//if (listen(sock, 100) == SOCKET_ERROR)
//{
// printf("* error in listen!\n");
// exit(1);
//}
//printf("* started listening for connection requests...\n");
}
// load_accept_ex()
{
//DWORD dwBytes;
// black magic for me!!!
// You do not need to call in your code WSAIoctl. You can directly use AcceptEx and adds Mswsock.lib.
//WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &pfAcceptEx, sizeof(pfAcceptEx), &dwBytes, NULL, NULL);
}
// start_accepting()
{
//SOCKET acceptor = create_accepting_socket();
//DWORD expected = sizeof(struct sockaddr_in) + 16;
//printf("* started accepting connections...\n");
// uses listener's completion key and overlapped structure
//sock_state.socket = acceptor;
//memset(&sock_ovl, 0, sizeof(WSAOVERLAPPED));
// starts asynchronous accept
//if (!pfAcceptEx(sock, acceptor, sock_state.buf, 0 /* no recv */, expected, expected, NULL, &sock_ovl))
//{
// int err = WSAGetLastError();
// if (err != ERROR_IO_PENDING)
// {
// printf("* error %d in AcceptEx\n", err);
// exit(1);
// }
//}
}
}
// -----------------------------------------------------------------------------
static void recvBuffer(void)
{
char* buf = msgBuf;
int pendingLen = BUFLEN;
printf("* receiving reply\n");
while (pendingLen > 0)
{
int partialLen = recv(sock, buf, pendingLen, 0);
if (partialLen > 0)
{
pendingLen -= partialLen;
buf += partialLen;
continue;
}
// ------
if (partialLen == 0)
{
printf("* connection closed by the server\n");
}
else // partialLen < 0
{
int err = WSAGetLastError();
printf("* error %d in recv\n", err);
}
exit(1);
}
}
// -----------------------------------------------------------------------------
static void run(void)
{
DWORD length;
BOOL resultOk;
WSAOVERLAPPED* ovl_res;
SocketState* socketState;
for (;;)
{
sendBuffer();
resultOk = get_completion_status(&length, &socketState, &ovl_res);
recvBuffer();
}
}
// -----------------------------------------------------------------------------
static void sendBuffer(void)
{
char* buf = msgBuf;
int pendingLen = BUFLEN;
printf("* sending message\n");
sprintf(msgBuf, "%05 *****", msgNumber++);
while (pendingLen > 0)
{
int partialLen = send(sock, buf, pendingLen, 0);
if (partialLen > 0)
{
pendingLen -= partialLen;
buf += partialLen;
continue;
}
// -----------
if (partialLen == 0)
{
printf("* connection closed by the server\n");
}
else // partialLen < 0
{
int err = WSAGetLastError();
printf("* error %d in send\n", err);
}
exit(1);
}
}
// -----------------------------------------------------------------------------
static SOCKET create_accepting_socket(void)
{
SOCKET acceptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (acceptor == INVALID_SOCKET)
{
printf("* error creating accept socket!\n");
exit(1);
}
return acceptor;
}
// -----------------------------------------------------------------------------
static void create_io_completion_port(void)
{
cpl_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!cpl_port)
{
int err = WSAGetLastError();
printf("* error %d in line %d CreateIoCompletionPort\n", err, __LINE__);
exit(1);
}
}
// -----------------------------------------------------------------------------
static BOOL get_completion_status(DWORD* length, SocketState** socketState, WSAOVERLAPPED** ovl_res)
{
BOOL resultOk;
*ovl_res = NULL;
*socketState = NULL;
resultOk = GetQueuedCompletionStatus(cpl_port, length, (PULONG_PTR)socketState, ovl_res, INFINITE);
if (!resultOk)
{
DWORD err = GetLastError();
printf("* error %d getting completion port status!!!\n", err);
}
if (!*socketState || !*ovl_res)
{
printf("* don't know what to do, aborting!!!\n");
exit(1);
}
return resultOk;
}
// -----------------------------------------------------------------------------
// the end
When the server send a response by calling:
WSASend(socketState->socket, &wsabuf, 1, NULL, 0, ovl, NULL)
I’d expect it to be picked up by the client on this line:
resultOk = get_completion_status(&length, &socketState, &ovl_res);
But it doesn’t…
Would anybody be able to tell me what I’m doing wrong?
Edit:
I’ve taken the following points:
On the client side, you use WSAConnect() to create an outbound connection.
Call WSARecv() and WSASend() to start reading/writing operations when needed
you have to use WSASend/WSARecv if you want to use I/O completion ports.
and attempted to create a simple IOCP based client:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter);
int main(void)
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != NO_ERROR)
return 0;
// Step 1 - Create an I/O completion port.
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
return 0;
// Step 2 - Find how many processors.
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
const int nNumberOfProcessors = systemInfo.dwNumberOfProcessors;
// Step 3 - Create worker threads.
for (int i = 0; i < nNumberOfProcessors; i++)
{
HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
// Step 4 - Create a socket.
SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET)
return 0;
struct hostent *host;
if ((host = gethostbyname("localhost")) == NULL)
return 0;
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
SockAddr.sin_port = htons(8888);
// Step 5 - Associate the socket with the I/O completion port.
CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, (ULONG_PTR)0, 0);
if (WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
return 0;
char buffer[1000];
memset(buffer, 0, 999);
WSABUF wsaBuf = {strlen(buffer), buffer};
DWORD dwSendBytes = 0;
DWORD dwReceivedBytes = 0;
DWORD dwFlags = 0;
WSAOVERLAPPED wsaOverlapped;
SecureZeroMemory((PVOID)&wsaOverlapped, sizeof(wsaOverlapped));
wsaOverlapped.hEvent = WSACreateEvent();
for(;;)
{
WSARecv(Socket, &wsaBuf, 1, &dwReceivedBytes, &dwFlags, &wsaOverlapped, NULL);
std::cout << wsaBuf.buf;
//WSASend(Socket, &wsaBuf, 1, &dwSendBytes, 0, &wsaOverlapped, NULL);
int nError = WSAGetLastError();
if(nError != WSAEWOULDBLOCK&&nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Server disconnected!\r\n";
shutdown(Socket, SD_SEND);
closesocket(Socket);
break;
}
Sleep(1000);
}
WSACleanup();
system("PAUSE");
return 0;
}
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD dwBytesTransferred = 0;
while (TRUE)
{
BOOL bRet = GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred, (LPDWORD)0, (LPOVERLAPPED*)0, INFINITE);
}
return 0;
}
I know there are several things I’m doing wrong but I don’t know what they are.
Could somebody take a look at my code and give me some hints please?
Many thanks
Edit 2:
Sorry this post is getting too long.
I've had another go trying to implement an IOCP based client after reading Remy's comments below but I'm still not sure if I'm on the right track.
I'd really appreciate it if somebody could take a look at my new code (compiles fine under VS2010 & error checking omitted) below and give me some feedback.
NonBlockingClient:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter);
typedef struct _PER_HANDLE_DATA
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
typedef struct
{
WSAOVERLAPPED wsaOverlapped;
WSABUF wsaBuf;
int OperationType;
} PER_IO_DATA, * LPPER_IO_DATA;
int main(void)
{
WSADATA WsaDat;
WSAStartup(MAKEWORD(2, 2), &WsaDat);
// Step 1 - Create an I/O completion port.
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
// Step 2 - Find how many processors.
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
// Step 3 - Create worker threads.
for (int i = 0; i < (int)systemInfo.dwNumberOfProcessors; i++)
{
HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
// Step 4 - Create a socket.
SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
PER_HANDLE_DATA *pPerHandleData = new PER_HANDLE_DATA;
pPerHandleData->Socket = Socket;
struct hostent *host;
host = gethostbyname("localhost");
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
SockAddr.sin_port = htons(8888);
// Step 5 - Associate the socket with the I/O completion port.
CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, (DWORD)pPerHandleData, 0);
WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL);
static char buffer[1000];
memset(buffer, 0, 999);
PER_IO_DATA *pPerIoData = new PER_IO_DATA;
pPerIoData->wsaBuf.buf = buffer;
pPerIoData->wsaBuf.len = sizeof(buffer);
DWORD dwSendBytes = 0;
DWORD dwReceivedBytes = 0;
DWORD dwFlags = 0;
SecureZeroMemory((PVOID)&pPerIoData->wsaOverlapped, sizeof(pPerIoData->wsaOverlapped));
pPerIoData->wsaOverlapped.hEvent = WSACreateEvent();
WSARecv(Socket, &pPerIoData->wsaBuf, 1, &dwReceivedBytes, &dwFlags, &pPerIoData->wsaOverlapped, NULL);
std::cout << pPerIoData->wsaBuf.buf;
for (;;)
{
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK&&nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Server disconnected!\r\n";
shutdown(Socket, SD_SEND);
closesocket(Socket);
break;
}
Sleep(1000);
}
delete pPerHandleData;
delete pPerIoData;
WSACleanup();
return 0;
}
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD bytesCopied = 0;
OVERLAPPED *overlapped = 0;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
BOOL bRet;
while (TRUE)
{
bRet = GetQueuedCompletionStatus(hCompletionPort, &bytesCopied, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE);
if (bytesCopied == 0)
{
break;
}
else
{
Flags = 0;
ZeroMemory(&(PerIoData->wsaOverlapped), sizeof(WSAOVERLAPPED));
PerIoData->wsaBuf.len = 1000;
WSARecv(PerHandleData->Socket, &(PerIoData->wsaBuf), 1, &RecvBytes, &Flags, &(PerIoData->wsaOverlapped), NULL);
}
}
return 0;
}
NonBlockingServer:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA WsaDat;
WSAStartup(MAKEWORD(2,2), &WsaDat);
SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(8888);
bind(listenSocket, (SOCKADDR*)(&server), sizeof(server));
listen(listenSocket, 1);
SOCKET acceptSocket = SOCKET_ERROR;
sockaddr_in saClient;
int nClientSize = sizeof(saClient);
while (acceptSocket == SOCKET_ERROR)
{
std::cout << "Waiting for incoming connections...\r\n";
acceptSocket = WSAAccept(listenSocket, (SOCKADDR*)&saClient, &nClientSize, NULL, NULL);
}
std::cout << "Client connected!\r\n\r\n";
char *szMessage = "Welcome to the server!\r\n";
WSAOVERLAPPED SendOverlapped;
DWORD SendBytes;
WSABUF DataBuf;
DataBuf.len = 1000;
DataBuf.buf = szMessage;
SecureZeroMemory((PVOID)&SendOverlapped, sizeof(WSAOVERLAPPED));
SendOverlapped.hEvent = WSACreateEvent();
for (;;)
{
WSASend(acceptSocket, &DataBuf, 1, &SendBytes, 0, &SendOverlapped, NULL);
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK && nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Client disconnected!\r\n";
shutdown(acceptSocket, SD_SEND);
closesocket(acceptSocket);
break;
}
Sleep(1000);
}
WSACleanup();
return 0;
}
Thanks again!
Try something like this:
Client:
#include <iostream>
#include <string>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
typedef struct
{
WSAOVERLAPPED Overlapped;
SOCKET Socket;
WSABUF wsaBuf;
char Buffer[1024];
DWORD Flags;
} PER_IO_DATA, * LPPER_IO_DATA;
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD NumBytesRecv = 0;
ULONG CompletionKey;
LPPER_IO_DATA PerIoData;
while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesRecv, &CompletionKey, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if (!PerIoData)
continue;
if (NumBytesRecv == 0)
{
std::cout << "Server disconnected!\r\n\r\n";
}
else
{
// use PerIoData->Buffer as needed...
std::cout << std::string(PerIoData->Buffer, NumBytesRecv);
PerIoData->wsaBuf.len = sizeof(PerIoData->Buffer);
PerIoData->Flags = 0;
if (WSARecv(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesRecv, &(PerIoData->Flags), &(PerIoData->Overlapped), NULL) == 0)
continue;
if (WSAGetLastError() == WSA_IO_PENDING)
continue;
}
closesocket(PerIoData->Socket);
delete PerIoData;
}
return 0;
}
int main(void)
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
return 0;
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
return 0;
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i)
{
HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET)
return 0;
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
SockAddr.sin_port = htons(8888);
CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, 0, 0);
if (WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
return 0;
PER_IO_DATA *pPerIoData = new PER_IO_DATA;
ZeroMemory(pPerIoData, sizeof(PER_IO_DATA));
pPerIoData->Socket = Socket;
pPerIoData->Overlapped.hEvent = WSACreateEvent();
pPerIoData->wsaBuf.buf = pPerIoData->Buffer;
pPerIoData->wsaBuf.len = sizeof(pPerIoData->Buffer);
DWORD dwNumRecv;
if (WSARecv(Socket, &(pPerIoData->wsaBuf), 1, &dwNumRecv, &(pPerIoData->Flags), &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
delete pPerIoData;
return 0;
}
}
while (TRUE)
Sleep(1000);
shutdown(Socket, SD_BOTH);
closesocket(Socket);
WSACleanup();
return 0;
}
Server:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
typedef struct
{
WSAOVERLAPPED Overlapped;
SOCKET Socket;
WSABUF wsaBuf;
char Buffer[1024];
DWORD BytesSent;
DWORD BytesToSend;
} PER_IO_DATA, * LPPER_IO_DATA;
static DWORD WINAPI ServerWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD NumBytesSent = 0;
ULONG CompletionKey;
LPPER_IO_DATA PerIoData;
while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesSent, &CompletionKey, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if (!PerIoData)
continue;
if (NumBytesSent == 0)
{
std::cout << "Client disconnected!\r\n\r\n";
}
else
{
PerIoData->BytesSent += NumBytesSent;
if (PerIoData->BytesSent < PerIoData->BytesToSend)
{
PerIoData->wsaBuf.buf = &(PerIoData->Buffer[PerIoData->BytesSent]);
PerIoData->wsaBuf.len = (PerIoData->BytesToSend - PerIoData->BytesSent);
}
else
{
PerIoData->wsaBuf.buf = PerIoData->Buffer;
PerIoData->wsaBuf.len = strlen(PerIoData->Buffer);
PerIoData->BytesSent = 0;
PerIoData->BytesToSend = PerIoData->wsaBuf.len;
}
if (WSASend(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesSent, 0, &(PerIoData->Overlapped), NULL) == 0)
continue;
if (WSAGetLastError() == WSA_IO_PENDING)
continue;
}
closesocket(PerIoData->Socket);
delete PerIoData;
}
return 0;
}
int main()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2,2), &WsaDat) != 0)
return 0;
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
return 0;
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i)
{
HANDLE hThread = CreateThread(NULL, 0, ServerWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (listenSocket == INVALID_SOCKET)
return 0;
SOCKADDR_IN server;
ZeroMemory(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(8888);
if (bind(listenSocket, (SOCKADDR*)(&server), sizeof(server)) != 0)
return 0;
if (listen(listenSocket, 1) != 0)
return 0;
std::cout << "Waiting for incoming connection...\r\n";
SOCKET acceptSocket;
do
{
sockaddr_in saClient;
int nClientSize = sizeof(saClient);
acceptSocket = WSAAccept(listenSocket, (SOCKADDR*)&saClient, &nClientSize, NULL, NULL);
}
while (acceptSocket == INVALID_SOCKET);
std::cout << "Client connected!\r\n\r\n";
CreateIoCompletionPort((HANDLE)acceptSocket, hCompletionPort, 0, 0);
LPPER_IO_DATA pPerIoData = new PER_IO_DATA;
ZeroMemory(pPerIoData, sizeof(PER_IO_DATA));
strcpy(pPerIoData->Buffer, "Welcome to the server!\r\n");
pPerIoData->Overlapped.hEvent = WSACreateEvent();
pPerIoData->Socket = acceptSocket;
pPerIoData->wsaBuf.buf = pPerIoData->Buffer;
pPerIoData->wsaBuf.len = strlen(pPerIoData->Buffer);
pPerIoData->BytesToSend = pPerIoData->wsaBuf.len;
DWORD dwNumSent;
if (WSASend(acceptSocket, &(pPerIoData->wsaBuf), 1, &dwNumSent, 0, &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
delete pPerIoData;
return 0;
}
}
while (TRUE)
Sleep(1000);
shutdown(acceptSocket, SD_BOTH);
closesocket(acceptSocket);
WSACleanup();
return 0;
}
Have you had a look at the example in the MSDN documentation for WSARecv?
Essentially, you have to start the asynchronous WSARecv operation first and then get notified via the completion port of its completion.
Or to put it another way: Windows I/O completion ports are using a proactor model (in contrast to the reactor model of Linux/FreeBSD/NetBSD).