Чтобы добавлять сообщения и комментарии, .

@fuze:
fuze

мигаем диодом ikalogic.com

@Spumer:
Spumer

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

Во-вторых, исследуя траффик общения сервера и клиента игры Left 4 Dead 2, удалось выявить особый и, разумеется, недокументированный тип запроса.
Он предназначен для отображения списка серверов в главном меню и пакеты такого запроса содержат номер версии игры и текст InetSearchServerDetails. Стоит сказать, что для этого типа пакетов сервер уже реализует анти-ддос защиту: проверяет версию и следит за числом pps.
Но ситуация усугубляется тем, что для данного типа запросов нет никакого мастер-сервера. Точнее он есть, и возвращает лишь список серверов которые привязаны к Steam-группам в которых ты состоишь. И при получении такого списка клиент сразу же шлет INET_SEARCH-пакеты всем серверам.

Тем не менее, даже с кэшированием сервер тратит драгоценное время на проверку запроса и отправку ответа. Забегая вперед скажу, что полностью сняв с сервера эту обязанность мне удалось снизить задержки в игре (пинг) в 2, а то и 3 раза.

Решение достаточно простое: сервер не должен отвечать на все эти запросы. Вообще. Эту задачу можно попробовать переложить на плечи какой-нибудь службы, которая бы отвечала на весь этот флуд, изредка запрашивала актуальные данные с основного сервера, а ОСь бы грамотно распределяла ресурсы.

Но как отделить пакеты запросов инфы, от обычного обмена данными с сервером? Первой попыткой было использовать statefull firewall. Это обычный знакомый нам фаерволл, который следит за подключениями, может их маркеровать и тд. Не получилось. Вот так просто. Даже поймав пакет и отправив его куда надо (сторонней службе), фаерволл пометит соединение и все что придет с пары ip:port на пару ip:port будет отправлено туда же.
PS: Многие просто не смогут подключиться, так как клиент как правило использует тот же сокет для обмена данными, что и для запроса информации.

stateless firewall я даже не ковырял, и как я потом понял - он мне бы и не подошел.

Остался самый очевидный и как следствие - простой - способ отделить один пакет и направить куда надо. Это модуль ядра. И это сработало!
Я зарегистрировал обработчик пакетов самым первым (там можно выставлять приоритет), еще пока он обрабатывается в пределах RAW таблицы и никто его в глаза не видел. Нагло подменяю порт отправки, пересчитываю контрольную сумму и пропускаю его дальше! А дальше он проходит через воду, огонь и медные трубы и попадает прямо к службе, которая слушает нужный порт. Ответы от службы обратно транслируются и порт подменяется при отправлении.
Для простоты и удобства порты имеют жесткую связку:
27015 -> 27915
27016 -> 27916
...

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

Перспектив для развития достаточно много, можно отвечать не всем, проще управлять лимитами, детектировать атаки и собирать разного рода статистику. В общем, все что душе угодно. И главное - всё это не будет отнимать процессорного времени у сервера (игрового).

@Spumer:
Spumer

Хотелось бы сказать "по долгу службы", ан нет. Не в столь отдаленном прошлом необходимо было решить проблему с "DDoS". Это была не спланированная атака, никто не хотел причинить вред или что-то подобное. Нет. Просто слишком много людей хотело спросить сервер о его состоянии, но обо всем по порядку.

Речь идет о source-based серверах (игровых, на движке Source). При запуске такой сервер отправляет информацию о себе мастер-серверу, и уже с мастер-серевером происходит всё общение клиентов: получение списка серверов и базовая информация. Подробная инфофрмация будет запрошена напрямую. Такое общение происходит по спец. протоколу, который описан тут https://developer.valvesoftware.com/wiki/Server_queries и тут https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol

Атаковать сервер не так уж сложно, достаточно начать флудить запросами, а если еще провайдер позволяет ip-spoofing и на другой стороне нет даже rp-фильтра, то дело за малым. Проблему люди быстро научились решать кэшированием: https://forums.alliedmods.net/showthread.php?t=135543 Кэшируют конечно не всё, но ситуация стала определенно лучше. Проблема же кроется в другом, а для Left 4 Dead 2 их целых две.

@utoplen-nick:
utoplen-nick

Я не поцреот, но вот оно свершилось — по качеству литературы мы снова уже вполне на мировом уровне, не стыдно показать иностранцам. Но есть один маааленький минус — издают только полное говно, ну или изредка этак стыдливо выкидывают проходняк. И тиражи пиздецки низкие.
С колен, ёбнрот.

@likbezov:
likbezov

лонг дублирует инт? зачем?

@byt3:
byt3

