Форум программистов, компьютерный форум, киберфорум
C++: Сети
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.82/11: Рейтинг темы: голосов - 11, средняя оценка - 4.82
9 / 6 / 3
Регистрация: 01.07.2018
Сообщений: 49

fstream некорректно работает в потоке созданном CreateThread

02.02.2020, 22:08. Показов 2208. Ответов 10

Студворк — интернет-сервис помощи студентам
Здравствуйте! Я пишу программку, которая получает TCP пакеты (их содержимое) от сервера и сохраняет их в файл. Делается это в потоке. Поток создаю функцией CreateThread, с файлами работаю посредством fstream. В файл успешно записываются сообщения от сервера, но только в том случае, если сообщения примерно одинаковой длины! Если длина сообщений разнится, то в файле некоторые сообщения дублируются, какие-то сообщения теряются, вообщем плохо получается. Я не могу понять почему так! Я пробовал по разному, сначала думал, что fstream как-то плохо себя в потоке ведет, и переписал с системными функциями CreateFile и WriteFile, но результат тот же. Ещё я пробовал записывать сообщения не сразу, а складывать какое-то их кол-во в буфер, а потом записывать этот буфер, в котором содержится 10-20 сообщений, в файл, но не помогло.

Среда разработки Qt, но для работы с сетью и потоками я использую WinAPI функции, а Qt только для QUI.

Вот код функции потока
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
DWORD WINAPI threadProc(const LPVOID lpParam)
{
     // инициализация сокета, подключение к серверу, открытие файлов и другое
     int rlen = 1;
     while (rlen > 0) // пока функция recv получает какое-то кол-во байт
     {
 
         rlen = recv(sock, rbuf, MAX_RECV_BUF_SIZE, 0);
 
         // принимаем TCP пакеты, в каждом пакете может содержаться несколько сообщений разделенных '\n'
         // я достаю каждое сообщение из очередного принятого пакета и помещаю в вектор messages методом push_back
         // ...
         
         // вот собственно, код записи в файл
         for (std::vector<std::string>::iterator it = messages.begin(); it != messages.end(); ++it)
         {
              outfile << *it << "\n\n";
         }
 
          // ...
          // очистим вектор разобранных сообщений
          messages.clear();
      }
      
      // освобождение всех дескрипторов, закрытие файлов, сокетов и выход из потока
      return 0;
}
 
int main()
{
     // ...
     HANDLE hThread = CreateThread(NULL, 0, &threadProc, NULL, NULL, NULL);
     // ...
}
Повторюсь, что все работает хорошо, когда сервер посылает сообщения вида

message from server 1
message from server 2
message from server ...

но, если я пробую генерировать на сервере сообщения разной длины, то они записываются в файл некорректно, с коллизиями, путаются между собой

Если нужно, могу привести полный код функции threadProc
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
02.02.2020, 22:08
Ответы с готовыми решениями:

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

не работает fstream.h
Всем доброго дня !!! Решил поработать с файлами. В учебнике четко сказанно , что fstream.h не требует iostream.h В...

В VS2015 не работает fstream
Подключаю файл fstream, но при создании объекта fstream fin(&quot;in.bin&quot;, ios::binary); Компилятор выдает ошибку ...

10
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
03.02.2020, 06:26
покажите, как разбирается пакет.
и переменную rbuf и MAX_RECV_BUF_SIZE.
в общем, давайте полную функцию. быстрее будет.
1
9 / 6 / 3
Регистрация: 01.07.2018
Сообщений: 49
03.02.2020, 13:23  [ТС]
Спасибо за ответ!

