LINUX.ORG.RU

Сообщения stD

 

Размер выделенного массива (СИ)

Форум — Development

Здравствуйте.

Выделяю память под массив:

char *tochdost = calloc(64+64+64+64, sizeof(char));

if(tochdost != NULL)  
 {
   char *encrypt = tochdost + 64;
   char *res_encrypt = encrypt + 64;
   char *pasword = res_encrypt + 64;
   ...

Смотрю размер выделенной области вот так:

printf("SIZE %zu\n", malloc_usable_size(tochdost));

В ответ получаю - SIZE 264.

Скажите, откуда берутся лишние 8 байт?

Язык СИ, Линукс.

 

stD
()

Как правильно закрывать popen?

Форум — Development

Здравствуйте.

Помогите разобраться с закрытием потока, открытого popen: Язык СИ, линукс.

char curl_buff[64] = {0,};

FILE *curl_prog = popen("curl http://192.168.5.189 2> /dev/null", "r");

if(curl_prog != NULL)
 {
   fgets(curl_buff, 63, curl_prog);
 }

if(pclose(curl_prog) == -1) error_log("close curl_prog.");

Закрытие должно быть после фигурных скобок или внутри...?

char curl_buff[64] = {0,};

FILE *curl_prog = popen("curl http://192.168.5.189 2> /dev/null", "r");

if(curl_prog != NULL)
 {
   fgets(curl_buff, 63, curl_prog);
   if(pclose(curl_prog) == -1) error_log("close curl_prog.");
 }

То есть, мне не понятно, если curl_prog вернул NULL, то это значит что?

Поток открылся после этой строчки:

FILE *curl_prog = popen("curl http://192.168.5.189 2> /dev/null", "r");

Или ещё не ясно, открылся он или нет и это проверяется здесь:

if(curl_prog != NULL)
 {
   ...

 

stD
()

Проверка выделения памяти (си)

Форум — General

Здравствуйте.

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

char *str = (char*)malloc(1 * sizeof(char));

Какие могут быть причины НЕ выделения?

Спасибо.

 

stD
()

Размер выделяемой памяти

Форум — General

Здравствуйте.

Скажите пожалуйста, как будет правильней выделять количество памяти. Кратно (8, 32, 64) или столько сколько нужно?

То есть, допустим мне нужно 10 байт, как будет лучше/правильней, выделить 11 байт или 16?

Извиняюсь за корявую постановку вопроса.))) Спасибо.

 

stD
()

Telegrambot на СИ - SSL_read

Форум — General

Здравствуйте.

Пишу сервер для бота работающий через Webhook. Вот код (только main):

