Windows — Windws C ++ прерывистое разъединение гнезда

У меня есть сервер, который использует двухпоточную систему для управления от 100 до 200 одновременных соединений. Он использует TCP-сокеты, так как гарантия доставки пакетов важна (это система связи, где пропущенные удаленные вызовы API могут FUBAR-клиент).

Я реализовал специальный уровень протокола для разделения входящих байтов на пакеты и их правильной отправки (библиотека включена ниже). Я понимаю проблемы использования MSG_PEEK, но, насколько мне известно, это единственная система, которая будет отвечать потребностям реализации библиотеки. Я открыт для предложений, особенно если это может быть частью проблемы.

По сути, проблема в том, что случайным образом сервер отбрасывает сокет клиента из-за отсутствия входящих пакетов в течение более 20 секунд, несмотря на то, что клиент успешно отправляет пакет keepalive каждые 4. Я могу убедиться, что сам сервер этого не делал выйдите из сети, и соединение пользователей (включая меня), испытывающих проблему, будет стабильным.

Библиотека для отправки / получения находится здесь:

short ncsocket::send(wstring command, wstring data) {
wstringstream ss;
int datalen = ((int)command.length() * 2) + ((int)data.length() * 2) + 12;
ss << zero_pad_int(datalen) << L"|" << command << L"|" << data;
int tosend = datalen;
short __rc = 0;
do{
int res = ::send(this->sock, (const char*)ss.str().c_str(), datalen, NULL);
if (res != SOCKET_ERROR)
tosend -= res;
else
return FALSE;
__rc++;
Sleep(10);
} while (tosend != 0 && __rc < 10);
if (tosend == 0)
return TRUE;
return FALSE;
}

short ncsocket::recv(netcommand& nc) {
vector<wchar_t> buffer(BUFFER_SIZE);
int recvd = ::recv(this->sock, (char*)buffer.data(), BUFFER_SIZE, MSG_PEEK);
if (recvd > 0) {
if (recvd > 8) {
wchar_t* lenstr = new wchar_t[4];
memcpy(lenstr, buffer.data(), 8);
int fulllen = _wtoi(lenstr);
delete lenstr;

if (fulllen > 0) {
if (recvd >= fulllen) {
buffer.resize(fulllen / 2);
recvd = ::recv(this->sock, (char*)buffer.data(), fulllen, NULL);
if (recvd >= fulllen) {
buffer.resize(buffer.size() + 2);
buffer.push_back((char)L'\0');
vector<wstring> data = parsewstring(L"|", buffer.data(), 2);
if (data.size() == 3) {
nc.command = data[1];
nc.payload = data[2];
return TRUE;
}
else
return FALSE;
}
else
return FALSE;
}
else
return FALSE;
}
else {
::recv(this->sock, (char*)buffer.data(), BUFFER_SIZE, NULL);
return FALSE;
}
}
else
return FALSE;
}
else
return FALSE;

}

Это код для определения, прошло ли слишком много времени:

if ((int)difftime(time(0), regusrs[i].last_recvd) > SERVER_TIMEOUT) {
regusrs[i].sock.end();
regusrs[i].is_valid = FALSE;
send_to_all(L"removeuser", regusrs[i].server_user_id);

wstringstream log_entry;
log_entry << regusrs[i].firstname << L" " << regusrs[i].lastname << L" (suid:" << regusrs[i].server_user_id << L",p:" << regusrs[i].parent << L",pid:" << regusrs[i].parentid << L") was disconnected due to idle";
write_to_log_file(server_log, log_entry.str());
}

«Regusrs [i]» использует текущий итерированный член вектора, который я использую для описания дескрипторов сокетов и пользовательской информации. Проверка is_valid предназначена для того, чтобы определить, является ли связанный пользователь действительным пользователем — это сделано для того, чтобы система не имела необходимости освобождать член вектора — она ​​просто возвращает его в пул доступных слотов. Таким образом, нет проблем с доступом к потоку / вне диапазона.

Во всяком случае, я начал задаваться вопросом, был ли это сам сервер был проблемой. В настоящее время я тестирую на другом сервере, но я хотел посмотреть, сможет ли другой набор глаз остановить что-то неуместное или напомнить мне о концепции с сокетами и расширенной поддержкой активности, о которой я не знаю.

Заранее спасибо!

0

Решение

Я думаю, что я вижу, что вы делаете с MSG_PEEKгде вы ждете, пока у вас не появится достаточно данных для чтения полного пакета. Однако я бы с подозрением отнесся к этому. (Трудно определить динамическое поведение вашей системы, просто взглянув на эту небольшую часть источника, а не на всю.)

Чтобы избежать использования MSG_PEEKследуйте этим двум принципам:

  1. Когда вы получаете уведомление о том, что данные готовы (я предполагаю, что вы используете select), затем прочитайте все данные ожидания от recv(), Вы можете использовать более одного recv() вызов, так что вы можете обрабатывать входящие данные по частям.

  2. Если вы читаете только частичный пакет (длина или полезная нагрузка), сохраните его где-нибудь, чтобы в следующий раз вы получили уведомление о прочтении. Соберите вместе пакеты и полезные данные, не оставляйте их в буфере сокетов.

Кроме того, использование new/memcpy/wtoi/delete ужасно неэффективно. Вам вообще не нужно выделять память, вы можете использовать локальную переменную. И тогда вам даже не нужно memcpy вообще, просто актерский состав.

Я предполагаю, что вы уже предполагаете, что ваши пакеты могут иметь длину не более 999 байт.

2

Другие решения


По вопросам рекламы [email protected]