Вот полный код поточной функции
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
DWORD WINAPI threadProc(const LPVOID lpParam)
{
    flagRunningTelThread = true; // отметим, что поток начал свою работу
 
    WSAData wsa;
    SOCKADDR_IN telServ;
 
    if (WSAStartup(0x0202, &wsa) != 0)
    {
        return 0;
    }
 
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock <= 0)
    {
        WSACleanup();
        return 0;
    }
 
    telServ.sin_family = AF_INET;
    telServ.sin_port = htons(Review::settings_file.inputPort); // порт сервера телеметрии
    telServ.sin_addr.s_addr = inet_addr(Review::settings_file.inputIP.c_str()); // IP адрес сервера
 
    // узнаем сегодняшнюю дату
    QDate curDate = QDate::currentDate();
    curDate.toString("yyyy-MM-dd");
    std::string strCurDate(curDate.toString("yyyy-MM-dd").toLocal8Bit().constData());
    std::string curDateFileName = "\\data\\" + strCurDate + ".txt";
    std::string strPrivDate = strCurDate;
 
    // узнаем текущую директорию приложения
    std::string currentDir = getCurrentDir();
 
    if(fileExists(currentDir + curDateFileName)) // если файл с сегодняшней датой уже существует
    {
        outfile.open(currentDir + curDateFileName, std::ios_base::app); // откроем его для записи в конец
    }
    else
    {
        // иначе, создадим новый файл с сегодняшней датой и откроем его для записи
        outfile.open(currentDir + curDateFileName, std::ios_base::out);
    }
 
    if(!outfile.is_open()) // произошла ошибка
        return 0;
 
    const int MAX_RECV_BUF_SIZE = 1024;
 
    if (::connect(sock, (SOCKADDR *)&telServ, sizeof(telServ)) != SOCKET_ERROR)
    {
        int rlen = 1; // кол-во принятых байт функцией recv
        char rbuf[MAX_RECV_BUF_SIZE]; // принимаем данные порциями по 1024 байт
        int messageCount = 1; // счетчик полученных сообщений
 
        std::string sep = "\n";
        std::size_t sep_size = sep.size();
        std::string temp;
        std::string privMessagePart; // предыдущая часть сообщения
 
        while (rlen > 0) // пока функция recv получает какое-то кол-во байт
        {
            // проверим, не пора ли нам завершить свою работу
            if(flagRunningTelThread == false)
            {
                break;
            }
 
            rlen = recv(sock, rbuf, MAX_RECV_BUF_SIZE, 0);
            // получена новая порция данных
 
            // узнаем дату получения
            QDate curDate = QDate::currentDate();
            std::string strCurDate(curDate.toString("yyyy-MM-dd").toLocal8Bit().constData());
 
            // узнаем время получения с точностью до миллисекунд
            QTime curTime = QTime::currentTime();
            std::string strCurTime(curTime.toString("hh:mm:ss.zzz").toLocal8Bit().constData());
 
            if(strCurDate != strPrivDate) // если уже начался новый день
            {
                // закроем старый файл
                outfile.close();
 
                // создадим и откроем новый файл с датой нового (только что наступившего дня)
                std::string curDateFileName = "\\data\\" + strCurDate + ".txt";
                outfile.open(currentDir + curDateFileName, std::ios_base::app); // открываем для записи в конец файла
                if(!outfile.is_open())
                    return 0;
 
                // запомним текущий день
                strPrivDate = strCurDate;
 
                // теперь будем записывать в новый файл
            }
 
            // теперь необходимо извлечь из этих данных сообщение
 
            // преобразуем принятые данные в std::string, чтобы с ними было удобней работать
            std::string recvBuff(rbuf);
 
            std::string borderMessage; // полное сообщение, которое было разделено по двум пакетам данных
 
            std::vector<std::string> messages; // все полученные сообщения в очередном пакете данных
 
            int q = 0;
 
            // вот код разделения сообщений
            while (1)
            {
                temp = recvBuff.substr(0, recvBuff.find(sep));
 
                if (q == 0)
                {
                    borderMessage = privMessagePart + temp;
                    messages.push_back(borderMessage);
                }
                else
                {
                    if (temp.size() == recvBuff.size())
                    {
                        privMessagePart = temp;
                        break; // переходим к следующему пакету данных
                    }
                    else
                    {
                        messages.push_back(temp);
                    }
                }
 
                if (temp.size() != recvBuff.size())
                {
                    recvBuff = recvBuff.substr(temp.size() + sep_size);
                }
                ++q;
            }
 
            for (std::vector<std::string>::iterator it = messages.begin(); it != messages.end(); ++it)
            {
                // сформируем строку json сообщения
                std::string jsonStr = std::to_string(Telemetry::threadsCount) + ": {\"id\":" + std::to_string(messageCount) +
                ",\"timestamp\":\"" + strCurDate + " " + strCurTime +
                "\",\"message\":\"" + *it + "\"}";
 
                ++messageCount;
 
                outfile << jsonStr << "\n\n";
            }
            
            // перешлём полученные сообщения всем WebSocket клиентам
            server_instance.sendToAllClients(jsonStr); // эта функция по факту не выполняется, потому что я тестировал программку без подключеных клиентов
 
            // очистим вектор разобранных сообщений
            messages.clear();
           
 
        } // end while (rlen > 0)
    }
 
    closesocket(sock);
    WSACleanup();
    if(outfile.is_open()) // если файл все еще открыт
        outfile.close(); // закроем его
    return 0;
}
Код подключения к серверу с последующим принятием сообщений это строки 49-157