int main() 
{  
    ////////////////////////////////////    ssl    //////////////////////////////////////////
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    SSL_library_init();
    SSL_CTX * sslctx = SSL_CTX_new(TLSv1_2_server_method());

    /////////////////////////////    read certificate    ////////////////////////////////////
    if(SSL_CTX_use_certificate_file(sslctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) error_log("use_certificate_file!");
    if(SSL_CTX_use_PrivateKey_file(sslctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) error_log("use_PrivateKey_file!");
    if(!SSL_CTX_check_private_key(sslctx)) error_log("check_private_key!");

    ///////////////////////////////////    server    ////////////////////////////////////////
    int sd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sd < 0) error_log("descriptor socket!");
    int one = 1;
    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
 
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_addr.s_addr = INADDR_ANY;
    s_addr.sin_port = htons(port);

    if(bind(sd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) error_log("binding!");

    if(listen(sd, 5) == -1) 
     {
       close(sd);
       error_log("listen!");
     }


    char read_buffer[BREADSIZE] = {0,};

    while(1) 
     {  
        printf("ROUTER WAIT CONNECTION.\n");
        memset(read_buffer, 0, BREADSIZE);
        int client = accept(sd, NULL, NULL); 

        ///////////////////////////// ssl socket //////////////////////////////
        SSL *ssl = SSL_new(sslctx);
        if(SSL_set_fd(ssl, client) == 0) error_log("SSL_set_fd!");

        int acc = SSL_accept(ssl); 
        if(acc <= 0)
         { 
            SSL_free(ssl);
            if(close(client) == -1) error_log("close client_2!");
            error_log("Not SSL_accept.");
            continue;
         }
         
        /////////////////////////////// fork ///////////////////////////////////
        pid_t fpid;  
        signal(SIGCHLD, child_kill);  
        fpid = fork();

        /////////////////////////// start child ////////////////////////////////
        if(fpid != 0) 
         { 
            SSL_free(ssl);
            if(close(client) == -1) error_log("close client_pid!");
            continue;
         }
         
        ///////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////// read header from telegram //////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////////
        int n = SSL_read(ssl, read_buffer, BREADSIZE - 2); // first SSL_read
        if(n <= 0)
         {
            SSL_free(ssl);
            if(close(client) == -1) error_log("close client_3!");
            printf("Disconnection:%d\n", n);
            exit(0);           
         } 

printf("READ_BUFER:%s_END\n\n", read_buffer);

        ////////////////////////////////////////////////////////////////////////
        /////////////////////////// read json data ///////////////////////////// 
        ////////////////////////////////////////////////////////////////////////
        memset(read_buffer, 0, BREADSIZE); 
        int m = SSL_read(ssl, read_buffer, BREADSIZE - 2);  // second SSL_read
        if(m <= 0)
         {
            SSL_free(ssl);
            if(close(client) == -1) error_log("close client_8!");
         }
         
printf("READ_JSON:%s_END\n\n", read_buffer);

        //////////////////////////// telegram reply  //////////////////////////////
        SSL_write(ssl, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 38);
        SSL_free(ssl);
        if(close(client) == -1) error_log("close client_6!");
        exit(0); 
  
    } // END while(1) 
 
}

Код работает, но вот в чём вопрос: приходится делать SSL_read два раза. То есть, при первом чтении (first SSL_read) считывается заголовок:

READ_BUFER:POST /55454634:AAEJ2Z930r3oyno6kTA97gWGj2osSUXI HTTP/1.1
Host: 6.129.231.13
Content-Type: application/json
Content-Length: 308
Connection: keep-alive
Accept-Encoding: gzip, deflate

А во-втором (second SSL_read) читаются json-данные:

READ_JSON:{"update_id":33,"message":{"message_id":209,"from": бла-бла"}}_END

Если же делать такой же запрос к серверу с помощью curl'a, то всё читается в первом SSL_read.

Почему так происходит, может кто-нибудь объяснить? Может это особенность работы телеграмма? Или я всё неправильно делаю?

Спасибо.

 ,

stD
()

Как поменять пароль на линукс-машине POST или GET запросом

Форум — Development

Предположим есть удалённая машина, на ней крутится самописный сервер (или какой-нибудь стандартный), как можно из браузера поменять пароль?

 ,

stD
()

HEX to char (в программе на СИ)

Форум — General

Здравствуйте.

Есть сервер на СИ, который должен принимать строку из браузера отправленную POST-запросом через Ajax. Строки (в браузере) вводятся через поля <input>. Так вот, некоторые символы приходят в виде HEX, например - dima:dimon приходит в таком виде - dima%3Adimon.

Как это переводить в читаемые символы?

 

stD
()

Помогите разобраться с malloc СИ

Форум — Development

Здравствуйте.

Есть функция в которой есть две переменные для массивов - buff и trans_rate:

void rate_coin(char *coin)
 {
    FILE *rat_coi;
    char *buff = (char*) malloc(128 * sizeof(char));
    snprintf(buff, (int)strlen(coin) + 59, "%s%s%s", "curl https://api.coinmarketcap.com/v1/ticker/", coin, "/ 2>/dev/null"); 
    printf("Coinmarketcap: %s\n", buff);
    rat_coi = popen(buff, "r"); 
    if(rat_coi == NULL) error_log("rat_coi!");

    char trans_rate[128] = {0,};
    //char *trans_rate = (char*) malloc(128 * sizeof(char));
    int count = 0;
    strcat(trans_rate, "RATE COIN\n");
    memset(buff, 0, 128);
    while(fgets(buff, 126, rat_coi) != NULL)
     {
       count++;
       if(count > 8) break;
       if(count == 4 || count == 7 || count == 8)
        {
          char *pch = strtok(buff, " ,\"");
          while(pch != NULL)
           {
             char *ptr = strchr(pch, ':');
             if(ptr!=NULL) *ptr = ' ';
             strcat(trans_rate, pch);
             pch = strtok(NULL, " ,\"");
           }
        }
     }

    trans_rate[strlen(trans_rate) - 1] = 0;
    SendMessage(glob_chat_id, trans_rate);
    free(buff);
    printf("%s\n\n", trans_rate);
    //free(trans_rate);
 }

Для переменной buff делаю malloc, использую её в функции snprintf(buff, ...). После этого очищаю buff с помощью memset(buff,...), использую её же в функции while(fgets(buff,...) и в конце освобождаю память - free(buff);.

Для переменной trans_rate выделяю нужное кол-во байт и использую её.

Всё работает хорошо.

Если же для переменной trans_rate тоже делать malloc (строчка закомментирована) вместо char trans_rate[128] = {0,};, то прога начинает вести себя непредсказуемо, как будто перекрываются массивы (в printf попадают непонятные символы):

h�"�WRATE COIN

Объясните пожалуйста, почему в данном случае для buff malloc работает, а для trans_rate нет?

 ,

stD
()

Работа с массивом на СИ

Форум — Development

Здравствуйте.

Суть вот в чём: мне нужно проверить наличие подстроки (она всегда в самом начале строки) в строке, и всё что идёт после этой подстроки записать в другой массив.

Допустим делаю так:

char *restr;
char str[32] = {0,};
char res_mas[16] = {0,};
...

if((restr = strstr(str, "xa-xa")) != NULL) 
 {
   strcpy(res_mas, restr + 5);
...

То есть я сдвигаю указатель на длину подстроки и оставшееся копирую в конечный массив.

Вопрос заключается в том, могу ли я сделать так?

char str[32] = {0,};
char res_mas[16] = {0,};
...

if(strstr(str, "xa-xa") != NULL) 
 {
   strcpy(res_mas, str + 5);
...

В обоих случаях всё работает, но правильно ли так делать?

(только не пишите пожалуйста, что лучше использовать strncpy или memcpy, речь не об этом, strcpy нарисована в качестве примера)

...

И ещё «подвопрос»: если я использую несколько конструкций «if()» подряд...

char *restr;
char str[32] = {0,};
char res_mas[16] = {0,};
char res_mas2[16] = {0,};
char res_mas3[16] = {0,};
...

if((restr = strstr(str, "xa-xa")) != NULL) 
 {
   strcpy(res_mas, restr + 5);
 }

if((restr = strstr(str, "xo-xo")) != NULL) 
 {
   strcpy(res_mas2, restr + 5);
 }

if((restr = strstr(str, "xy-xy")) != NULL) 
 {
   strcpy(res_mas3, restr + 5);
 }

...то нужно ли после каждого блока if() «обнулять» указатель *restr? И если да, то как это делать правильно?

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

 ,

stD
()

Приоритет операторов

Форум — Development

Здравствуйте.

Совершенно дурацкий вопрос:

if(count == 10 && (strstr(buff, "бла-бла") != NULL || strstr(buff, "блу-блу"))

Если count НЕ равен 10, то будет ли дальше проверяться условия?

Или надо так?

if((count == 10) && (strstr(buff, "бла-бла") != NULL || strstr(buff, "блу-блу"))

 

stD
()

Создать ресурс для облачного майнинга

Форум — Science & Engineering

Здравствуйте.

Собственно вопрос вот в чём: мой товарищ предложил создать сервис для облачного майнинга. Сам я майню на «любительском» уровне (пара видеокарт) и конечно же не обладаю надлежащими знаниями.

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

Поделитесь своими мыслями и/или опытом в этом деле? Я бы потом дал это почитать своему товарищу, чтоб он успокоился )))

 

stD
()

Как убрать из строки символы цвета этой строки?

Форум — Development

Здравствуйте.

Допустим имеется строка:

\x1b[31mЭта строка будет красного цвета\x1b[0m

Подскажите, есть ли в языки СИ какие-либо средства для удаления этих симолов (\x1b[31m, \x1b[0m)?

 

stD
()

Захват stdout'а какой-либо программы в своей программе (СИ)

Форум — Development

Здравствуйте.

Я уже задавал подобный вопрос, ответ на него вроде бы прост:

#include <stdio.h>

int main() 
{
	FILE *in;
	char buff[512];

	if(!(in = popen("df -h", "r")))
         {
		printf("xana");
	 }

	while(fgets(buff, sizeof(buff), in)!=NULL)
         {
		printf("RECIV:%s", buff);
	 }

	pclose(in);
    return 0;
}

Получаю всё как положено.

RECIV:Файл.система   Размер Использовано  Дост Использовано% Cмонтировано в
RECIV:udev             3,9G         4,0K  3,9G            1% /dev
RECIV:tmpfs            799M         1,4M  798M            1% /run
RECIV:/dev/sdb1        143G         105G   31G           78% /
RECIV:none             4,0K            0  4,0K            0% /sys/fs/cgroup
RECIV:none             5,0M            0  5,0M            0% /run/lock
RECIV:none             3,9G          28M  3,9G            1% /run/shm
RECIV:none             100M          52K  100M            1% /run/user

Программа которую я планирую запускать (это майнер EWBF) подключается к пулу (передавая ему пароль) и начинает майнить.

Дык вот в чём дело: если запустить майнер с НЕ правильным паролем, то он выдаёт сообщение об ошибке, а моя программа ловит его и завершает работу.

RECIV:+-------------------------------------------------+
RECIV:|         EWBF's Zcash CUDA miner. 0.3.4b         |
RECIV:+-------------------------------------------------+
RECIV:INFO: Current pool: zec.suprnova.cc:2142
RECIV:INFO: Selected pools: 1
RECIV:INFO: Solver: Auto.
RECIV:INFO: Devices: All.
RECIV:INFO: Temperature limit: 90
RECIV:INFO: Api: Disabled
RECIV:---------------------------------------------------
RECIV:INFO: Target: 003c3c3c3c3c3c3c...
RECIV:INFO: Detected new work: 4902
RECIV:ERROR: Stratum authorization error

Если же запустить с правильными данными, то моя программа ничего не ловит, как будто майнер ничего не выдаёт (а он выдаёт).

То есть (как я думаю) когда майнер запускается с правильными данными, то внутри что-то форкается и инфа выдаётся неизвестно куда. (другого объяснения я не занаю)

Такое может быть? Или почему вобще так происходит?

П.С. Извиняюсь за формулировку вопроса и заранее спасибо.

 

stD
()

Захват stdout'а какой-либо программы в своей программе (СИ)

Форум — Development

Здравствуйте.

Я хочу в своей программе запускать другую программу и ловить то, что она выдаёт в stdout.

Допустим делаю это так:

#include <stdio.h>

int main()
 {
   FILE * som_prog;
   char stroka[200];

   som_prog = popen("top", "r");

   while (!feof(som_prog))
    {
      fgets(stroka, 199, som_prog);
      printf("START: %s", stroka);
    }

   pclose(som_prog);
   printf("END\n");
   return 0;
 }

// gcc -Wall -Wextra as2.c -o as2

В данном случае запускаю «top» и получаю ожидаемое...

...
START:  9481 dima      20   0  617380  61688  49960 S   6,0  0,8  59:14.48 gnome-system-mo                                           
START:  2915 dima      20   0 1115668 196464  57604 S   2,3  2,4  73:16.01 plugin-containe                                           
START:  1417 root      20   0  358360 112796  88736 S   1,0  1,4  66:37.20 Xorg                                                      
START:  2501 dima      20   0 1514860 190560  71068 S   1,0  2,3  33:44.33 compiz                                                    
START:  5991 dima      20   0  656628  32728  23936 S   1,0  0,4   0:17.11 gnome-terminal 
...
...то есть всё хорошо.

Однако заковыка в другом. Мне нужно ловить данные от программы, которая сначала «плюёт» пару строк (и они успешно ловятся), а потом эта программа создаёт ещё один свой поток и «плюёт» строки уже из него. И вот эти строки уже не ловятся.

Что можно придумать?

 

stD
()

Как сделать так, чтоб самодельный сервер принимал только локальные соединения?

Форум — General

Здравствуйте.

Типа понадобился свой сервер, что-то вроде бекенда как в связке nginx + apache, где апач(мой сервер) принимает соединения только на - "... localhost:81>"

Я к тому, что, это как-то флагами какими-то устанавливается или нужно свою функцию писать?

Пишется на СИ.

Спасибо.

 

stD
()

Возьмите кто-нибудь меня на работу

Форум — Job

Возьмите кто-нибудь меня на работу, пожалуйста.

Живу в Питере, возраст 40 лет.

Linux, программирование СИ, микроконтроллеры, Android.

Пишите на почту: pccar@mail.ru

 

stD
()

Про HTTPS

Форум — Web-development

Здравствуйте.

Сегодня от гугла пришло письмо «счастья» с сообщением о понижении сайта ввиду отсутствия SSL-сертификата.

Естественно хочется бесплатно - кто-что посоветует и чем пользуется сам? Вроде как «Let's Encrypt» рекламируют.

И второй вопрос, который тяготит меня: если стоит связка nginx + apache, то настраивать нужно оба сервера или только nginx, который находится на «передовой»?

 

stD
()

Откуда в Search Console (google), в заблокированных ресурсах появился чужой сайт?

Форум — Web-development

Зашёл сегодня в Search Console, посмотреть как там дела, и в разделе «Заблокированные ресурсы» обнаружил чужой сайт (подчеркнул красным). Объясните пожалуйста, откуда он там взялся?

https://hsto.org/files/ffd/4b5/3d1/ffd4b53d1bb941119e2b8434a1d42863.png

 

stD
()

WebSocket-сервер

Форум — Development

Здравствуйте.

Пост пишется с целью послушать критику в адрес моего поделья, а так же в надежде на то, что это кому-то пригодится.

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

Сразу оговорюсь, сервер не умеет пересылать больше 125-ти байт, не умеет wss:// и не работает с фрагментированными фреймами.

В итоге вот что получилось:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h> 
#include <fcntl.h>    
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/sendfile.h>

#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

#if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
    |(rol(block->l[i],8)&0x00FF00FF))

#elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i]

#else
#error "Endianness not defined!"
#endif

#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
    ^block->l[(i+2)&15]^block->l[i&15],1))

#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);

// opcode - тип фрейма
#define WS_TEXT_FRAME 0x01
#define WS_PING_FRAME 0x09
#define WS_PONG_FRAME 0x0A
#define WS_CLOSING_FRAME 0x08

#define BUFSIZE 1024
#define FILESTR 32
#define ALLARRAY 64
#define SHA_DIGEST_LENGTH 20

char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n";

char response_404[] = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n";

char response_img[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: image/png; charset=UTF-8\r\n\r\n";  

char response_xicon[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: image/x-icon; charset=UTF-8\r\n\r\n";

char response_css[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/css; charset=UTF-8\r\n\r\n";

char response_js[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/js; charset=UTF-8\r\n\r\n";

char response_ttf[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: font/ttf ; charset=UTF-8\r\n\r\n";

char response_text[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/text; charset=UTF-8\r\n\r\n";

char response_403[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html><html><head><title>403</title>"
"<style>body { background-color: #312f2f }"
"h1 { font-size:4cm; text-align: center; color: #666;}</style></head>"
"<body><h1>403</h1></body></html>\r\n";

char GUIDKey[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; //36

char response_ws[] = "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: "; //97

unsigned char charset[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};

char patch_to_dir[ALLARRAY] = {0,};
char fpfile[ALLARRAY] = {0,};
char buffer[BUFSIZE] = {0,};
int client_fd, count_warning_log =0;
struct stat stat_buf;
sem_t sem;

typedef struct
 {
   uint32_t state[5];
   uint32_t count[2];
   unsigned char buffer[64];
 } SHA1_CTX;


/////////////////////////////////////////////// SHA1 /////////////////////////////////////////////////////////////
void SHA1Transform( uint32_t state[5], const unsigned char buffer[64])
 {
    uint32_t a, b, c, d, e;
    typedef union
     {
       unsigned char c[64];
       uint32_t l[16];
     } CHAR64LONG16;

    CHAR64LONG16 block[1];    
    memcpy(block, buffer, 64);

    a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4];

    R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3);
    R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7);
    R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11);
    R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15);
    R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);
    R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);
    R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);
    R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);
    R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);
    R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);
    R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);
    R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);
    R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);
    R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);
    R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);
    R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);
    R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);
    R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);
    R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);
    R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);

    state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e;
    a = b = c = d = e = 0;
    memset(block, 0, sizeof(block));
 }


