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;
}
Related
I'm having an issue with winsock on windows 8.1 where recv keeps returning 0 randomly. I'm running both client and server on the same machine (thus all traffic is pointed at the loopback address) and i have breakpoints on any statement on both client and server which would shut down the sockets. But when the issue occurs the server is still operating normally, and hasn't shutdown anything, while the client has hit a breakpoint that only triggers on recv returning 0 or less.
The client keeps returning 0 from recv randomly (although always at the same point in my code) when the server VS 2013 project is set to compile as a windows program (rather than a console, in order to make it produce no window, it's supposed to be silent running). The bug doesn't seem to occur when i compile the server as a console application, as I've been debugging the program in that mode and only come across this issue when i switched compilation settings.
Is there any way to launch a console window when compiling as a windows application so i can see any debug statements?
Does winsock behave differently when compiling for the console vs compiling for a windows application?
Why would the client's recv return 0, when I've not sent a shutdown signal from the server?
My code, ask if there's any more you need:
Client
void recvLoop()
{
int recievedBytes = 1;
while (running && recievedBytes > 0)
{
WORD dataSize;
WORD dataType;
int recievedBytesA = ConnectSock.Recieve(&dataSize, sizeof(WORD));
if (recievedBytesA <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
int recievedBytesB = ConnectSock.Recieve(&dataType, sizeof(WORD));
if (recievedBytesB <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
unique_ptr<char[]> data(new char[dataSize]);
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true; //breakpoint set here - Always triggers here
attemptKillThreads();
continue;
}
//use the received data.....
}
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
ConnectSock is a global of type ConnectSocket. here is its Recieve()
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock != INVALID_SOCKET)
{
int i = recv(sock, (char *)recvbuf, recvbuflen, 0);
if ((i == SOCKET_ERROR))
{
int err = 0;
err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
}
return i;
}
return -2;
}
Server:
void sendLoop()
{
int bytessent = 0;
QueuePack tosend;
while (running)
{
tosend = sendQueue.Dequeue();
if (tosend.packType == QueuePack::EXIT || tosend.packType == 0 || tosend.dSize == 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.dSize, sizeof(WORD));
//cout used to see what exactly is being sent, even if it is garbage when converted to text
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.packType, sizeof(WORD));
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(tosend.bufferPtr(), tosend.dSize);
cout << tosend.bufferPtr() << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
}
}
if (Connection->shutdownSock(SD_SEND) == SOCKET_ERROR)
{
Connection->closeSock();
}
}
SendData is literally a wrapper for send that uses a reinterpret_cast
int SendData(void * writeBuffer, int bufferSize)
{
return send(SocketManager.clientSocket, reinterpret_cast<char *>(writeBuffer), bufferSize, 0);
}
sendQueue is a Bounded blocking queue that holds QueuePacks
QueuePacks are used to transfer data, it's size and what kind of data it is between threads. both Client and server use this as it allows me to make sure data gets to the right thread on the client
Queuepack has 2 public variables packType and dSize of type WORD.
QueuePack::QueuePack() : packType(UND), dSize(0)
{
int h = 0; //debug statement to break on - never gets hit after initial collection construction occurs
}
QueuePack::QueuePack(const WORD type, WORD size, char * data) : packType(type), dSize(size)
{
//debug test and statement to break on
if (size == 0 || type == 0)
{
int h = 0; //breakpoint - never gets hit
}
dSize = (dSize < 1 ? 1 : dSize);
_buffer = make_unique<char[]>(dSize);
memcpy(_buffer.get(), data, dSize);
}
QueuePack::QueuePack(QueuePack &other) : packType(other.packType), dSize(other.dSize)
{
//debug test and statement to break on
if (other.dSize == 0 || other.packType == 0)
{
int h = 0; //breakpoint - never gets hit
}
if (&other == this)
{
return;
}
_buffer = make_unique<char[]>(dSize);
other.buffer(_buffer.get());
}
QueuePack QueuePack::operator= (QueuePack &other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
// reuse storage when possible
if (dSize != other.dSize)
{
_buffer.reset(new char[other.dSize]);
dSize = other.dSize;
}
packType = other.packType;
other.buffer(_buffer.get());
return *this;
}
QueuePack::~QueuePack()
{
}
HRESULT QueuePack::buffer(void* container)
{
try
{
memcpy(container, _buffer.get(), dSize);
}
catch (...)
{
return E_FAIL;
}
return S_OK;
}
char * QueuePack::bufferPtr()
{
return _buffer.get();
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
You are calling ConnectSock.Recieve() when dataSize is 0. There is nothing to read, so Receive() reports that 0 bytes were read.
You need to add a check for that condition:
unique_ptr<char[]> data(new char[dataSize]);
if (dataSize != 0) // <-- add this
{
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true;
attemptKillThreads();
continue;
}
}
Your code is also assuming that Recieve() reads all bytes that are requested, it is not handling the possibility that recv() may return fewer bytes. So you need to make Recieve() call recv() in a loop to guarantee that everything requested is actually read:
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(recvbuf);
int total = 0;
while (recvbuflen > 0)
{
int i = recv(sock, buf, recvbuflen, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
if (i == 0)
{
cout << "disconnected" << endl;
return 0;
}
buf += i;
recvbuflen -= i;
total += i;
}
return total;
}
Same with SendData(), as send() may return fewer bytes than requested:
int SendData(void * writeBuffer, int bufferSize)
{
if (SocketManager.clientSocket == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(writeBuffer);
int total = 0;
while (bufferSize > 0)
{
int i = send(SocketManager.clientSocket, buf, bufferSize, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
buf += i;
bufferSize -= i;
total += i;
}
return total;
}
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;
}
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
I have some questions about when it is needed to store the data in the wait buffer (waiting for the FD_WRITE event).
This is my send function (fixed):
bool MyClass::DataSend(char *buf, int len)
{
if (len <=0 || m_Socket == INVALID_SOCKET) return false;
if (m_SendBufferLen > 0)
{
if ((m_SendBufferLen + len) < MAX_BUFF_SIZE)
{
memcpy((m_SendBuffer + m_SendBufferLen), buf, len);
m_SendBufferLen += len;
return true;
}
else
{
// close the connection and log insuficient wait buffer size
return false;
}
}
int iResult;
int nPosition = 0;
int nLeft = len;
while (true)
{
iResult = send(m_Socket, (char*)(buf + nPosition), nLeft, 0);
if (iResult != SOCKET_ERROR)
{
if (iResult > 0)
{
nPosition += iResult;
nLeft -= iResult;
}
else
{
// log 0 bytes sent
break;
}
}
else
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if ((m_SendBufferLen + nLeft) < MAX_BUFF_SIZE)
{
// log data copied to the wait buffer
memcpy((m_SendBuffer + m_SendBufferLen), (buf + nPosition), nLeft);
m_SendBufferLen += nLeft;
return true;
}
else
{
// close the connection and log insuficient wait buffer size
return false;
}
}
else
{
// close the connection and log winsock error
return false;
}
}
if (nLeft <= 0) break;
}
return true;
}
My send (FD_WRITE event) function (fixed):
bool MyClass::DataSendEvent()
{
if (m_SendBufferLen < 1) return true;
int iResult;
int nPosition = 0;
int nLeft = m_SendBufferLen;
while (true)
{
iResult = send(m_Socket, (char*)(m_SendBuffer + nPosition), nLeft, 0);
if (iResult != SOCKET_ERROR)
{
if (iResult > 0)
{
nPosition += iResult;
nLeft -= iResult;
}
else
{
// log 0 bytes sent
break;
}
}
else
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if (nPosition > 0)
{
memmove(m_SendBuffer, (m_SendBuffer + nPosition), (m_SendBufferLen - nPosition));
m_SendBufferLen -= nPosition;
}
break;
}
else
{
// close the connection and log winsock error
return false;
}
}
if (nLeft <= 0)
{
if (m_SendBufferLen == nPosition)
{
m_SendBufferLen = 0;
break;
}
else
{
memmove(m_SendBuffer, (m_SendBuffer + nPosition), (m_SendBufferLen - nPosition));
m_SendBufferLen -= nPosition;
nPosition = 0;
nLeft = m_SendBufferLen;
}
}
}
return true;
}
Do I really need the if (nPosition > 0) or not? How do I simulate this scenario? Is there possible send() in non-blocking mode send less bytes than the requested? If not, why using the while() looping?
This is the final code (thanks to #user315052)
At the top of your while loop, you are already decrementing nLeft, so you don't need to subtract nPosition from it.
iResult = send(m_Socket, (char*)(buf + nPosition), nLeft, 0);
In your second function, when you are shifting the unsent bytes to the beginning of the array, you should use memmove, since you have overlapped regions (you are copying a region of m_SendBuffer into itself). The overlap is illustrated below, where some of the A's would get copied onto itself.
m_SendBuffer: [XXXXAAAAAAAAAAAA]
mPosition: 4
nLeft: 12
I am a little confused about why DataSend() is implemented to allow the caller to keep calling it with success even when the WSAEWOULDBLOCK is encountered. I would suggest the interface be modified to return a result that lets the caller know that it should stop sending, and to wait for an indication to resume sending.
You don't need the nPosition > 0 check.
You can force the case to occur by having the receiver of the data not read anything.
It is definitely possible for send in non-blocking mode to send fewer bytes than requested.
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);
}