Вообще, все работает, если не разделять сообщения, а просто записывать содержимое принятого буфера в файл

Если сообщения такого вида
1-й пакет [message 1\n]
2-й пакет [message 2\n]

Я просто пишу
C++
1
2
3
4
5
rlen = recv(sock, rbuf, MAX_RECV_BUF_SIZE, 0);
std::string recvBuff(rbuf);
// сформировал json строку
// jsonStr = ... + recvBuff ...
outfile << jsonStr << "\n\n";
то все нормально

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

1-й пакет [message 1\nmessa]
2-й пакет [ge 2\nmessage 3\n]
3-й пакет [some data from ser]
4-й пакет [ver\nlolollolloollol\n]

И я вроде как успешно разделяю их, но вот в файл сообщения записываются плохо

Под пакетом я подразумеваю не сам TCP пакет, а тот кусочек данных, который приняла функция recv

Или может быть я вообще не рационально это делаю и есть более хороший способ

Добавлено через 24 минуты
Сам смысл этой этой части программы в том, что я постоянно принимаю TCP пакеты от сервера, достаю из этих пакетов сообщения, которые разделяются символом '\n' и сохраняю их в файл с временной меткой полученного сообщения. Файл имеет имя сегодняшней даты, например "03 02 20.txt", если программа обнаруживает, то начался новый день, то она создаст новый файл "04 02 20.txt" и начнет записывать сообщения в него.

Ещё отмечу что размер буфера, который отправляет сервер мне не известен, поэтому я и провожу такую проверку с разделением сообщений

Вот так я создаю поток, попробовал назначить ему высокий приоритет, но не помогло
C++
1
2
3
4
5
6
hTelemetryThread = CreateThread(NULL, 0, &threadProc, NULL, THREAD_PRIORITY_NORMAL, NULL);
SetThreadPriority(hTelemetryThread, THREAD_PRIORITY_HIGHEST);
if(hTelemetryThread == NULL)
{
    // обработка ошибки
}
Я просто сильно удивлен! Как я уже писал, тот код функции threadProc работает, если сообщения одинаковый по длине, ну +- 1 байт, но если сообщения имеют разную длину, то запись в файл работает плохо

Перед записью я пробовал использовать flush(), но проблема не решилась
0
фрилансер
 Аватар для Алексей1153
6466 / 5683 / 1131
Регистрация: 11.10.2019
Сообщений: 15,128
03.02.2020, 13:32
peoplep8, как отлаживать подобное:

1) все операции вывода в файл заменить на вызов функции, в которой:
- выводимая строка выводится в лог (если символы в строке непечатные, то лучше выводить классический в hex виде)
- производится запись в файл

2) в этот же лог можно выводить и любые другие события потока, чтобы проследить их порядок