void SHA1Init( SHA1_CTX * context)
 {
    context->state[0] = 0x67452301;
    context->state[1] = 0xEFCDAB89;
    context->state[2] = 0x98BADCFE;
    context->state[3] = 0x10325476;
    context->state[4] = 0xC3D2E1F0;
    context->count[0] = context->count[1] = 0;
 }


void SHA1Update( SHA1_CTX * context, const unsigned char *data, uint32_t len)
 {
    uint32_t i;
    uint32_t j;

    j = context->count[0];
    if ((context->count[0] += len << 3) < j) context->count[1]++;

    context->count[1] += (len >> 29);
    j = (j >> 3) & 63;
    if((j + len) > 63)
     {
       memcpy(&context->buffer[j], data, (i = 64 - j));

       SHA1Transform(context->state, context->buffer);
       for(; i + 63 < len; i += 64)
        {
          SHA1Transform(context->state, &data[i]);
        }

       j = 0;
     }

    else i = 0;

    memcpy(&context->buffer[j], &data[i], len - i);
 }


void SHA1Final( unsigned char digest[20], SHA1_CTX * context)
 {
    unsigned i;
    unsigned char c, finalcount[8];

    for(i = 0; i < 8; i++)
     {
       finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);     
     }

    c = 0200;
    SHA1Update(context, &c, 1);

    while((context->count[0] & 504) != 448)
     {
       c = 0000;
       SHA1Update(context, &c, 1);
     }

    SHA1Update(context, finalcount, 8); 

    for(i = 0; i < 20; i++)
     {
       digest[i] = (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
     }

    memset(context, 0, sizeof(*context));
    memset(&finalcount, 0, sizeof(finalcount));
 }


