i've write a socket class in C++, the design is event-based, that means, every 64 players it will create a new thread to handle these sockets. My problem is, when i start reading a file, or write, or even copy and paste, it says "Server.exe is not responding", and when the process finish (disk), it starts responding again, disconnecting some players in my server.
Here is my listener:
void ServerSocket::ListenerThread(ServerSocket * Server)
{
// Inicializa a lista de espera.
InitializeCriticalSection(&Server->m_EventCheckers.Lock);
InitializeCriticalSection(&Server->m_Pending.Lock);
bool TerminateThread = false;
DWORD Result = 0;
WSANETWORKEVENTS NetEvents = { 0 };
// Enquanto for permitida a leitura de novas conexões.
while (WaitForSingleObject(Server->m_Listener.WaitObject, FALSE) != WAIT_OBJECT_0)
{
Result = WSAWaitForMultipleEvents(1, &Server->m_Listener.Event, FALSE, 2500, FALSE);
if (Result == WSA_WAIT_EVENT_0)
{
// Existe algo pendente no soquete.
// Enumera os eventos pendentes.
Result = WSAEnumNetworkEvents(Server->m_Listener.Socket, Server->m_Listener.Event, &NetEvents);
if (Result != SOCKET_ERROR)
{
// Verifica qual evento está ativo.
if (NetEvents.lNetworkEvents & FD_ACCEPT)
{
INT ErrorCode = NetEvents.iErrorCode[FD_ACCEPT_BIT];
if (ErrorCode == NULL)
{
// Aceita uma nova conexão.
Client* User = new Client();
if (User->AcceptFromSocket(Server->m_Listener.Socket))
{
int LastUserCount;
// Coloca na lista de espera e verifica as procedures.
EnterCriticalSection(&Server->m_Pending.Lock);
LastUserCount = Server->m_ConnectionCount++;
Server->m_Pending.Clients.push_front(User);
LastUserCount += Server->m_Pending.Clients.size();
LeaveCriticalSection(&Server->m_Pending.Lock);
EnterCriticalSection(&Server->m_EventCheckers.Lock);
if (Server->m_EventCheckers.Runnings.size() * WSA_MAXIMUM_WAIT_EVENTS < LastUserCount)
{
std::shared_ptr<ThreadInformation> Information = std::make_shared<ThreadInformation>();
Information->EventObject = CreateEventA(NULL, TRUE, FALSE, NULL);
Information->Thread.Initialize(std::thread(EventChecker, Server, Information));
Server->m_EventCheckers.Runnings.push_back(Information);
char szMessage[256] = { 0 };
sprintf_s(szMessage, "Número de Threads( %d ) -> Jogadores( %d )",
Server->m_EventCheckers.Runnings.size(), LastUserCount);
World::ServerManager->LogFile("Socket", szMessage);
}
LeaveCriticalSection(&Server->m_EventCheckers.Lock);
}
else
{
// Algum erro ocorreu durante o accept.
delete User;
}
}
else
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
else if (NetEvents.lNetworkEvents & FD_CLOSE)
{
INT ErrorCode = NetEvents.iErrorCode[FD_CLOSE_BIT];
if (ErrorCode == NULL)
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
else
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
}
else
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
else if (Result != WSA_WAIT_TIMEOUT)
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
if (TerminateThread)
{
Server->m_Listener.WaitObject = INVALID_HANDLE_VALUE;
WSACloseEvent(Server->m_Listener.Event);
closesocket(Server->m_Listener.Socket);
Log("Function: ServerSocket::ListenerThread(). Details: TerminateThread is true.\n");
}
// Deleta a lista de espera.
DeleteCriticalSection(&Server->m_Pending.Lock);
DeleteCriticalSection(&Server->m_EventCheckers.Lock);
}
And here is where i handle the socket.
void ServerSocket::EventChecker(ServerSocket * Server, std::shared_ptr<ThreadInformation> Information)
{
SOCKET Sockets[WSA_MAXIMUM_WAIT_EVENTS] = { INVALID_SOCKET };
WSAEVENT Events[WSA_MAXIMUM_WAIT_EVENTS] = { WSA_INVALID_EVENT };
DWORD LastConnection = GetCurrentTime();
DWORD LastUserCheck = NULL;
DWORD Result = NULL;
WSANETWORKEVENTS NetworkEvents = { 0 };
std::vector<std::shared_ptr<Client>> Clients;
// Predicate to Erase-Remove Idiom.
auto UserPredicate = [&](std::shared_ptr<Client> User)
{
if (!User || User->GetSocket() == INVALID_SOCKET || User->GetEvent() == WSA_INVALID_EVENT || User->GetFreeFlag())
return true;
return false;
};
while (WaitForSingleObject(Information->EventObject, FALSE) != WAIT_OBJECT_0)
{
// Se não tiver clientes conectados, sai da thread para economizar CPU.
if (Clients.empty() && GetCurrentTime() - LastConnection > 150)
{
SetEvent(Information->EventObject);
continue;
}
// Verifica se esta na hora de pegar os usuários na lista de espera.
if (LastUserCheck + 2000 < GetCurrentTime() && Clients.size() < WSA_MAXIMUM_WAIT_EVENTS)
{
// Possui algum usuário na lista de espera.
EnterCriticalSection(&Server->m_Pending.Lock);
// Obtêm o usuário da lista.
for (size_t i = 0; i < Server->m_Pending.Clients.size(); i++)
{
if (Clients.size() < WSA_MAXIMUM_WAIT_EVENTS)
{
Client * User = Server->m_Pending.Clients.back();
if (User)
{
// Obtêm o ownership do usuário com um ponteiro compartilhado.
Clients.push_back(std::move(std::shared_ptr<Client>(User)));
Server->m_Pending.Clients.pop_back();
}
}
else
break;
}
LeaveCriticalSection(&Server->m_Pending.Lock);
LastUserCheck = GetCurrentTime();
}
// Verifica por conexões não autenticadas.
for (int i = 0; i < (int)Clients.size(); i++)
{
if (!Clients[i]->isInitialized() && !Clients[i]->GetPlayerData())
{
if (Clients[i]->GetLastRecvTime() + Connection::MaxAutorizationTime < GetCurrentTime())
{
// Percorre todos os jogadores para desconectar o IP.
for (int c = 0; c < (int)Clients.size(); c++)
{
if (_strcmpi(Clients[i]->GetIP(), Clients[c]->GetIP()) == 0)
{
Clients[c]->CloseSocket();
}
}
}
}
}
// Verifica por conexões inválidas de time-out.
for (int i = 0; i < (int)Clients.size(); i++)
{
if (Clients[i]->GetLastRecvTime() + (60 * 1000) < GetCurrentTime())
{
if (!Clients[i]->isReceivingUpdate())
{
Clients[i]->CloseSocket();
}
}
}
// Verifica por eventos inválidos ou jogadores "recém desconectados".
Clients.erase(std::remove_if(Clients.begin(), Clients.end(), UserPredicate), Clients.end());
// Separa os eventos e os soquetes.
for (unsigned i = 0; i < Clients.size(); i++)
{
Sockets[i] = Clients[i]->GetSocket();
Events[i] = Clients[i]->GetEvent();
}
// Caso não exista um usuário, apenas continua o loop.
if (Clients.empty())
continue;
LastConnection = GetCurrentTime();
Result = WSAWaitForMultipleEvents(Clients.size(), Events, FALSE, 1000, FALSE);
if (Result != SOCKET_ERROR)
{
if (Result >= WSA_WAIT_EVENT_0 && Result < WSA_MAXIMUM_WAIT_EVENTS + WSA_WAIT_EVENT_0)
{
INT Index = Result - WSA_WAIT_EVENT_0;
if (Clients.size() >= 0 && Index < int(Clients.size()))
{
std::shared_ptr<Client> & User = Clients[Index];
Result = WSAEnumNetworkEvents(User->GetSocket(), User->GetEvent(), &NetworkEvents);
if (Result != SOCKET_ERROR)
{
INT ErrorCode = 0;
if (NetworkEvents.lNetworkEvents & FD_READ)
{
ErrorCode = NetworkEvents.iErrorCode[FD_READ_BIT];
if (ErrorCode == FALSE)
{
// O soquete está com dados pendentes.
char DataReceived[Connection::MAX_BufferSize] = { 0 };
int Len = recv(User->GetSocket(), DataReceived, sizeof(DataReceived), NULL);
if (Len > 0)
{
if (User->isFirstRecv())
{
if (Len >= 4)
{
User->Initialize();
}
else
{
Clients.erase(Clients.begin() + Index);
continue;
}
}
if (!User->PushRecv(DataReceived, Len))
{
Clients.erase(Clients.begin() + Index);
continue;
}
}
else
{
Clients.erase(Clients.begin() + Index);
}
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: FD_ReadBit is %d.\n", ErrorCode);
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
else if (NetworkEvents.lNetworkEvents & FD_WRITE)
{
ErrorCode = NetworkEvents.iErrorCode[FD_WRITE_BIT];
if (ErrorCode == FALSE)
{
// O soquete está livre para mandar dados.
User->WriteData();
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: FD_WriteBit is %d.\n", ErrorCode);
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
else if (NetworkEvents.lNetworkEvents & FD_CLOSE)
{
ErrorCode = NetworkEvents.iErrorCode[FD_CLOSE_BIT];
if (ErrorCode == FALSE)
{
User->GracefulShutdown();
Clients.erase(Clients.begin() + Index);
continue;
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: FD_CloseBit is %d.\n", ErrorCode);
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: Network ErrorCode is %d.\n", WSAGetLastError());
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
}
}
else if (Result != WSA_WAIT_TIMEOUT)
{
char szMessage[256] = { 0 };
sprintf_s(szMessage, "WSAWaitForMultipleEvents Error Code [ %d ]", Result);
World::ServerManager->LogFile("Socket", szMessage);
Sleep(100);
}
}
EnterCriticalSection(&Server->m_EventCheckers.Lock);
Information->EventObject = INVALID_HANDLE_VALUE;
for (unsigned i = 0; i < Server->m_EventCheckers.Runnings.size(); i++)
{
if (Server->m_EventCheckers.Runnings[i] == Information)
{
Server->m_EventCheckers.Runnings.erase(Server->m_EventCheckers.Runnings.begin() + i);
break;
}
}
LeaveCriticalSection(&Server->m_EventCheckers.Lock);
}
Related
Before this project, my only experience with C++ was with some basic arduino programming. I decided to suck it up and start working with C++ for my projects rather than trying to make python work for things it shouldn't, so I'm trying to figure out a lot of best practice things and simple issues.
I have written a program that has many classes inside of it. The classes have functions which do many things, but it may be important to note that some functions start threads or are continually looping inside a thread. I often need to call functions from another class from a different class, but that seems to be causing me issues. What I've been doing now is creating an object for that class I need to reference inside of the class I'm currently in, but that is causing issues with the order I put my classes in my code, for example:
(347,13): error C2079: 'anim' uses undefined class 'animation'
This error occurs in this section of the code:
class metronome {
private:
changeTempo tempo;
animation anim;
int lastManualTap = 0;
void beat() {
if (started) {
std::cout << "-------- " << j << "\n";
j++;
if (j == (globalBeatsPerMeasure + 1)) { j = 1; }
}
else {
std::cout << "--------\n";
}
}
//runs the click at a set tempo without user input
void autoMetro(float bpm) {
//global variable to stop the auto metro loop
while (autoActive) {
int bpmdelay = (60.0 / bpm) * 1000;
if ((clock() - lastBeatTime) >= bpmdelay) {
subdivideActive = false;
isFirst = false;
beat(); //console output
startSub(bpmdelay); //start subdivion
tempo.setBpm(bpm); //sets auto tempo to manual tempo
lastBeatTime = clock();
lastAutoBeatTime = clock();
}
}
return;
}
// outputs the subdivision at a set number of subdivisions per beat
void subdivide(int timeBetweenBeats) {
int timeBetweenSubdivision = timeBetweenBeats / globalSubdivision;
int b = 2;
int lastSubdivisionTime = clock();
subdivideActive = true;
while (subdivideActive == true) { // makes sure the subdivison is supposed to be on in case there is a manual tap before end of subdivision
//check if the amount of time between subdivisions has passed, print subdivision
if ((clock() - lastSubdivisionTime) >= timeBetweenSubdivision) {
lastSubdivisionTime = clock();
std::cout << b << "\n";
b++;
}
//when completed with set amount of subdivisions per beat, break
if (b == (globalSubdivision + 1)) {
b = 2;
subdivideActive = false;
}
}
}
//start subdivision
void startSub(int delay) {
std::thread subthread(&metronome::subdivide, this, delay);
subthread.detach();
}
//sdfusdhfus this is all trash anyways
void manualMetro() {
char c;
while (1) {
if (fileLoaded) {
c = _getch();
if (c) {
if (c == 'b') {
if ((clock() - lastAutoBeatTime) >= 300) {
subdivideActive = false;
beat();
this->startSub(clock() - lastBeatTime);
if (!isFirst) {
globalBPM = tempo.averageBpm((clock() - lastManualTap));
}
isFirst = false;
}
lastManualTap = clock();
lastBeatTime = clock();
autoActive = false;
}
else if (c == ' ' && autoActive == false) {
started = true;
subdivideActive = false;
this->startAutoMetroThread(globalBPM);
}
else if (c == 'r' && started == true) {
anim.initializeAnimationFile("animation.txt");
}
}
}
}
}
// start the auto metro thread
void startAutoMetroThread(float bpm) {
autoActive = true;
std::thread autometrothread(&metronome::autoMetro, this, bpm);
autometrothread.detach();
}
public:
// start thread to control the metronome
void startManualMetrothread() {
std::thread keyboard(&metronome::manualMetro, this);
keyboard.detach();
}
};
//class that reads from the animation file
class animation {
private:
metronome metro;
std::string line;
public:
void initializeAnimationFile(std::string animationFileName) {
std::ifstream animationFile(animationFileName);
autoActive = false;
started = false;
subdivideActive = false;
if (animationFile.is_open())
{
while (std::getline(animationFile, line))
{
if (line.rfind("#", 0) == 0) {
if (line.rfind("#=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set bpm
else if (line.rfind("#bpm=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set beats per measure
else if (line.rfind("#beatsPerMeasure=", 0) == 0) {
line.erase(0, 17);
globalBeatsPerMeasure = std::stoi(line);
std::cout << globalBeatsPerMeasure << " beats per measure\n";
}
//set subdivision
else if (line.rfind("#subdivision=", 0) == 0) {
line.erase(0, 13);
globalSubdivision = std::stoi(line);
std::cout << globalSubdivision << " subdivisions\n";
}
//signal that the settings have been located
else if (line.rfind("#start", 0) == 0) {
fileLoaded = true;
std::cout << "Ready!\n";
}
else {
std::cout << line << " command not recognized!\n";
}
}
else if (line.rfind("&", 0) == 0) {
}
}
animationFile.close();
}
}
};
This is my full code:
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include <time.h>
#include <stdio.h>
#include <iostream>
#include <thread>
#include <conio.h>
#include <atomic>
#include <fstream>
#include <string>
#include <sstream>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
//intialize global variables between threads
std::atomic<float> globalBPM(0);
std::atomic<int> globalSubdivision(0);
std::atomic<int> globalBeatsPerMeasure(0);
std::atomic<bool> isFirst(true);
std::atomic<bool> started(false);
std::atomic<bool> fileLoaded(false);
std::atomic<bool> autoActive(false);
std::atomic<int> lastAutoBeatTime(0);
std::atomic<int> lastBeatTime(0);
std::atomic<int> j(1);
std::atomic<bool> subdivideActive(false);
#define TCPListenerPort "17"
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
#define DEFAULT_BUFLEN 512
int iResult;
//array to hold ip addresses of each node at their id value
std::string nodeIP[100] = { "0" };
//array to hold battery voltage of each node at their id value
std::string nodeBatt[100] = { "0" };
class animation;
//class that handles all network transfers
class network {
private:
char recvbuf[DEFAULT_BUFLEN] = { 0 };
int recvbuflen = DEFAULT_BUFLEN;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
SOCKET Listen;
//thread that handles recieved connections from nodes
void TCPRequestRecvThread(SOCKET sock) {
do {
//get data from node, save it to string, output it
iResult = recv(sock, recvbuf, recvbuflen, 0);
if (iResult > 0) {
std::string str = recvbuf;
//printf("Bytes received: %d\n", iResult);
std::cout << str << "\n";
}
else if (iResult == 0) {
// printf("Connection closing :(((\n");
}
else {
printf("recv failed with error: %d D:\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
}
} while (iResult > 0);
//exit thread
return;
}
//start a thread to handle the node connection
void startTCPRequestRecvThread(SOCKET sk) {
std::thread t(&network::TCPRequestRecvThread, this, sk);
t.detach();
}
//loop thread that blocks until client connects and passes socket to recieve thread
void TCPListener(SOCKET Listen) {
while (true) {
// block and accept a client socket
ClientSocket = accept(Listen, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
WSACleanup();
}
//start communication thread
this->startTCPRequestRecvThread(ClientSocket);
}
}
public:
//start the TCP listener loop thread
void startTCPListenerThread(SOCKET sk) {
std::thread t(&network::TCPListener, this, sk);
t.detach();
}
SOCKET intializeTCPSocket() {
std::cout << "initil start\n";
struct addrinfo* result = NULL, * ptr = NULL, hints;
WSADATA wsaData;
//network tcp;
int iResult;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return INVALID_SOCKET;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, TCPListenerPort, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return INVALID_SOCKET;
}
// Create a SOCKET for connecting to server
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();
return INVALID_SOCKET;
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return INVALID_SOCKET;
}
freeaddrinfo(result);
//start listening on that socket
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return INVALID_SOCKET;
}
//return the setup listening socket
return ListenSocket;
}
};
//class that controls tempo changes
class changeTempo {
private:
int tapTimes[5] = { 0,0,0,0,0 };
int shiftArray[4];
int x = 0;
int add = 0;
public:
//change the average bpm when there is a manual user input
float averageBpm(int differance) {
//moves all the data down one position in the array so only 4 taps are averaged
if (x == 5) {
shift();
x = 4;
}
//store time between taps
tapTimes[x] = differance;
x++;
//add all the times together
add = 0;
for (int i = 0; i < 5; i++) {
add = add + tapTimes[i];
}
//find the number of user inputted taps in the array (so the bpm average is correct even if the user hasn't put in 4 beats before turning it to auto mode)
int num = 0;
for (int i = 0; i < 5; i++) {
if (tapTimes[i] > 0) { num++; }
}
//calculate and return bpm
return ((float)60 / (add / num))*1000;
}
//shift all the time between beats one position down (i know i'm lazy)
void shift() {
shiftArray[0] = tapTimes[1];
shiftArray[1] = tapTimes[2];
shiftArray[2] = tapTimes[3];
shiftArray[3] = tapTimes[4];
tapTimes[0] = shiftArray[0];
tapTimes[1] = shiftArray[1];
tapTimes[2] = shiftArray[2];
tapTimes[3] = shiftArray[3];
}
//set all of the times in the array to the auto time once auto is started
void setBpm(float bpm) {
for (int i = 0; i < 4; i++) {
tapTimes[i] = (60.0 / bpm) * 1000;
}
}
};
class metronome {
private:
changeTempo tempo;
animation anim;
int lastManualTap = 0;
void beat() {
if (started) {
std::cout << "-------- " << j << "\n";
j++;
if (j == (globalBeatsPerMeasure + 1)) { j = 1; }
}
else {
std::cout << "--------\n";
}
}
//runs the click at a set tempo without user input
void autoMetro(float bpm) {
//global variable to stop the auto metro loop
while (autoActive) {
int bpmdelay = (60.0 / bpm) * 1000;
if ((clock() - lastBeatTime) >= bpmdelay) {
subdivideActive = false;
isFirst = false;
beat(); //console output
startSub(bpmdelay); //start subdivion
tempo.setBpm(bpm); //sets auto tempo to manual tempo
lastBeatTime = clock();
lastAutoBeatTime = clock();
}
}
return;
}
// outputs the subdivision at a set number of subdivisions per beat
void subdivide(int timeBetweenBeats) {
int timeBetweenSubdivision = timeBetweenBeats / globalSubdivision;
int b = 2;
int lastSubdivisionTime = clock();
subdivideActive = true;
while (subdivideActive == true) { // makes sure the subdivison is supposed to be on in case there is a manual tap before end of subdivision
//check if the amount of time between subdivisions has passed, print subdivision
if ((clock() - lastSubdivisionTime) >= timeBetweenSubdivision) {
lastSubdivisionTime = clock();
std::cout << b << "\n";
b++;
}
//when completed with set amount of subdivisions per beat, break
if (b == (globalSubdivision + 1)) {
b = 2;
subdivideActive = false;
}
}
}
//start subdivision
void startSub(int delay) {
std::thread subthread(&metronome::subdivide, this, delay);
subthread.detach();
}
//sdfusdhfus this is all trash anyways
void manualMetro() {
char c;
while (1) {
if (fileLoaded) {
c = _getch();
if (c) {
if (c == 'b') {
if ((clock() - lastAutoBeatTime) >= 300) {
subdivideActive = false;
beat();
this->startSub(clock() - lastBeatTime);
if (!isFirst) {
globalBPM = tempo.averageBpm((clock() - lastManualTap));
}
isFirst = false;
}
lastManualTap = clock();
lastBeatTime = clock();
autoActive = false;
}
else if (c == ' ' && autoActive == false) {
started = true;
subdivideActive = false;
this->startAutoMetroThread(globalBPM);
}
else if (c == 'r' && started == true) {
anim.initializeAnimationFile("animation.txt");
}
}
}
}
}
// start the auto metro thread
void startAutoMetroThread(float bpm) {
autoActive = true;
std::thread autometrothread(&metronome::autoMetro, this, bpm);
autometrothread.detach();
}
public:
// start thread to control the metronome
void startManualMetrothread() {
std::thread keyboard(&metronome::manualMetro, this);
keyboard.detach();
}
};
//class that reads from the animation file
class animation {
private:
metronome metro;
std::string line;
public:
void initializeAnimationFile(std::string animationFileName) {
std::ifstream animationFile(animationFileName);
autoActive = false;
started = false;
subdivideActive = false;
if (animationFile.is_open())
{
while (std::getline(animationFile, line))
{
if (line.rfind("#", 0) == 0) {
if (line.rfind("#=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set bpm
else if (line.rfind("#bpm=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set beats per measure
else if (line.rfind("#beatsPerMeasure=", 0) == 0) {
line.erase(0, 17);
globalBeatsPerMeasure = std::stoi(line);
std::cout << globalBeatsPerMeasure << " beats per measure\n";
}
//set subdivision
else if (line.rfind("#subdivision=", 0) == 0) {
line.erase(0, 13);
globalSubdivision = std::stoi(line);
std::cout << globalSubdivision << " subdivisions\n";
}
//signal that the settings have been located
else if (line.rfind("#start", 0) == 0) {
fileLoaded = true;
std::cout << "Ready!\n";
}
else {
std::cout << line << " command not recognized!\n";
}
}
else if (line.rfind("&", 0) == 0) {
}
}
animationFile.close();
}
}
};
int main()
{
metronome metro;
animation ani;
network net;
SOCKET sk;
std::cout << "meow\n";
//initialize the TCP socket and check to see if it was initialized correctly
sk = net.intializeTCPSocket();
if (sk == INVALID_SOCKET) {
std::cout << "Error intializing socket!!\n";
}
//start the TCP listener thread
else {
net.startTCPListenerThread(sk);
}
metro.startManualMetrothread();
ani.initializeAnimationFile("animation.txt");
while (true) {}
}
I am not sure what is the correct fix for this issue
I use the following function to get the name of the connected WiFi connection. The function is called repeatedly every 4 seconds to monitor connection changes. But after a while, the function no longer works as before, and fails returning false.
bool GetConnectedConnectionsName(wstring connection_names[50], int& number)
{
HANDLE h_client = NULL;
DWORD dwMaxClient = 2;
DWORD dwCurVersion = 0;
DWORD result = 0;
DWORD dwRetVal = 0;
int iRet = 0;
WCHAR GuidString[39] = { 0 };
unsigned int i, j, k;
PWLAN_INTERFACE_INFO_LIST p_if_list = NULL;
PWLAN_INTERFACE_INFO p_if_info = NULL;
PWLAN_AVAILABLE_NETWORK_LIST p_bss_list = NULL;
PWLAN_AVAILABLE_NETWORK p_bss_entry = NULL;
int iRSSI = 0;
result = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &h_client);
if (result != ERROR_SUCCESS)
{
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return false;
}
result = WlanEnumInterfaces(h_client, NULL, &p_if_list);
if (result != ERROR_SUCCESS)
{
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return false;
}
for (i = 0; i < (int)p_if_list->dwNumberOfItems; i++)
{
p_if_info = (WLAN_INTERFACE_INFO*)&p_if_list->InterfaceInfo[i];
result = WlanGetAvailableNetworkList(h_client,
&p_if_info->InterfaceGuid,
0,
NULL,
&p_bss_list);
if (result != ERROR_SUCCESS)
{
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return false;
}
else
{
number = 0;
for (j = 0; j < p_bss_list->dwNumberOfItems; j++)
{
p_bss_entry = (WLAN_AVAILABLE_NETWORK*)&p_bss_list->Network[j];
if (p_bss_entry->dwFlags && (p_bss_entry->dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED))
{
connection_names[number] = wstring(p_bss_entry->strProfileName);
number++;
}
}
}
}
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return true;
}
What's wrong, and how can I solve the problem?
Good afternoon, recently discovered a problem in doing, is generating error 10035, generating several and several times the error causing the connection of the client is no longer remote and there is a lag without emulator. similarity with the error and I can not find a solution to it, if anyone can give me a light on how I can solve this error I thank. the code is as follows:
xxxx: An error occurred while sending the package. Total to be sent: 500. Error: 10035 xxxx: Error in sending the package. Total to be sent: 568. Error: 10035
BOOL CUser::SendMessageA(void)
{
if(Socket.Socket <= 0)
{
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
return false;
}
if(Socket.nSentPosition > 0)
RefreshSendBuffer();
if(Socket.nSendPosition > MAX_BUFFER || Socket.nSendPosition < 0 || Socket.Socket < 0)
{
Log(SERVER_SIDE, LOG_ERROR, "Send, 1");
return false;
}
if(Socket.nSentPosition > Socket.nSendPosition || Socket.nSentPosition > MAX_BUFFER || Socket.nSentPosition < 0)
{
Log(SERVER_SIDE, LOG_ERROR, "Send, 2");
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
}
INT32 err = 0;
for(INT32 i = 0; i < 1; i++)
{
INT32 LOCAL_68 = Socket.nSendPosition - Socket.nSentPosition;
INT32 LOCAL_69 = send(Socket.Socket, (char*)Socket.sendBuffer + Socket.nSentPosition, LOCAL_68, 0);
if(LOCAL_69 != -1)
Socket.nSentPosition += LOCAL_69;
else
err = WSAGetLastError();
}
if(err != 0)
{
CheckIdle(clientId);
Socket.Error++;
if(Socket.Error < 10)
Log(clientId, LOG_INGAME, "Erro no envio do pacote. Total a ser enviado: %d. Error: %d", Socket.nSendPosition, err);
}
if(Socket.nSentPosition >= Socket.nSendPosition || err != 0)
{
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
return true;
}
if(Socket.nSendPosition > MAX_BUFFER)
return false;
return true;
}
I do not know if it is possible to undo the following error or something of the type, because when it occurs it generates a disconnection of the user almost instantly, but it stays connected.
Error 10035 is WSAWOULDBLOCK, which is NOT a fatal error, but you are treating it as if it were.
You are obviously using a non-blocking socket, or you wouldn't be getting this "error" in the first place. As such, you need to handle the possibility that send() may fail to block the calling thread (because the receiver doesn't have enough buffer space to receive more data).
Simply retry the same send() operation again, preferably preceeding it with a call to select() to wait (with an optional timeout) for the receiver to free up some buffer space before you send data again.
Try something more like this:
BOOL CUser::SendMessageA(void)
{
if (Socket.Socket == INVALID_SOCKET)
{
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
return FALSE;
}
if (Socket.nSentPosition > 0)
RefreshSendBuffer();
if (Socket.nSendPosition > MAX_BUFFER || Socket.nSendPosition < 0 || Socket.Socket == INVALID_SOCKET)
{
Log(SERVER_SIDE, LOG_ERROR, "Send, 1");
return FALSE;
}
if (Socket.nSentPosition > Socket.nSendPosition || Socket.nSentPosition > MAX_BUFFER || Socket.nSentPosition < 0)
{
Log(SERVER_SIDE, LOG_ERROR, "Send, 2");
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
}
while (Socket.nSentPosition < Socket.nSendPosition)
{
INT32 numSent = send(Socket.Socket, (char*)Socket.sendBuffer + Socket.nSentPosition, Socket.nSendPosition - Socket.nSentPosition, 0);
if (numSent != SOCKET_ERROR)
Socket.nSentPosition += numSent;
else
{
INT32 err = WSAGetLastError();
if (err != WSAWOULDBLOCK)
{
CheckIdle(clientId);
Socket.Error++;
if (Socket.Error < 10)
Log(clientId, LOG_INGAME, "Erro no envio do pacote. Total a ser enviado: %d. Error: %d", Socket.nSendPosition, err);
return FALSE;
}
// optionally call select() here to wait for the socket to become writable again...
}
}
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
return TRUE;
}
Or, if you want the function to exit when a blockage occurs, and not wait for the data to fully send, but to finish sending at a later time:
BOOL CUser::SendMessageA(void)
{
if (Socket.Socket == INVALID_SOCKET)
{
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
return FALSE;
}
if (Socket.nSentPosition > 0)
RefreshSendBuffer();
if (Socket.nSendPosition > MAX_BUFFER || Socket.nSendPosition < 0 || Socket.Socket == INVALID_SOCKET)
{
Log(SERVER_SIDE, LOG_ERROR, "Send, 1");
return FALSE;
}
if (Socket.nSentPosition > Socket.nSendPosition || Socket.nSentPosition > MAX_BUFFER || Socket.nSentPosition < 0)
{
Log(SERVER_SIDE, LOG_ERROR, "Send, 2");
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
}
INT32 numSent = send(Socket.Socket, (char*)Socket.sendBuffer + Socket.nSentPosition, Socket.nSendPosition - Socket.nSentPosition, 0);
if (numSent != SOCKET_ERROR)
Socket.nSentPosition += numSent;
else
{
INT32 err = WSAGetLastError();
if (err != WSAWOULDBLOCK)
{
CheckIdle(clientId);
Socket.Error++;
if (Socket.Error < 10)
Log(clientId, LOG_INGAME, "Erro no envio do pacote. Total a ser enviado: %d. Error: %d", Socket.nSendPosition, err);
return FALSE;
}
}
if (Socket.nSentPosition >= Socket.nSendPosition)
{
Socket.nSendPosition = 0;
Socket.nSentPosition = 0;
}
return TRUE;
}
I began to study the anonymous pipe in windows and ran into a problem.
The program should create a child process that will handle the commands given ones to enter. But I can not read from the pipe until the child process works.
Here is the programm
#define _CRT_SECURE_NO_DEPRECATE
#include "windows.h"
#include "cstdio"
#include "tchar.h"
#include "io.h"
#include "conio.h"
#include "cstring"
static const int bufSize = 20;
int main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPTSTR szCmdline = _tcsdup(TEXT("Project2\\Release\\command_handler.exe"));
HANDLE writePipe, readPipe, writePipeInput, readPipeInput;
SECURITY_ATTRIBUTES sa;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if(!CreatePipe(&readPipe, &writePipe, &sa, 0) || !CreatePipe(&readPipeInput, &writePipeInput, &sa, 0))
{
printf("ERROR: cannot create pipe\n");
system("pause");
exit(1);
}
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = writePipe;
si.hStdInput = readPipeInput;
DWORD dwByteRead, dwByteWrite;
if(!CreateProcess(0, szCmdline, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi)) {
printf("ERROR: cannot create process\n");
system("pause");
exit(2);
}
CloseHandle(writePipe);
CloseHandle(readPipeInput);
printf("Procces ready!\n");
TQueue <int> q;
TQueue <int> tmp;
char c;
char s[bufSize];
int value;
for ( ; ; ) {
int i;
for(i = 0;i < bufSize && (s[i] = getchar()) != '\n';++i);
WriteFile(writePipeInput,s,i + 1,&dwByteWrite,NULL);
ReadFile(readPipe, &c, (int)1, &dwByteRead, NULL);
printf("Get : '%c'\n",c);
switch (c) {
case '+':
ReadFile(readPipe, &value, sizeof(int), &dwByteRead, NULL);
printf("%d\n",value);
q.push(value);
printf("OK\n");
break;
case '-':
if (!q.empty()) {
q.pop();
}
printf("OK\n");
break;
case 'f':
if (!q.empty()) {
printf("%d\nOK\n",q.firstEl());
} else {
printf("Queue is empty\nOK\n");
}
break;
case 'p':
if (q.empty()) {
printf("empty");
}
while (!q.empty()) {
tmp.push(q.firstEl());
printf("%d ",q.firstEl());
q.pop();
}
printf("\nOK\n");
while (!tmp.empty()) {
q.push(tmp.firstEl());
tmp.pop();
}
break;
case 'e':
printf("%s\nOK\n",q.empty()?"yes!":"no");
break;
case 'q':
break;
case '?':
printf("ERROR: undefined command\n");
break;
default:
printf("Fatal error: wrong symbol from hadeler '%c'\n",c);
//system("pause");
//exit(3);
}
if (c == 'q') break;
}
CloseHandle(readPipe);
CloseHandle(writePipeInput);
printf("Exited normaly\n");
system("pause");
}
And this is handler
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
static const int sz = 10;
char read_cmd(char *s) {
char c = getchar();
int i;
for (i = 0;(i < sz) && (c != ' ') && (c != '\n');i++) {
s[i] = c;
c = getchar();
// printf("%c",s[i]);
}
s[i] = '\0';
return c;
}
char correct(char res) {
if (res != '\n') {
while (getchar() != '\n');
return 0;
}
return 1;
}
int main() {
char s[sz + 1];
char c, res;
int val = 0;
for ( ; ; ) {
res = read_cmd(s);
if (strcmp(s,"add") == 0) {
val = 0;
while ((c = getchar()) >= '0' && c <= '9') {
val = val * 10 + c - '0';
}
res = c;
c = '+';
}else if (strcmp(s,"pop") == 0) {
c = '-';
}else if (strcmp(s,"first") == 0) {
c = 'f';
}else if (strcmp(s,"print") == 0) {
c = 'p';
}else if (strcmp(s,"empty") == 0) {
c = 'e';
}else if (strcmp(s,"help") == 0) {
c = 'h';
}else if (strcmp(s,"quit") == 0) {
c = 'q';
}else {
c = '?';
}
if (!correct(res)) c = '?';
printf("%c",c);
if (c == 'q') break;
if (c == '+') printf("%d",val);
}
return 0;
}
So if I run it and immediately type "quit" it works, otherwise it stop when trying get result from output pipe.
Why is this happening?
Thanks in advance for your reply.
I rewrote the program again and it worked. Honestly, I do not know that there was not correct. Here is a working version:
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 256
template <class T> class TQueue {
private:
class TQueEl {
public:
T value;
TQueEl *next, *prev;
};
TQueEl *first, *last;
int size;
public:
int& operator = (TQueue <T>& b){
while (!empty ()) {
pop ();
}
while (!b.empty ()) {
push(b.firstEl ());
b.pop ();
}
}
TQueue () {
size = 0;
first = last = nullptr;
}
void push (T val) {
if (size == 0) {
first = last = new TQueEl;
first -> next = first -> prev = nullptr;
first -> value = val;
} else {
last -> next = new TQueEl;
last -> next -> prev = last;
last = last -> next;
last -> next = nullptr;
last -> value = val;
}
size++;
}
T firstEl () {
return first -> value;
}
void pop () {
TQueEl *tmp=first;
if (size == 1) {
first = last = nullptr;
}
else if ( empty() ) {
// error("pop of empty queue");
}
else {
first = first -> next;
first -> prev = nullptr;
}
size--;
delete tmp;
}
bool empty () {
return !size;
}
~TQueue () {
TQueEl *tmp;
while (first != nullptr) {
tmp = first;
first = first -> next;
delete tmp;
}
}
};
DWORD WINAPI rerout(LPVOID params) {
DWORD dwByteWrite, dwByteRead;
char buffer[BUFSIZE];
HANDLE hParentStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE childInput = (HANDLE) params;
for ( ; ; ) {
ReadFile(hParentStdIn, buffer, BUFSIZE, &dwByteRead, NULL);
WriteFile(childInput, buffer, dwByteRead, &dwByteWrite, NULL);
}
return 0;
}
int main() {
HANDLE childInputRd = NULL;
HANDLE childInputWr = NULL;
HANDLE childOutputRd = NULL;
HANDLE childOutputWr = NULL;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if ( ! CreatePipe(&childOutputRd, &childOutputWr, &sa, 0) ) {
printf("Fatal error: cannot create child output pipe\n");
system("pause");
exit(1);
}
if (! CreatePipe(&childInputRd, &childInputWr, &sa, 0)) {
printf("Fatal error: cannot create child input pipe\n");
system("pause");
exit(2);
}
TCHAR szCmdline[]=TEXT("Project2\\Release\\command_handler.exe");
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
si.hStdError = childOutputWr;
si.hStdOutput = childOutputWr;
si.hStdInput = childInputRd;
si.dwFlags = STARTF_USESTDHANDLES;
if ( !CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) ) {
printf("Fatal error: cannot create child process\n");
system("pause");
exit(3);
}
else {
CloseHandle(childInputRd);
CloseHandle(childOutputWr);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
DWORD reroutThreadID;
HANDLE inputRerouting = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)rerout,(LPVOID) childInputWr,0 , &reroutThreadID);
if (inputRerouting == NULL) {
printf("Fatal error: cannot create thread\n");
system("pause");
exit (4);
}
TQueue <int> q;
TQueue <int> tmp;
DWORD dwRead;
CHAR c;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;) {
ReadFile( childOutputRd, &c, sizeof(char), &dwRead, NULL);
switch (c) {
case '+':
int value;
ReadFile( childOutputRd, &value, sizeof(int), &dwRead, NULL);
q.push(value);
printf("OK\n");
break;
case '-':
if (!q.empty()) {
q.pop();
printf("OK\n");
}
else {
printf("Queue is empty!\n");
}
break;
case 'f':
if (!q.empty()) {
printf("%d\n",q.firstEl());
} else {
printf("Queue is empty!\n");
}
break;
case 'p':
printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");
if (q.empty()) {
printf("empty");
}
while (!q.empty()) {
tmp.push(q.firstEl());
printf("%d ",q.firstEl());
q.pop();
}
while (!tmp.empty()) {
q.push(tmp.firstEl());
tmp.pop();
}
printf("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\nOK\n");
break;
case 'e':
printf("%s\n",q.empty()?"YES":"NO");
break;
case 'q':
break;
case '?':
printf("ERROR: undefined command\n");
break;
default:
printf("Fatal error: wrong symbol from hadeler '%c'\n",c);
system("pause");
exit(5);
}
if (c == 'q') break;
}
CloseHandle(inputRerouting);
CloseHandle(childInputWr);
CloseHandle(childOutputRd);
system("pause");
return 0;
}
And the handler:
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 5
HANDLE hStdin, hStdout;
DWORD dwRead, dwWritten;
void readCmd(char* s) {
ReadFile(hStdin, s, 1, &dwRead, NULL);
int i;
for (i = 0;(i < BUFSIZE) && (s[i] != ' ') && (s[i] != '\n') && (s[i] != '\r');i++) {
ReadFile(hStdin, s + i + 1, 1, &dwRead, NULL);
}
s[i] = '\0';
}
bool correct() {
char c;
ReadFile(hStdin, &c, sizeof(char), &dwRead, NULL);
if (c != '\n') {
while (c != '\n') ReadFile(hStdin, &c, sizeof(char), &dwRead, NULL);
return 0;
}
return 1;
}
int main() {
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if ( (hStdout == INVALID_HANDLE_VALUE) || (hStdin == INVALID_HANDLE_VALUE) ) {
ExitProcess(1);
}
int val;
char s[BUFSIZE + 1];
char c,digit;
for (;;) {
readCmd(s);
if (strcmp(s,"add") == 0) {
val = 0;
ReadFile(hStdin, &digit, 1, &dwRead, NULL);
if (digit == '\n') {
c = '?';
WriteFile(hStdout,&c,sizeof(char),&dwRead,NULL);
continue;
}
else if (digit < '0' || digit > '9') {
c = '?';
}
else {
c = '+';
}
while (digit >= '0' && digit <= '9') {
val = val * 10 + digit - '0';
ReadFile(hStdin, &digit, 1, &dwRead, NULL);
}
}else if (strcmp(s,"pop") == 0) {
c = '-';
}else if (strcmp(s,"first") == 0) {
c = 'f';
}else if (strcmp(s,"print") == 0) {
c = 'p';
}else if (strcmp(s,"empty") == 0) {
c = 'e';
}else if (strcmp(s,"help") == 0) {
c = 'h';
}else if (strcmp(s,"quit") == 0) {
c = 'q';
}else {
c = '?';
}
if (!correct()) c = '?';
WriteFile(hStdout, &c, 1, &dwRead, NULL);
if (c == 'q') break;
if (c == '+') WriteFile(hStdout, &val, sizeof(int), &dwRead, NULL);
}
return 0;
}
Thank you all for your help!
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;
}