Алсо, неспешно разыскиваю работу в каких-то ебенях, кроме ДС и прочих ебаных муравейников. Могу в сишку, пистон, на крайняк поадминить линуксы. А в идеале пойду к каким-нибудь спамерам-ботоводам. После проверки, разумеется.

@Strephil:
Strephil

Есть какое-то внятное объяснение, почему в struct timeval, например, где время в секундах и микросекундах, секунда не равна 1048576???
Почему нельзя было сделать нормально и удобно???

@batekman:
batekman

Народ, подскажите какие-нибудь аудиокниги о философии программирования, в частности, на C, но желателен упор на общее понимание.

@qark:
qark

Лузер.

@akastargazer:
akastargazer

Отличный тип enum. Перечислить можно что угодно. Только сколько под это памяти отведётся — загадка. Только опция компилятора даст ответ. Уродство, одно слово.

@nox:
nox

Все Свидетели Иеговы — уроды.

@6Volt:
6Volt

По сцылке чистое зло.
Высокой концетрации.
gist.github.com

@Spumer:
Spumer

За несколько часов я перепробовал почти все возможные и невозможные варианты, и когда провалилась бинарная запись в файл — я понял — надо что-то менять, не в данных, не во мне, а в подходе. До того момента я старался избегать "грязных" хаков, трюков, основанных на знании работы компилятора и задачи программы, за которые она /никогда/ не выйдет. Я старался абстрагироваться, делать все максимально прозрачно (насколько мне позволяла лень :Р) и грамотно. Но у меня не осталось выхода. 

И так, я знал три(?) вещи: 
1. Итераторы не трогали данные, во всяком случае без моего приказа.
2. Итераторы предоставляли прямой доступ к данным
3. При создании итераторов я использовал метод std::basic_string c_str()/data() из описания которых следует: Modifying the character array accessed through data is *undefined behavior*.
В дополнение ко всему этому я знал, что строка изменяться уже не будет, и взглотнув побольше воздуха я сделал это — получил прямой доступ к данным через итератор, нагло избавился от const модификатора, который имел место быть благодаря c_str()/data() и стал записывать туда данные.
В исходном тексте замена пробельного символа на перенос стала выглядеть так: 	(*(xmlChar*)u8_word.base()) = '\n';
Дописав код я скомпилировал, запустил и... выдохнул. Оно работало! Данные отображались верно, без доп. копирования, проверок (ранее я пытался записывать по слову) и прочего. "Dirty hack" — то самое выражение, которое пришло в голову сразу после получения (ожидаемого?) результата.

Будь у меня более свежая голова, мозг товарища и пицца, я думаю мы разделались бы с этим за 10 минут. Но это был веселый опыт разработки. Больше, пожалуй, мне добавить нечего. Пишите код правильно ребята, не давайте доводить себя до отчаянья. У меня не было ни времени ни возможности обратиьтся к товарищу, и я решил эту проблему иначе.
Тем не менее, ссылки по теме:
https://github.com/spumer/tenzor-test/ — исходный код, виновник торжества.
http://en.cppreference.com/w/cpp/string/basic_string/data — ... through data is undefined behavior.
http://utfcpp.sourceforge.net/ — utf8 C++ portable library
http://www.xmlsoft.org/html/libxml-xmlstring.html#xmlChar

@Spumer:
Spumer

Сталось так, что при работе с библиотекой libxml2 понадобилось подсчитывать кол-во символов в строке, с целью ограничить длину строки в N символов и остальную ее часть перенести на новую(строку). Символы в этой строке естественно могут занимать от 1 до 6 байт, в соответствии с форматом UTF-8. Самолично парсить строку по стандарту не было никакого желания, а уж темболее проходиться по граблям желания было еще меньше. Благо это тема довольно изъезженная и решение в виде небольшой портабильной библиотеки уже было написано.

Язык С++, и библиотека была написана в виде итераторов для прохода по последовательности байт и функций, которые осуществляют некоторые простые операции как с итераторами, так и с самими последовательностями. libxml2 для хранения данных использует свой собственный тип xmlChar, хотя на деле это ни что иное как unsigned char. Камнем преткновения стала запись в файл, ведь именно туда мне было необходимо выгрузить все отформатированные данные. unsigned char в принципе не хотел записываться в файл и каждый раз он получался пустым, запись же в бинарном виде либо полностью колечило данные, либо их non-ASCII составляющую. Все попытки воссоздать правильную последовательность xmlChar байт с разбивкой на N символов в строке заканчивались одним — полный бред на выходе.