void SHA1(unsigned char *hash_out, const char *str, unsigned int len)
 {
    SHA1_CTX ctx;
    unsigned int ii;
    SHA1Init(&ctx);
    for (ii=0; ii<len; ii+=1) SHA1Update(&ctx, (const unsigned char*)str + ii, 1);
    SHA1Final((unsigned char *)hash_out, &ctx);
    hash_out[20] = 0;
 }


////////////////////////////////////// base64_encode ////////////////////////////////////////////
int base64_encode(unsigned char sha_key_in[], unsigned char base64_key_out[], int len)
 {
   int idx, idx2, blks, left_over;

   blks = (len / 3) * 3;
   for(idx=0, idx2=0; idx < blks; idx += 3, idx2 += 4) 
    {
      base64_key_out[idx2] = charset[sha_key_in[idx] >> 2];
      base64_key_out[idx2+1] = charset[((sha_key_in[idx] & 0x03) << 4) + (sha_key_in[idx+1] >> 4)];
      base64_key_out[idx2+2] = charset[((sha_key_in[idx+1] & 0x0f) << 2) + (sha_key_in[idx+2] >> 6)];
      base64_key_out[idx2+3] = charset[sha_key_in[idx+2] & 0x3F];
    }

   left_over = len % 3;

   if(left_over == 1) 
    {
      base64_key_out[idx2] = charset[sha_key_in[idx] >> 2];
      base64_key_out[idx2+1] = charset[(sha_key_in[idx] & 0x03) << 4];
      base64_key_out[idx2+2] = '=';
      base64_key_out[idx2+3] = '=';
      idx2 += 4;
    }

   else if(left_over == 2) 
    {
      base64_key_out[idx2] = charset[sha_key_in[idx] >> 2];
      base64_key_out[idx2+1] = charset[((sha_key_in[idx] & 0x03) << 4) + (sha_key_in[idx+1] >> 4)];
      base64_key_out[idx2+2] = charset[(sha_key_in[idx+1] & 0x0F) << 2];
      base64_key_out[idx2+3] = '=';
      idx2 += 4;
    }

   base64_key_out[idx2] = 0;
   return(idx2);
 }