3) запускаешь, смотришь лог. Уже на этом этапе можно много удивительного обнаружить
1
9 / 6 / 3
Регистрация: 01.07.2018
Сообщений: 49
03.02.2020, 14:03  [ТС]
Спасибо, я бы так и делал, но когда я собираю проект как Debug (чтобы иметь возможность выводить лог в консоль Qt) и запускаю программу прямо из Qt, то она не имеет прав ни на что, я не знаю почему так, ведь я работаю в Qt от имени администратора! Она даже файлы не создает! Кроме того, в режиме отладки программа собирается минут 5) Может попробую как-нибудь консоль создать, хотя у меня QUI приложение. А пока для отладки использовал QMessageBox и просто выводил ошибки в файл

Добавлено через 17 минут
Вот еще кусочек кода, как сервер отправляет сообщения
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
char allSymbols[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
 
// ...
 
for (int i = 0; i < 1000; ++i)
{
     int delayTime = getRandomNumber(250, 1000); // интервал задержки (мсек) между отправками сообщений
     int messLength = getRandomNumber(25, 50); // длина очередного сообщения
     int messLength = 25; // если длина сообщения константа, то все нормально работает, сообщения записываются правильно
 
     // генерируем сообщение
     // если использовать такой способ генерации, то плохо записывается в файл
     int buffInd = 0;
     for (; buffInd < messLength; ++buffInd)
         sendBuff[buffInd] = allSymbols[getRandomNumber(0, strlen(allSymbols)-1)];
     sendBuff[buffInd] = '\n';
     sendBuff[buffInd + 1] = '\0';
 
     // если вместо генерации случайных сообщений, отправлять такую строку, то все нормально работает
     //wsprintfA(sendBuff, "%s %d\n", "message from server", i+1);
 
     // отправляем клиенту
     if ((slen = send(connSock, sendBuff, strlen(sendBuff), 0)) != strlen(sendBuff))
     {
         nstd::cout << "send() returned " << slen << ", but we need to send " << strlen(sendBuff) << " bytes! something went wrong" << std::endl;
         break;
     }
     Sleep(delayTime);
}
0
фрилансер
 Аватар для Алексей1153
6466 / 5683 / 1131
Регистрация: 11.10.2019
Сообщений: 15,128
03.02.2020, 14:10
peoplep8, какую IDE используешь? Надо разобраться, с такой ерундой невозможно разрабатывать и отлаживать. Может, переустановка поможет

Добавлено через 2 минуты
Цитата Сообщение от peoplep8 Посмотреть сообщение
Кроме того, в режиме отладки программа собирается минут 5)
нужно ещё уточнить, что промежуточные релизные и дебажные файлы падают в разные папки, а то будет каждый раз перекомпиляция при переключении режима

также настрой количество потоков для работы компилятора, поставь значение, равное количеству ядер минус 1 . В студии и в QtCreator это настраивается
1
9 / 6 / 3
Регистрация: 01.07.2018
Сообщений: 49
03.02.2020, 14:15  [ТС]
Я использую QtCreator 5.13.2 в Windows, собирает все это дело MinGW x64. Вообще, я новичек в Qt и использую его только для QUI. Попробую переустановить, но с моими ресурсами это займет достаточно времени)

Добавлено через 1 минуту
Хорошо, попробую
0
фрилансер
 Аватар для Алексей1153
6466 / 5683 / 1131
Регистрация: 11.10.2019
Сообщений: 15,128
03.02.2020, 14:17
peoplep8, какая IDE используется?

QtCreator?

Добавлено через 1 минуту
peoplep8, пристегни проект в ZIP . Только папки release и debug удали, а также экзешники

Добавлено через 34 секунды
сам не обещаю поразбираться, если только время будет. Но, может, кто более свободный поможет
1
9 / 6 / 3
Регистрация: 01.07.2018
Сообщений: 49
03.02.2020, 16:27  [ТС]
Да, спасибо, сегодня вечером прикреплю архив
0
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
03.02.2020, 17:27
Лучший ответ Сообщение было отмечено peoplep8 как решение