Немного о том, как работает UTF-8 библиотечка: созданные итераторы как и предполагается не трогают данные по которым следуют, но доступ к ним предоставляют. Доступ к символу в моем случае можно было осуществить двумя способами: operator* и метод base(), где первый возвращает данные типа unsigned int, второй же возвращает указатель на место в последовательности, в котором он в данный момент находится. Так, например, если *u8_it вернет нам 'c', то (*u8_it.base()) будет равен &("ascending"[2]). Если же вы(как и я) передавали при создании итератора константный указатель на данные (e.g. const xmlChar *) то при использовании base() для записи вам придется избавиться от const модификатора, но об этом далее.

@Spumer:
Spumer

Закинул на свой гитхаб бетку тестового задания, в состояние релиза должно перейти сегодня вечером ;) https://github.com/spumer/tenzor-test

@Spumer:
Spumer

Знаете, а libxml2 забавная штука. Например, пустое пространство определяет как тег text, и чтобы полноценно запарасить без фантомов приходится задавать флаг HTML_PARSE_NOBLANKS при обработке, а так же дополнительно проверять node->name на соответствие NULL или "text".
void parseNode(htmlNodePtr node, int level) {
	while(node != NULL) {
		/* skip blank tags */
		if( node->name != NULL && xmlStrcmp(node->name, (const xmlChar *)"text") ) {
			printf("TAG: %s. Level: %d\n", node->name, level);
			if(node->xmlChildrenNode) parseNode(node->xmlChildrenNode, level+1);
		}
		node = node->next;
	}
	return;
}

@Spumer:
Spumer

Что более тру?
		uint32_t (*piVersusSurvivorCompletion)[4] = reinterpret_cast<uint32_t(*)[4]>((unsigned char *)(this) + 976);
		for(i = 0; i < 4; ++i) {
			piVersusSurvivorCompletion[flipped][i] = g_totalResult / 4;
		}
		piVersusSurvivorCompletion[flipped][0] += g_totalResult % 4;
или же
		uint32_t *piVersusSurvivorCompletion = reinterpret_cast<uint32_t*>((unsigned char *)(this) + 976);
		for(i = 0; i < 4; ++i) {
			piVersusSurvivorCompletion[flipped*4 + i] = g_totalResult / 4;
		}
		piVersusSurvivorCompletion[flipped*4] += g_totalResult % 4;

@qark:
qark

Когда в прошлом году была квалификация чемпионата города по спортивному "свояку", "Сандерленд" играл с "Сити". И сыграл вничью. И в пакете был вопрос про "Сандерленд", который решил исход последнего боя (в мою пользу:)). Та ничья чуть не лишила "Сити" чемпионства. Сегодня "Сандерленд" играет с "МЮ". Не будем поддаваться суевериям.

@qark:
qark

Этим жанром, близким к Metalcore и Emocore, на Last.fm отмечают унылую отечественную эстраду.

@Spumer:
Spumer

Пишу с другом апдейтер. На моих плечах работа с файлами и списками файлов.
Тестирую на своем каталоге с исходниками, размер которого 936 МБ в котором 40 217 файлов и 4146 каталогов. Программа обходит рекурсивно указанную папку и создает md5-хэш для каждого файла и каталога на основе его содержимого. В качестве данных для хэширования каталога, я решил использовать хэши его файлов/каталогов.

Для указанного мной ранее каталога, это заняло 15 мин. и 44 сек.
Компилировал без оптимизации, разве что -s :3
Для того же самого каталога, но с -O3 и -Os, это заняло 15 мин. 39 сек.
И наконец, просто с -O2 это заняло 15 мин. 27 сек

Пока без поддержки многоядерности. Посему наблюдается лишь 50% заргузки на моем ноуте.
Потребление памяти не замерял, но точно менее 10МБ.
Достаточно неплохие результаты на мой взгляд, буду продолжать работать в этом направлении :)

@Spumer:
Spumer

Написал енкриптер паролей для обновленного steamcommunity:
https://github.com/spumer/Steam-Password-Encrypter

@khades:
khades

В итоге за сегодня я проехал примерно 21км. И я осознал что я зря не купил крылья на велик, разьезжая по растаявшему снего

@Spumer:
Spumer

Даже в такой маленькой программе выигрыш во времени составил 0.07 сек. при использовании указателей. Хотя тут активно используется STL, что не делает программу такой уж маленькой =\

#include <fstream>
#include <iostream>
#include <string>
#include <list>
#include <unistd.h> // for readlink()
#include <libgen.h> // for dirname()

#include <time.h>

using namespace std;

string* getline(istream& input);