////////////////////////////////////// error_log ////////////////////////////////////////////
void error_log(char *my_error) 
 { 
   time_t t;
   time(&t);
   FILE *f;
   f = fopen("/var/log/ErrorWsstd.log", "a"); 
   if(f == NULL) printf("Error open /var/log/ErrorWsstd.log.\n");
   fprintf(f, "%s", ctime( &t));
   fprintf(f, "Error %s\n\n", my_error);
   printf("Error %s Write to /var/log/ErrorWsstd.log.\n", my_error);
   fclose(f);
   exit(0);
 }


//////////////////////////////// warning_access_log ////////////////////////////////////////
void warning_access_log(char *war_ac) 
 {  
   count_warning_log++;
   if(count_warning_log > 100)
     {
       system("gzip -f /var/log/Access_warning.log");
       count_warning_log = 0;
       time_t t;
       time(&t);
       FILE *f;
       f = fopen("/var/log/Access_warning.log", "w"); 
       fprintf(f, "%s", ctime( &t));
       fprintf(f, "%s\n\n", war_ac);
       printf("_______________________________________\nWrite to /var/log/Access_warning.log...\n%s\n", war_ac);
       fclose(f);
     }

    else
     {
       time_t t;
       time(&t);
       FILE *f;
       f = fopen("/var/log/Access_warning.log", "a"); 
       fprintf(f, "%s", ctime( &t));
       fprintf(f, "%s\n\n", war_ac);
       printf("_______________________________________\nWrite to /var/log/Access_warning.log...\n%s\n", war_ac);
       fclose(f);
     }
 }


