[LINUX] Asynchronous programming with libev # 3

Here is an example of an HTTPS client over SSL using libev.

Add the following path or link to xcode:

Header file path Library file path Dependent libraries
usr/local/opt/openssl/include /usr/local/opt/openssl/lib libcrypto.a态libssl.a

Blocking is used up to the connection. If there is no problem with communication, the html of the top page of google will be displayed on the console.

Please see # 1 for installation of libev.

#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/Shut down the TLS connection.
    SSL_shutdown(_ssl);
    SSL_free(_ssl);
    
    ::shutdown(socket, SD_BOTH);
    ::close(socket);
    
    SSL_CTX_free(_ctx);
    ERR_free_strings();
    
}

int get_error(){
    return errno;
}

//SSL preparation
SSL_CTX * ssl_setup(){
    
    //SSL library initialization.
    SSL_library_init();
    
    //Preparing to string the error.
    SSL_load_error_strings();
    
    //Global context initialization.
    const SSL_METHOD *meth = SSLv23_method();
    
    return SSL_CTX_new(meth);
}

//TCP connection.
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;
    }
    
    
    //Comment out the following to terminate with a timeout.
    
    // I/Disassociate O from loop.
    //ev_io_stop (EV_A_ w);
    
    //Break the event loop.
    //ev_unloop (EV_A_ EVUNLOOP_ALL);
}

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

    //Stop the timer.
    ev_timer_stop (EV_A_ w);
    
    //Break the event loop.
    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();
    
    //Create an ssl session object.
    _ssl = SSL_new(_ctx);
    
    //Associate with socket.
    SSL_set_fd(_ssl, _socket);

    //SSL connection.
    int error = 0;
    if (SSL_connect(_ssl) <= 0){
        error = get_error();
        ::shutdown(_socket, SD_BOTH);
        close_socket(_socket, _ctx, _ssl);
        return 0;
    }
    
    //Set to non-blocking.
    int flag = fcntl(_socket, F_GETFL, 0);
    fcntl(_socket, F_SETFL, flag | O_NONBLOCK);
    
    
    //Create an event loop.
    struct ev_loop *loop = ev_loop_new (0);

    // I/O watch object.
    ev_io io_watcher;
    
    // I/For O watch objects
    //Associate callbacks with FDs with event types.
    ev_io_init (&io_watcher, io_cb, _socket, EV_READ);
    
    // I/Associate an event loop with an O watch object.
    ev_io_start (loop, &io_watcher);


    //Create a timer object.
    ev_timer timeout_watcher;
    
    //Associate a callback with a timer object and specify a timeout.
    ev_timer_init (&timeout_watcher, timeout_cb, 20, 0.);
    
    //Associate an event loop with a timer object.
    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";
    
    //Loop start.
    ev_loop (loop, 0);

    //If you don't want to block.
    //ev_loop (loop, EVLOOP_NONBLOCK);

    
    std::cout << "end\n";
    
    //Discard the created event loop
    ev_loop_destroy(loop);
    
    close_socket(_socket, _ctx, _ssl);
    
    return 0;
}

Compile with 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

Asynchronous programming with libev # 2
Asynchronous programming with libev
Asynchronous programming with libev # 3
3. 3. AI programming with Python
Competitive programming with python
Shader programming with pyOpenGL
Linear Programming with PuLP
Programming with Python Flask
Programming with Python and Tkinter
Try GUI programming with Hy
Programming education game with SenseHAT
Network programming with Python Scapy
[Python] Object-oriented programming learned with Pokemon
Easy Python + OpenCV programming with Canopy
[Python] Asynchronous request with async / await
Programming for humans with a well-defined __repr__
GUI programming with kivy ~ Part 4 Various buttons ~
A complete understanding of Python's asynchronous programming
Do embedded programming with test-driven development with googletest
Sound programming with Go (super introductory level)
What you can do with programming skills