[LINUX] Asynchrone Programmierung mit libev # 3

Hier ist ein Beispiel eines HTTPS-Clients mit SSL unter Verwendung von libev.

Fügen Sie den folgenden Pfad oder Link zu xcode hinzu:

Header-Dateipfad Pfad der Bibliotheksdatei Abhängige Bibliotheken
usr/local/opt/openssl/include /usr/local/opt/openssl/lib libcrypto.a、libssl.a

Ich blockiere die Verbindung. Wenn es kein Problem mit der Kommunikation gibt, wird das HTML der oberen Seite von Google auf der Konsole angezeigt.

Die Installation von libev finden Sie unter # 1.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string>
#include <ev.h>
#include <iostream>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#define SOCKET int
#define SD_BOTH SHUT_WR

#include <openssl/ssl.h>
#include <openssl/err.h>

#define READ_BUF_SIZE 4096
#define BUF_SIZE 4097
#define PORT 443

SSL *_ssl;

void close_socket(SOCKET socket, SSL_CTX *_ctx, SSL *_ssl){
    
    // SSL/Beenden Sie die TLS-Verbindung.
    SSL_shutdown(_ssl);
    SSL_free(_ssl);
    
    ::shutdown(socket, SD_BOTH);
    ::close(socket);
    
    SSL_CTX_free(_ctx);
    ERR_free_strings();
    
}

int get_error(){
    return errno;
}

//Vorbereitung auf SSL
SSL_CTX * ssl_setup(){
    
    //Initialisierung der SSL-Bibliothek.
    SSL_library_init();
    
    //Vorbereiten der Fehlerbehebung.
    SSL_load_error_strings();
    
    //Globale Kontextinitialisierung.
    const SSL_METHOD *meth = SSLv23_method();
    
    return SSL_CTX_new(meth);
}

//TCP-Verbindung.
SOCKET tcp_connect(const char* host){
    
    struct hostent *hp;
    struct sockaddr_in addr;
    SOCKET _socket;
    
    if (!(hp = gethostbyname(host))){
        return -1;
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_addr = *(struct in_addr*)hp->h_addr_list[0];
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    
    if ((_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0){
        return -1;
    }
    if (connect(_socket, (struct sockaddr *)&addr, sizeof(addr))<0){
        return -1;
    }
    
    return _socket;
}


static void io_cb (EV_P_ ev_io *w, int revents)
{
    char buf[BUF_SIZE] = { 0 };
    char* p = buf;
    int r = SSL_read(_ssl, p, READ_BUF_SIZE);
    
    if(r > 0){
        std::cout << p;
    }
    
    
    //Kommentieren Sie Folgendes aus, um mit einer Zeitüberschreitung zu beenden.
    
    // I/Trennen Sie O von der Schleife.
    //ev_io_stop (EV_A_ w);
    
    //Unterbrechen Sie die Ereignisschleife.
    //ev_unloop (EV_A_ EVUNLOOP_ALL);
}

static void timeout_cb (EV_P_ ev_timer *w, int revents)
{
    std::cout << "timeout\n";

    //Stoppen Sie den Timer.
    ev_timer_stop (EV_A_ w);
    
    //Unterbrechen Sie die Ereignisschleife.
    ev_unloop (EV_A_ EVUNLOOP_ONE);
}

int main(int argc, const char * argv[])
{
    
    std::string host = "www.google.co.jp";
    
    SOCKET _socket = tcp_connect(host.c_str());
    
    SSL_CTX *_ctx = ssl_setup();
    
    //Erstellen Sie ein SSL-Sitzungsobjekt.
    _ssl = SSL_new(_ctx);
    
    //Mit Socket verknüpfen.
    SSL_set_fd(_ssl, _socket);

    //SSL-Verbindung.
    int error = 0;
    if (SSL_connect(_ssl) <= 0){
        error = get_error();
        ::shutdown(_socket, SD_BOTH);
        close_socket(_socket, _ctx, _ssl);
        return 0;
    }
    
    //Auf nicht blockierend einstellen.
    int flag = fcntl(_socket, F_GETFL, 0);
    fcntl(_socket, F_SETFL, flag | O_NONBLOCK);
    
    
    //Erstellen Sie eine Ereignisschleife.
    struct ev_loop *loop = ev_loop_new (0);

    // I/O Objekt beobachten.
    ev_io io_watcher;
    
    // I/Für O Objekte beobachten
    //Verknüpfen Sie Rückrufe mit FDs mit Ereignistypen.
    ev_io_init (&io_watcher, io_cb, _socket, EV_READ);
    
    // I/Verknüpfen Sie eine Ereignisschleife mit einem O-Überwachungsobjekt.
    ev_io_start (loop, &io_watcher);


    //Erstellen Sie ein Timer-Objekt.
    ev_timer timeout_watcher;
    
    //Verknüpfen Sie einen Rückruf mit einem Zeitgeberobjekt und geben Sie eine Zeitüberschreitung an.
    ev_timer_init (&timeout_watcher, timeout_cb, 20, 0.);
    
    //Verknüpfen Sie eine Ereignisschleife mit einem Timer-Objekt.
    ev_timer_start (loop, &timeout_watcher);
    
    
    std::string str = "GET / HTTP/1.1\r\nHost: " + host;
    str = str + "\r\n\r\n";
    int w = SSL_write(_ssl, str.c_str(), static_cast<int>(str.length()));
    
    
    std::cout << "write count" << w << "\n";
    
    //Schleifenstart.
    ev_loop (loop, 0);

    //Wenn Sie nicht blockieren möchten.
    //ev_loop (loop, EVLOOP_NONBLOCK);

    
    std::cout << "end\n";
    
    //Verwerfen Sie die erstellte Ereignisschleife
    ev_loop_destroy(loop);
    
    close_socket(_socket, _ctx, _ssl);
    
    return 0;
}

Kompilieren Sie mit gcc

gcc -o test test.cpp -lev -I/usr/local/Cellar/libev/4.15/include/ -L/usr/local/Cellar/libev/4.15/lib/ -L/usr/local/opt/openssl/lib -I/usr/local/opt/openssl/include -lcrypto -lssl -lstdc++

Recommended Posts

Asynchrone Programmierung mit libev # 2
Asynchrone Programmierung mit libev
Asynchrone Programmierung mit libev # 3
3. 3. KI-Programmierung mit Python
Wettbewerbsfähige Programmierung mit Python
Shader-Programmierung mit pyOpenGL
Lineare Programmierung mit PuLP
Programmieren mit Python Flask
Programmieren mit Python und Tkinter
Versuchen Sie die GUI-Programmierung mit Hy
Programmier-Lernspiel mit SenseHAT
Netzwerkprogrammierung mit Python Scapy
[Python] Mit Pokemon erlernte objektorientierte Programmierung
Einfache Python + OpenCV-Programmierung mit Canopy
[Python] Asynchrone Anfrage mit async / await
Programmierung für Menschen mit einem genau definierten __repr__
GUI-Programmierung mit kivy ~ Teil 4 Verschiedene Tasten ~
Vollständiges Verständnis der asynchronen Python-Programmierung
Führen Sie Embedded-Programmierung mit testgetriebener Entwicklung mit googletest durch
Soundprogrammierung mit Go (Super-Einführungslevel)
Was Sie mit Programmierkenntnissen machen können