//////////////////////////////// read_in_file ////////////////////////////////////////
void read_in_file(char *name_file) 
 { 
   off_t offset = 0;
   memset(&stat_buf, 0, sizeof(stat_buf));    
   memset(fpfile, 0, ALLARRAY);
   snprintf(fpfile, (int)strlen(patch_to_dir) + (int)strlen(name_file) + 1, "%s%s", patch_to_dir, name_file);
   int file = open(fpfile, O_RDONLY);

   if(file < 0) 
    {
      if(close(client_fd) == -1) warning_access_log("open file close client_fd.");
      warning_access_log("Not File."); 
    }
 
   else
    {
      if(fstat(file, &stat_buf) != 0) error_log("fstat.");
      if(sendfile(client_fd, file, &offset, stat_buf.st_size) == -1) warning_access_log("sendfile."); 
      if(close(file) == -1) error_log("close file.");
      if(close(client_fd) == -1) warning_access_log("in function read_in_file() - close client_fd.");
      warning_access_log(buffer);
      printf("Trans %s\n\n", name_file);
    }
 }


///////////////////////////////////////// ws_func ///////////////////////////////////////////////////
void * ws_func(void *client_arg) 
 { 
   int client_fd = * (int *) client_arg;
   sem_post(&sem);
   warning_access_log("START_WS");
   printf("\nClient ID - %d\n", client_fd);
   char inbuf[132] = {0,};
   char reciv_r[48] = {0,};

   while(1)
    {
      memset(inbuf, 0, 132); 
      int rec_b = read(client_fd, inbuf, 131); // ожидаем данные от клиента и читаем их по приходу
      memset(reciv_r, 0, 48);
      snprintf(reciv_r, 39, "%s%d%s%d\n", "Ws_func recive ", rec_b, " bytes from clien ",  client_fd);
      warning_access_log(reciv_r); // пишем ссобытие в лог

      if(rec_b == 0 || rec_b == -1) // если клиент отвалился или что-то нехорошо, тогда...
       {
         memset(reciv_r, 0, 48);
         snprintf(reciv_r, 47, "%s%d%s%d\n", "Ws_func read return - ", rec_b, ", DIE clien - ",  client_fd);
         warning_access_log(reciv_r); // пишем ссобытие в лог
         if(close(client_fd) == -1) warning_access_log("Error close client in WS_1."); // закрываем соединение с клиентом
         pthread_exit(NULL);
       } 

      if(rec_b > 0)  // если чё то получили, то ...                    
       { 
         char masking_key[4] = {0,}; // сюда положим маску
         char opcode; // сюда тип фрейма
         int payload_len; // сюда длину сообщения (тела), то есть без служебных байтов 

         opcode = inbuf[0] & 0x0F;  
            printf("FIN: 0x%02x\n", inbuf[0] & 0x01);
            printf("RSV1: 0x%02x\n", inbuf[0] & 0x02);
            printf("RSV2: 0x%02x\n", inbuf[0] & 0x03);
            printf("RSV3: 0x%02x\n", inbuf[0] & 0x04);
            printf("Opcode: 0x%02x\n", inbuf[0] & 0x0F);
                      
         payload_len = inbuf[1] & 0x7F; 
            printf("Maska: 0x%02x\n", inbuf[1] & 0x80 ? 1:0);
            printf("Payload_len: %d\n", inbuf[1] & 0x7F);

         masking_key[0] = inbuf[2];
         masking_key[1] = inbuf[3];
         masking_key[2] = inbuf[4];
         masking_key[3] = inbuf[5];

              
         char payload[128] = {0,};

         if(opcode == WS_CLOSING_FRAME) // от клиента получен код закрытия соединения
          {
            memset(reciv_r, 0, 48);
            snprintf(reciv_r, 47, "%s%d\n", "Ws_func recive opcod - 0x08, DIE clien - ",  client_fd);
            warning_access_log(reciv_r); // пишем ссобытие в лог
            if(close(client_fd) == -1) warning_access_log("Error close client in WS_2."); // закрываем соединение с клиентом
            pthread_exit(NULL); // убиваем поtok
          }


         if(opcode == WS_PONG_FRAME) // от клиента получен PONG (маскированный)
          {
            int i = 6, pl = 0;
            for(; pl < payload_len; i++, pl++)
             {
               payload[pl] = inbuf[i]^masking_key[pl % 4]; 
             }
                     
            printf("\nRecive PONG and text \"%s\"\n", payload);
          }


         if(opcode == WS_TEXT_FRAME) // от клиента получен текст
          {
            int i = 6, pl = 0;
            for(; pl < payload_len; i++, pl++)
             {
               payload[pl] = inbuf[i]^masking_key[pl % 4]; 
             }
                     
            printf("\nReciv TEXT_FRAME from %d client, payload: %s\n", client_fd, payload);


            if(payload[0] == 'p' && payload[1] == 'i' && payload[2] == 'n' && payload[3] == 'g') // от клиента получен текст "ping"  
             {
               printf("\nPING client - %d\n", client_fd); 

               char ping[] = {0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f}; // Ping - не маскированный, тело содержит слово Hello, эта же слово вернётся с Понгом
               
               if(send(client_fd, ping, 7, 0) == -1)
                {
                  warning_access_log("Error PING."); 
                  if(close(client_fd) == -1) warning_access_log("Error close client in WS_3."); // закрываем соединение с клиентом
                  pthread_exit(NULL); 
                }

             }

            if(payload[0] == 'c' && payload[1] == 'l' && payload[2] == 'o' && payload[3] == 's' && payload[4] == 'e') // от клиента получен текст "close"
             {
               printf("\nClose client - %d\n", client_fd); 

               char close_client[] = {0x88, 0};
               
               if(send(client_fd, close_client, 2, 0) == -1)
                {
                  warning_access_log("Error CLOSE."); 
                  if(close(client_fd) == -1) warning_access_log("Error close client in WS_4."); // закрываем соединение с клиентом
                  pthread_exit(NULL); 
                }

             }

            if(payload[0] == 'h' && payload[1] == 'i') // от клиента получен текст "hi"
             {
               char messag[] = "Hi client - ";
               int message_size = (int) strlen(messag);
               char out_data[128] = {0,};
               memcpy(out_data + 2, messag, message_size); // копируем сообщение в массив "out_data" начиная со второго байта (первые два байта для опкода и длины тела)
               char nom_client[5] = {0,};
               sprintf(nom_client, "%d", client_fd); // номер клиента
               int nom_client_size = (int) strlen(nom_client);
               memcpy(out_data + 2 + message_size, nom_client, nom_client_size); // копируем номер клиента в массив "out_data" следом за сообщением

               message_size += nom_client_size; // получаем общую длину тела сообщения

               out_data[0] = 0x81;
               out_data[1] = (char)message_size;

               printf("\nSize out Msg: %d\n", message_size);
               
               if(send(client_fd, out_data, message_size + 2, 0) == -1) 
                {
                  warning_access_log("Error Hi."); 
                  if(close(client_fd) == -1) warning_access_log("Error close client in WS_5."); // закрываем соединение с клиентом
                  pthread_exit(NULL); 
                }
              }

          }
   
       } // END if(n > 0)  
   
    } // END while(1)

 } // END ws_func