int main(void){
	
	clock_t start = clock();


	char buf[96];
	if (readlink("/proc/self/exe", buf, 96) == -1) //http://www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html
		{ cout << "Error! Can't get path to myself!"; return 2; }
	sprintf(buf, "%s/%s", dirname(buf), "words.txt");

    ifstream ifs( buf );
	if( !ifs.is_open() ) { cout << "Words file not found!"; return 1; }

    list<string> words;
    //read file
	while ( ifs.peek() != EOF )
		words.push_back(*getline(ifs));
    ifs.close();
    //search patterns
    list<string>::iterator it;
	string *buffer;
	while( cin.peek() != EOF ){
		buffer = getline(cin);
        for(it = words.begin(); it != words.end(); ++it)
			if( buffer->find( *it ) != string::npos)
				goto next;
        cout << *buffer << endl;
		next: ;
    }
	
	printf("Время работы = %Lf\n", (long double) (clock() - start) / CLOCKS_PER_SEC);
	
    return 0;
}
string* getline(istream& input){
	static string str;
	register char c;
	str.clear();
	while ( (c = input.get()) != '\n' )
		str += c;
	return &str;
}

@juniorroy:
juniorroy

Без Стива Джобса (24 февраля 1955 — 5 октября 2011 года) мы не имели бы:
— iProducts
— дорогие ноутбуки

Без Деннис Ритчи (9 сентября 1941 — 12 октября 2011 года) мы не имели бы:
— Windows
— Unix (а следовательно Linux, FreeBSD, AIX, Solaris, MacOS X)
— Язык программирования С (а следовательно C++, Java, C# и т.п.)
— программы
— прогресс в компьютерах
— обычный печатный текст на компьютерах — мы всё читали бы в бинарной форме

Они умерли в одном и том же году и в одном и том же месяце, но кажется, лишь немногие заметили смерть Денниса Ритчи по сравнению со смертью Стива Джобса. (с) vinxru.livejournal.com

PS. Добавлю еще свои 5 копеек — John McCarthy(September 4, 1927 – October 24, 2011) — Nuff Said

@DJm00n:
DJm00n

img.chan4chan.com
Без Стива Джобса (24 февраля 1955 — 5 октября 2011 года) мы не имели бы:
— iProducts
— дорогие ноутбуки

Без Деннис Ритчи (9 сентября 1941 — 12 октября 2011 года) мы не имели бы:
— Windows
— Unix (а там и Linux)
— С
— и много что еще

Они умерли в одном и том же году и в одном и том же месяце, но кажется, лишь немногие заметили смерть Денниса Ритчи по сравнению со смертью Стива Джобса.

@Spumer:
Spumer

Бинарный поиск:
#include <stdio.h>
#define N 10

int main(){
    int ie, n, start, end, mid;
    int a[N] = {1, 2, 3, 4, 5, 7, 12, 24, 67, 136};

    n = 0;
    ie = start = 0;
    end = N - 1;

    do{
        if( a[ie] != n ){
            ie = mid = start + (end - start) / 2;
            if( n > a[mid] )
                ie = start = mid + 1;
            else end = mid;
        }
        else {
            printf("I find (%d) for (%d), correct?", a[ie], n);
            return 0;
        }
    }while(start != end);

    printf("Sorry. I can't find this item for you!");

    return 1;
}

@Spumer:
Spumer

На информатике получили в распоряжение цикл, который выводил латинский алфавит прописными буквами. К этому циклу мы получили задание на дом: усложнить цикл таким образом, чтобы выводилось по 10 символов в строку.
Для начала я родил небольшую проверку на деление по модулю на 10, ну а потом мы со знакомым стали извращаться и в итоге получилось вот это:
for(char c = 'A'; c <= 'Z'; printf( (c % 10) — 5 ? "%c" : "%c\n", c++ ));

@sai:
sai

Кто может помочь с сишечкой? Всё сегфолтится и ломается Т_Т ideone.com

@borman:
borman

Когда я вижу подобные статьи: labs.qt.nokia.com мне хочется бегать по кругу, кричать "ААА этот мир ебанулся!" и биться головой о стену. У меня такое впечатление, что C/C++ вменяемы, пока компилятор реализует только разумное подмножество стандарта, а не весь стандарт досконально.

@lovesan:
lovesan

давно не брал я в руки сей

@DEg:
DEg

С нетерпением жду сегодняшней ночи, ведь должны показать самою ожидаемую мной анимешку весеннего сезона: "С". Готовьте торрент-качалки )

@Spumer:
Spumer

Сидел и думал как разделить строку host:port, на host и порт. Внезапно родилось это:
typedef struct{
char* host;
char* port;
} ADDRESS;

