LINUX.ORG.RU

Telegrambot на СИ - SSL_read

 ,


0

2

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

Пишу сервер для бота работающий через 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 (всего исправлений: 1)

Скорей всего у Telegram сервер написан так, что делает два (или более) вызова write(2) на сокете, на котором включена опция TCP_NODELAY (или подобная ей). Это приводит к тому, что записанные данные сразу сбрасываются в сеть, а не копятся в буфере. При помощи Wireshark можно посмотреть, что в действительности происходит.

И вообще, не стоит завязываться на «один read(2) — полное считывание данных». Если не хочется реализовывать полное считывание из сокета — посмотри в сторону bufferevents из библиотеки libevent.

edigaryev ★★★★★
()
Ответ на: комментарий от edigaryev

не стоит завязываться на «один read(2) — полное считывание данных

По какой причине? Афаик очередной блокирующий read вернет 0 при нормальном закрытии с той стороны, не раз проверено и в общем-то задокументировано. А если закрытие ненормальное (-1), то дальше и смысла нет.

Если я не напутал конечно.

2тс: читай не один раз, а пока признак eof не прилетит, если SSL_read его вообще возвращает конечно. То, что читаться будет хз сколько раз - это норма, буферизация дело мутное.

anonymous
()
Ответ на: комментарий от edigaryev

Спасибо. С использованием библиотек проблема, эта штука работает на роутере, свободного места ~200кб.

stD
() автор топика
Ответ на: комментарий от edigaryev

Ай, блджад, я загнался, ты ж не об этом вообще говорил, сорян.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.