int main(int argc, char *argv[])  
{  
  if(argc != 3) error_log("not argumets.");
     
  unsigned int PORTW = strtoul(argv[1], NULL, 0); // порт для web-сервера 80
  strncpy(patch_to_dir, argv[2], 63); // путь к файлу index.html
  warning_access_log("START");
  pthread_t ws_thread;
  

  /////////////////////////////////////////////////////////    WEB    ///////////////////////////////////////////////////////////////
  int one = 1;
  struct sockaddr_in svr_addr, cli_addr;
  socklen_t sin_len = sizeof(cli_addr);
 
  int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock < 0) error_log("not socket.");
 
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
 
  svr_addr.sin_family = AF_INET;
  svr_addr.sin_addr.s_addr = INADDR_ANY;
  svr_addr.sin_port = htons(PORTW);

  if(bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) 
   {
     close(sock);
     error_log("bind.");
   }
 
  if(listen(sock, 5) == -1) 
   {
     close(sock);
     error_log("listen.");
   }


  signal(SIGPIPE, SIG_IGN);

  char str_from_buf[FILESTR] = {0,};
  char result_file[FILESTR] = {0,};

      
  for(;;) 
   {
    client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
 
    if(client_fd == -1) continue;

    printf("Сonnected %s:%d client - %d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), client_fd);

    memset(buffer, 0, BUFSIZE);
    memset(str_from_buf, 0, FILESTR);
    memset(result_file, 0, FILESTR);
    char *p = NULL;

    if(read(client_fd, buffer, BUFSIZE - 1) == -1) warning_access_log("Error in main - read_client_fd.");

    int i = 0;
    for(; i < FILESTR; i++)
     {
       str_from_buf[i] = buffer[i];
       if(str_from_buf[i] == '\n') break;
       if(i > 31)
        {
          str_from_buf[i] = '\0';
          break;  
        }
     }


    if((strstr(str_from_buf, "GET / ")) != NULL) 
     {
       if(send(client_fd, response, (int)strlen(response), MSG_NOSIGNAL) == -1) warning_access_log("send response.");
       read_in_file("index.html");
     }


    /////////////////////////////////// WS /////////////////////////////////////////////////
    else if((strstr(str_from_buf, "GET /ws ")) != NULL) 
     {
       warning_access_log(buffer);
       if((p = strstr(buffer, "Sec-WebSocket-Key:")) != NULL)
        {                                                  
          char resultstr[64] = {0,};
          int i = 0, it = 0;
          for(i = 19; it < 24; i++, it++)
           {
             resultstr[it] = p[i];
           }

          strcat(resultstr, GUIDKey);

          printf("\n_____________|Key ot clienta__________|GUIDKey____________________________\n");
          printf("Result_stroka:%s\n", resultstr);


          ////////////////////////////sha1///////////////////////////////////////
          unsigned char temp[SHA_DIGEST_LENGTH] = {0,};
          SHA1(temp, resultstr, strlen(resultstr));


        ///////////////////// нужна только для того чтоб увидеть SHA1-хеш //////////////////////
          char buf[SHA_DIGEST_LENGTH*2] = {0,};                                               //
          for(i=0; i < SHA_DIGEST_LENGTH; i++)                                                // 
           {                                                                                  //
             sprintf((char*)&(buf[i*2]), "%02x", temp[i]);                                    //  
           }                                                                                  //
          printf("\nSHA1_hash:%s\n", buf);                                                    // 
        ////////////////////////////////////////////////////////////////////////////////////////


          ////////////////////////////Base64//////////////////////////////////// 
          unsigned char key_out[64] = {0,};
          base64_encode(temp, key_out, sizeof(temp));

          printf("\nKey_for_client:%s\n", key_out);

          sem_init(&sem, 0, 0);
          char resp[131] = {0,};
          snprintf(resp, 130, "%s%s%s", response_ws, key_out, "\r\n\r\n");
          if(send(client_fd, resp, sizeof(char) * strlen(resp), MSG_NOSIGNAL) == -1) warning_access_log("send response_ws.");
    

          //////////////////////////// START WS /////////////////////////////////
          if(pthread_create(&ws_thread, NULL, &ws_func, &client_fd) != 0) error_log("creating WS.");
          pthread_detach(ws_thread);
          sem_wait(&sem);
        }
     }

    else if((p = strstr(str_from_buf, ".png")) != NULL) 
     {
       int index = p - str_from_buf;
       int i = 0;
       int otbor = 0;
       for(; i < index + 3; i++)
        {
          result_file[i] = str_from_buf[i];
    
          if(result_file[i] == '/') 
           {
             otbor = i;
           }
        }

       memset(result_file, 0, FILESTR);
       strncpy(result_file, str_from_buf + otbor - 3, index -1); //  otbor + 1
       if(send(client_fd, response_img, (int)strlen(response_img), MSG_NOSIGNAL) == -1) warning_access_log("Error send response_img.");
       read_in_file(result_file);
     }

    else if((p = strstr(str_from_buf, ".css")) != NULL) 
     {
       int index = p - str_from_buf;
       int i = 0;
       int otbor = 0;
       for(; i < index + 3; i++)
        {
          result_file[i] = str_from_buf[i];
    
          if(result_file[i] == '/') 
           {
             otbor = i;
           }
        }

       memset(result_file, 0, FILESTR);
       strncpy(result_file, str_from_buf + otbor + 1, index -1);
       if(send(client_fd, response_css, (int)strlen(response_css), MSG_NOSIGNAL) == -1) warning_access_log("Error send response_css.");
       read_in_file(result_file);
     }


    else if((strstr(str_from_buf, "jquery.js")) != NULL) 
     {
       if(send(client_fd, response_js, (int)strlen(response_js), MSG_NOSIGNAL) == -1) warning_access_log("Error send response_js.");
       read_in_file("jquery.js");
     }

    else if((strstr(str_from_buf, "favicon.ico")) != NULL) 
     {
       if(send(client_fd, response_xicon, (int)strlen(response_xicon), MSG_NOSIGNAL) == -1) warning_access_log("Error send favicon.ico.");
       read_in_file("favicon.ico");
     }

    else 
     {
       if(send(client_fd, response_403, sizeof(response_403), MSG_NOSIGNAL) == -1) warning_access_log("Error send response_403.");
       if(close(client_fd) == -1) warning_access_log("Error close client_fd 403.");
       warning_access_log(buffer);
     }

   }// end for(;;)

} //END main



