Openssl uses non-blocking bio and opensslbio
Collation
In the project, you need to access https-encrypted Web pages. In order to ensure concurrency, you need to use non-blocking sockets and search to find that there are not many introductions about such use cases, so here we record the process of use.
In the project, the ssl library used is the old sll library-openssl. The io multiplexing technology used is epoll.
Core Process
The overall process is similar to accessing a non-encrypted website. The differences are as follows:
Establish a connection
First, open the socket handle and set the required properties.
1 int sock_fd = -1;2 int flags = -1;3 sock_fd = socket(AF_INET, SOCK_STREAM, 0);4 flags = fcntl(sockfd, F_GETFL, 0);5 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
Add the handle to epoll management.
1 epoll_event ev;2 ev.events = EPOLLIN | EPOLLOUT | EPOLLET3 ev.data.ptr = your_ev_info;4 epoll_ctl(epfd, EPOLL_CTL_ADD, url_item->sockfd, &ev);
Now, you can start the real connection process. Like a common tcp connection, call the connect system call. In non-blocking io, you must use the connect return value and errno to determine the connection status and adopt different policies.
1 struct sockaddr_in serv_addr; 2 3 if (connect (sock_fd, (sockaddr *) & serv_addr, sizeof (sockaddr) <0) {4 // No connection is established immediately, errno 5 if (errno! = EINPROGRESS & errno! = EINTR) {6 // failed. Kill 7 epoll_ctl (epfd, EPOLL_CTL_DEL, sock_fd, NULL) from epoll ); 8} 9} else {10 // 11 prepare_connect_ssl (your_ev_info); 12}
If the connection fails immediately, epoll will be triggered after the connection is successful. In your_ev_info, we need to save the current state to determine the function to be called through the state after epoll_wait. These are epoll details.
Assume that the connection is successful, and preparations before the SSL handshake are started.
1 SSL_CTX * ssl_ctx; 2 SSL * ssl; 3 4 ssl_ctx = SSL_CTX_new (TLSv1_method (); 5 ssl = SSL_new (url_item-> ssl_ctx); 6 SSL_set_mode (url_item-> ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); 7 8 // bind the SSL and socket handle 9 SSL_set_fd (ssl, sock_fd );
This step is separated from the subsequent SSL handshake process because the SSL handshake may be called multiple times without blocking io, And this part only needs to be called once.
Start SSL handshake now
1 int ssl_conn_ret = SSL_connect (ssl); 2 if (1 = ssl_conn_ret) {3 // start interacting with peer 4} else if (-1 = ssl_conn_ret) {5 // If the handshake fails, the current status is 6 int ssl_conn_err = SSL_get_error (ssl, ssl_conn_ret) based on the error code ); 7 if (SSL_ERROR_WANT_READ = ssl_conn_err | 8 SSL_ERROR_WANT_WRITE = ssl_conn_err) {9 // The handshake 10} 11} else {12 // connection failed, do the necessary processing 13 if (0! = Ssl_conn_ret) {14 SSL_shutdown (ssl); 15} 16 SSL_free (ssl); 17 SSL_CTX_free (ssl_ctx); 18}
When the handshake fails, you need to call the code at the same time after epoll is triggered to continue the handshake process.
Now, the connection process is complete.
Send and read data
Since data transmission and reading may not completely complete the specified length, you need to determine the corresponding return value to determine whether to continue sending or reading.
1 // send data 2 int ret = SSL_write (ssl, buf + last_write_pos, buf_len-last_write_pos); 3 4 // read data 5 int ret = SSL_read (ssl, buf + last_read_pos, buf_len-last_read_pos );
Close connection
// Close the ssl connection SSL_shutdown (ssl); SSL_free (ssl); SSL_CTX_free (ssl_ctx); // then close socketclose (sock_fd );
Key record
The overall process is smooth. One of the most important points is about the edge trigger cooperation between openssl and epoll.
When you need to use the edge trigger of epoll, you must note that SSL_read only reads a complete encrypted segment at most. Therefore, when the data volume that can be read at one time exceeds this value, you need to call SSL_read cyclically until reading fails. Otherwise, the data in the buffer is not fully read.