ADDRESS* get_addr(char *address){
ADDRESS ptr = (ADDRESS)malloc(sizeof(ADDRESS));
ptr->host = address;
while(*(address-1) != ':')
ptr->port = ++address;
*(address — 1) = '\0';
return ptr;
}

@kamelusz:
kamelusz

из comp.lang.c

So, I'd prefer:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{ int ok = 1; int i = 0;

for( int looping = 1; looping; looping = ++i < 10 && ok )
ok = ok && 2 +( i < 9 )== printf( i < 9 ? "%2d " : "%2d", i );

if( 1 != printf( "\n" ))ok = 0;

return ok ? EXIT_SUCCESS : EXIT_FAILURE; }

This terminated the loop on error, but tries to terminate the line anyways.

@kamelusz:
kamelusz

И всё было бы хорошо с указателями, если бы их не было такое множество.
Это я к тому, что не разберусь с typedef void *PVOID;

@borman:
borman

Итак, вот хронология моего секса с багом:
1. Я увидел, что прога падает.
2. В поисках ошибки нашел пару кривых memset'ов и исправил.
3. Много раз пересобирал, запускал, ловил падение.
4. Задолбался, решил сохранить работу и пойти отдохнуть.
5. Закоммитил изменения из текущего подпроекта в текущую ветку devel (баг был в старом коде, который с новым напрямую не связан).
6. Перешел в ветку master.
7. Проверил master — работает.
8. Закоммитил исправление memset'ов.
9. Вернулся в devel, стянул этот коммит из master.
10. Проверил — работает.

Остался один вопрос: что это, блять, было?!

@borman:
borman

Жуйк, такие дела. Есть вариадическая функция, которая передает свой va_list на пару уровней вниз, где он скармливается в vsnprintf. На i686 системе все работает, на x86_64 падает с сегфолтом. Код: codepad.org (путь до падения помечен воскл. знаками). Анализ кордампа в gdb: codepad.org Что делать, жуйк?

@borman:
borman

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

@Spumer:
Spumer

Слегка оптимизировал запрос для бд, к сожалению пришлось добавлять memmove для удаления запятой в конце, тк sprintf с ипользованием \b в начале строки не работал как мне надо.
[code]http://paste.org.ru/?9hx2he[/code]

@Spumer:
Spumer

В качестве аргумента принимает путь до файла. Первым делом он ищет в нем id тех, кого надо исключить из запроса. Дописывает реквест и выполняет. После того как бд вернет то что нам надо, спокойно пишем в нужное место файла эти данные.
Код: paste.org.ru
Файл который пытал: paste.org.ru

@Spumer:
Spumer

я совсем забыл выложить то что у меня получилось и работает. ед. что недопилено — вшитые в код id тех, кого надо исключить из обработки. вот собственно сабж:
#include <windows.h>
#include <stdio.h>
#include <mysql.h>
#define HOST "127.0.0.1"
#define USER "root"
#define PASS ""
#define BDNAME "hlstats"
#define PORT 3306

MYSQL mysql;
MYSQL_RES *res;
MYSQL_ROW row;
FILE *file;

long offsetof(unsigned char , FILE );

void die(void){
printf("%sn", mysql_error(&mysql));
exit(0);
}

void main(int argc, char *argv[]){
unsigned int i = 0;
const char *query = "SELECT hlstats_Players.playerId, uniqueId, lastName FROM hlstats_Players, hlstats_PlayerUniqueIds WHERE hlstats_Players.game = 'l4d2' AND hlstats_PlayerUniqueIds.game = 'l4d2' AND hideranking =0 AND hlstats_Players.playerId = hlstats_PlayerUniqueIds.playerId ORDER BY skill DESC LIMIT 0 , 40";

mysql_init(&mysql);
mysql_real_connect(&mysql, HOST, USER, PASS, BDNAME, PORT, NULL, 0);
mysql_select_db(&mysql, BDNAME);
mysql_query(&mysql, query);
res = mysql_store_result(&mysql);

file = fopen(argv[1], "r+");
fseek(file, offsetof("begin", file), SEEK_SET);
int id;
while((row = mysql_fetch_row(res))) {
id = atoi(row[0]);
if(id == 1 || id == 76 || id == 14877) continue;
fprintf(file, "STEAM_0:%s\t0@TOP\t//%s\n", row[1], row[2]);
}
fclose(file);
mysql_free_result(res);
mysql_close(&mysql);
}

long offsetof(unsigned char *string, FILE *input){
register int c;
char *pString = string;
while((c = fgetc(input)) != EOF){
if(c == *pString)
pString++;
else pString = string;
if(*pString == '\0')
return ftell(input);
}
return 0L;
}