// gcc -Wall -Wextra -Werror websocket.c -o websocket -pthread 
// sudo ./websocket 85 /home/dima/Dropbox/ci/c-websocket/

#######################################################

Makefile для Openwrt

( Makefile для Openwrt )

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <!--<link rel="stylesheet" href="style.css" type="text/css" />-->

        <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
        <!--<script src="jquery.js"></script>-->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
  
        <style type="text/css">
           .knopka {
           height: 40px;
           width: 120px;
           text-align: center;
           line-height: 2.1;
           cursor: pointer;
           border: 1px solid #999;
           border-radius: 10px;
           }
           v{}
        </style>

        <script type="text/javascript">
            $(function() {
                window.WebSocket = window.WebSocket || window.MozWebSocket;
                var websocket = new WebSocket('ws://192.168.5.196:86/ws');


                websocket.onopen = function () {
                    $('h1').css('color', '#65c178'); /* green */
                };

                websocket.onclose = function (e) {
                    console.log("WebSocket: ", e)
                    $('h1').css('color', '#fe457e'); /* red */
                };


                websocket.onerror = function () {
                    $('h1').css('color', '#fe457e'); /* red */
     
                };

                websocket.onmessage = function (message) {
                    console.log(message.data);
                    $('v').append($('<p>', { text: message.data }));
                };
                
                $('.knp0').click(function(e) {
                    e.preventDefault();
                    websocket.send($('input').val());
                    $('input').val('');
                });

                $('.knp1').click(function(e) {
                    e.preventDefault();
                    websocket.send("hi");
                });

                $('.knp2').click(function(e) {
                    e.preventDefault();
                    websocket.send("ping");
                });



            });
        </script>
        </head>
    <body>
        <h1>WebSocket</h1>
     
          <div class='knopka knp1'>Hi...</div><br>
          <div class='knopka knp2'>Call PING</div><br>
          <div class='knopka knp0'>Send</div> <input type="text" />
          <v></v>
    </body>
</html>

Бинарник запускается с двумя параметрами — порт (80) и путь к папке с программой и файлом index.html.

Код содержит много ненужной отладочной информации и всяких логов. Так же есть некоторые неправильности (например потоки обращаются к одной и той же функции).

Заранее спасибо за все ваши высказывания.

П.С. То же самое я написал на geektimes, но там это не вызвало интереса.

 

stD
()

Вопрос про потоки и мьютексы (си)

Форум — Development

Здравствуйте. Скажите пожалуйста...

Есть приложение, в нём несколько потоков (выполняют одну и ту же функцию со своими локальными переменными), эти потоки обращаются к глобальной переменной, но не изменяют её, а только читают. Нужно ли в этом случае ставить pthread_mutex_lock() ?

 

stD
()

RSS подписка на новые темы