Решение

Цитата Сообщение от peoplep8 Посмотреть сообщение
rlen = recv(sock, rbuf, MAX_RECV_BUF_SIZE, 0);
попробуйте перед этой строкой вставить ::memset(rbuf, 0, sizeof(rbuf));.
1
9 / 6 / 3
Регистрация: 01.07.2018
Сообщений: 49
03.02.2020, 17:43  [ТС]
Ураааа!!!!! Все работает! Я забыл про то что буфер нужно обнулять XD Спасибо! А я еще думаю, ну что может быть там не так... Мой мозг просто забыл про обнуление :/
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
03.02.2020, 17:43
Помогаю со студенческими работами здесь

работает fstream в VC++2010
скажите пожалуйста библиотечный файл &lt;fstream.h&gt; поддерживает VC++2010? а то почему то даже простой код не работает ! или я может что то...

Как работает write в fstream?
есть файл с текстом: fstream f(&quot;f.txt&quot;,ios::in); надо его разделить его на части по сколько-то байт (переменная piece) создаю...

fstream не работает и чтение и запись
Добрый день! Не пойму, почему введение функции getline становится причиной того, что запись в файл не идет. Если getline убрать, то все...

fstream::tellp() не работает как надо.
#include &lt;iostream&gt; #include &lt;stdlib.h&gt; #include &lt;fstream&gt; using namespace std; int main(int argc, char** argv) { ...

Почему не работает код при переходе на fstream?
Доброго времени суток! Ломаю, по незнанию, голову над следующим вопросом. Ниже такой код (из книги Лафоре ): объект записывает/читает...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Новые блоги и статьи
Вывод данных через динамический список в справочнике
Maks 01.04.2026
Реализация из решения ниже выполнена на примере нетипового справочника "Спецтехника" разработанного в конфигурации КА2. Задача: вывести данные из ТЧ нетипового документа. . .
Функция заполнения текстового поля в реквизите формы документа
Maks 01.04.2026
Алгоритм из решения ниже реализован на нетиповом документе "ВыдачаОборудованияНаСпецтехнику" разработанного в конфигурации КА2, в дополнении к предыдущему решению. На форме документа создается. . .
К слову об оптимизации
kumehtar 01.04.2026
Вспоминаю начало 2000-х, университет, когда я писал на Delphi. Тогда среди программистов на форумах активно обсуждали аккуратную работу с памятью: нужно было следить за переменными, вовремя. . .
Идея фильтра интернета (сервер = слой+фильтр).
Hrethgir 31.03.2026
Суть идеи заключается в том, чтобы запустить свой сервер, о чём я если честно мечтал давно и давно приобрёл книгу как это сделать. Но не было причин его запускать. Очумелые учёные напечатали на. . .
Модель здравосоХранения 6. ESG-повестка и устойчивое развитие; углублённый анализ кадрового бренда
anaschu 31.03.2026
В прикрепленном документе раздумья о том, как можно поменять модель в будущем
10 пpимет, которые всегда сбываются
Maks 31.03.2026
1. Чтобы, наконец, пришла маршрутка, надо закурить. Если сигарета последняя, маршрутка придет еще до второй затяжки даже вопреки расписанию. 2. Нaдоели зима и снег? Не надо переезжать. Достаточно. . .
Перемещение выделенных строк ТЧ из одного документа в другой
Maks 31.03.2026
Реализация из решения ниже выполнена на примере нетипового документа "ВыдачаОборудованияНаСпецтехнику" с единственной табличной частью "ОборудованиеИКомплектующие" разработанного в конфигурации КА2. . . .
Functional First Web Framework Suave
DevAlt 30.03.2026
Sauve. IO Апнулись до NET10. Из зависимостей один пакет, работает одинаково хорошо как в режиме проекта так и в интерактивном режиме. из сложностей - чисто функциональный подход. Решил. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru