OpenSSL之ssl库_ssl_read_ex-程序员宅基地

技术标签: openssl  

ssl

OpenSSL的SSL/TLS库,实现了SSL(Secur)/TLS(Transport Layer Security)/DTLS(Datagram Transport Layer Security)协议的多个版本。

SSL_CTX对象包含证书、算法等信息,用于建立TLS/SSL连接。

网络连接建立后可以赋值给SSL对象,然后可以使用SSL对象完成握手操作(SSL_accept或SSL_connect或SSL_do_handshake),握手完成后就可以读写了。关闭网络连接前先调用SSL_shutdown关闭TLS/SSL连接。

  • SSL_METHOD 特定协议的操作函数,创建SSL_CTX时需要
  • SSL_CIPHER 加密算法信息,可以在SSL_CTX中指定可用的算法集合,SSL_SESSION中会记录实际使用的算法。
  • SSL_CTX 全局配置,包含SSL配置的默认值。
  • SSL_SESSION 包含当前session信息的结构,包括:加密算法、证书、密钥等
  • SSL SSL连接,

使用证书和密钥

        #include <openssl/ssl.h>

        /* CA 设置 */
        int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath);
        int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile);
        int SSL_CTX_load_verify_store(SSL_CTX *ctx, const char *CAstore);

        int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);

        int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx);
        int SSL_CTX_set_default_verify_file(SSL_CTX *ctx);
        int SSL_CTX_set_default_verify_store(SSL_CTX *ctx);

        int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
                                          const char *CApath);

        /* CA证书相关操作 */
        void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *list);
        void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *list);
        STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *s);
        STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx);
        int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *cacert);
        int SSL_add_client_CA(SSL *ssl, X509 *cacert);

        void SSL_CTX_set0_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list);
        void SSL_set0_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list);
        const STACK_OF(X509_NAME) *SSL_CTX_get0_CA_list(const SSL_CTX *ctx);
        const STACK_OF(X509_NAME) *SSL_get0_CA_list(const SSL *s);
        int SSL_CTX_add1_to_CA_list(SSL_CTX *ctx, const X509 *x);
        int SSL_add1_to_CA_list(SSL *ssl, const X509 *x);

        const STACK_OF(X509_NAME) *SSL_get0_peer_CA_list(const SSL *s);

        /* 证书链设置 */
        int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
        int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
        int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509);
        int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509);
        int SSL_CTX_get0_chain_certs(SSL_CTX *ctx, STACK_OF(X509) **sk);
        int SSL_CTX_clear_chain_certs(SSL_CTX *ctx);

        int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *sk);
        int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *sk);
        int SSL_add0_chain_cert(SSL *ssl, X509 *x509);
        int SSL_add1_chain_cert(SSL *ssl, X509 *x509);
        int SSL_get0_chain_certs(SSL *ssl, STACK_OF(X509) **sk);
        int SSL_clear_chain_certs(SSL *ssl);

        int SSL_CTX_build_cert_chain(SSL_CTX *ctx, flags);
        int SSL_build_cert_chain(SSL *ssl, flags);

        /* 证书和密钥设置 */
        int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);
        int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d);
        int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
        int SSL_use_certificate(SSL *ssl, X509 *x);
        int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len);
        int SSL_use_certificate_file(SSL *ssl, const char *file, int type);

        int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file);
        int SSL_use_certificate_chain_file(SSL *ssl, const char *file);

        int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey);
        int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d,
                                        long len);
        int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
        int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
        int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len);
        int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type);
        int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey);
        int SSL_use_PrivateKey_ASN1(int pk, SSL *ssl, const unsigned char *d, long len);
        int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type);
        int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa);
        int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const unsigned char *d, long len);
        int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type);

        int SSL_CTX_check_private_key(const SSL_CTX *ctx);
        int SSL_check_private_key(const SSL *ssl);

        int SSL_CTX_use_cert_and_key(SSL_CTX *ctx, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override);
        int SSL_use_cert_and_key(SSL *ssl, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override);

        /* 证书选择 */
        int SSL_CTX_select_current_cert(SSL_CTX *ctx, X509 *x509);
        int SSL_select_current_cert(SSL *ssl, X509 *x509);
        int SSL_CTX_set_current_cert(SSL_CTX *ctx, long op);
        int SSL_set_current_cert(SSL *ssl, long op);

SSL_CTX操作

        #include <openssl/ssl.h>

        SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq,
                                const SSL_METHOD *method);
        SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
        int SSL_CTX_up_ref(SSL_CTX *ctx);

        const SSL_METHOD *TLS_method(void);
        const SSL_METHOD *TLS_server_method(void);
        const SSL_METHOD *TLS_client_method(void);

        const SSL_METHOD *SSLv23_method(void);
        const SSL_METHOD *SSLv23_server_method(void);
        const SSL_METHOD *SSLv23_client_method(void);

        #ifndef OPENSSL_NO_SSL3_METHOD
        const SSL_METHOD *SSLv3_method(void);
        const SSL_METHOD *SSLv3_server_method(void);
        const SSL_METHOD *SSLv3_client_method(void);
        #endif

        #ifndef OPENSSL_NO_TLS1_METHOD
        const SSL_METHOD *TLSv1_method(void);
        const SSL_METHOD *TLSv1_server_method(void);
        const SSL_METHOD *TLSv1_client_method(void);
        #endif

        #ifndef OPENSSL_NO_TLS1_1_METHOD
        const SSL_METHOD *TLSv1_1_method(void);
        const SSL_METHOD *TLSv1_1_server_method(void);
        const SSL_METHOD *TLSv1_1_client_method(void);
        #endif

        #ifndef OPENSSL_NO_TLS1_2_METHOD
        const SSL_METHOD *TLSv1_2_method(void);
        const SSL_METHOD *TLSv1_2_server_method(void);
        const SSL_METHOD *TLSv1_2_client_method(void);
        #endif

        const SSL_METHOD *DTLS_method(void);
        const SSL_METHOD *DTLS_server_method(void);
        const SSL_METHOD *DTLS_client_method(void);

        #ifndef OPENSSL_NO_DTLS1_METHOD
        const SSL_METHOD *DTLSv1_method(void);
        const SSL_METHOD *DTLSv1_server_method(void);
        const SSL_METHOD *DTLSv1_client_method(void);
        #endif

        #ifndef OPENSSL_NO_DTLS1_2_METHOD
        const SSL_METHOD *DTLSv1_2_method(void);
        const SSL_METHOD *DTLSv1_2_server_method(void);
        const SSL_METHOD *DTLSv1_2_client_method(void);
        #endif

        void SSL_CTX_free(SSL_CTX *ctx);

        uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t options);
        uint64_t SSL_set_options(SSL *ssl, uint64_t options);

        uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t options);
        uint64_t SSL_clear_options(SSL *ssl, uint64_t options);

        uint64_t SSL_CTX_get_options(const SSL_CTX *ctx);
        uint64_t SSL_get_options(const SSL *ssl);

        long SSL_get_secure_renegotiation_support(SSL *ssl);


        typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);

        void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb verify_callback);
        void SSL_set_verify(SSL *ssl, int mode, SSL_verify_cb verify_callback);
        SSL_get_ex_data_X509_STORE_CTX_idx(void);

        void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth);
        void SSL_set_verify_depth(SSL *ssl, int depth);

        int SSL_verify_client_post_handshake(SSL *ssl);
        void SSL_CTX_set_post_handshake_auth(SSL_CTX *ctx, int val);
        void SSL_set_post_handshake_auth(SSL *ssl, int val);


        int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version);
        int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version);
        int SSL_CTX_get_min_proto_version(SSL_CTX *ctx);
        int SSL_CTX_get_max_proto_version(SSL_CTX *ctx);

        int SSL_set_min_proto_version(SSL *ssl, int version);
        int SSL_set_max_proto_version(SSL *ssl, int version);
        int SSL_get_min_proto_version(SSL *ssl);
        int SSL_get_max_proto_version(SSL *ssl);


        X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx);
        X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl);
        int SSL_CTX_set1_param(SSL_CTX *ctx, X509_VERIFY_PARAM *vpm);
        int SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm);
        

一些SSL_CTX中的设置也可以在SSL中单独设置或修改,接口也一并在此列出。

SSL_CONF_CTX操作

        #include <openssl/ssl.h>

        SSL_CONF_CTX *SSL_CONF_CTX_new(void);
        void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx);

        unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags);
        unsigned int SSL_CONF_CTX_clear_flags(SSL_CONF_CTX *cctx, unsigned int flags);

        unsigned int SSL_CONF_CTX_set1_prefix(SSL_CONF_CTX *cctx, const char *prefix);

        int SSL_CONF_cmd(SSL_CONF_CTX *ctx, const char *option, const char *value);
        int SSL_CONF_cmd_value_type(SSL_CONF_CTX *ctx, const char *option);

        int SSL_CONF_cmd_argv(SSL_CONF_CTX *cctx, int *pargc, char ***pargv);

        void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx);
        void SSL_CONF_CTX_set_ssl(SSL_CONF_CTX *cctx, SSL *ssl);

SSL基本操作

        #include <openssl/ssl.h>

        SSL *SSL_new(SSL_CTX *ctx);
        SSL *SSL_dup(SSL *s);
        int SSL_up_ref(SSL *s);
        void SSL_free(SSL *ssl);

        int SSL_set_fd(SSL *ssl, int fd);
        int SSL_set_rfd(SSL *ssl, int fd);
        int SSL_set_wfd(SSL *ssl, int fd);
        
        int SSL_get_fd(const SSL *ssl);
        int SSL_get_rfd(const SSL *ssl);
        int SSL_get_wfd(const SSL *ssl);
        
        void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio);
        void SSL_set0_rbio(SSL *s, BIO *rbio);
        void SSL_set0_wbio(SSL *s, BIO *wbio);

        BIO *SSL_get_rbio(SSL *ssl);
        BIO *SSL_get_wbio(SSL *ssl);
                
        int SSL_accept(SSL *ssl);
        int SSL_connect(SSL *ssl);

        void SSL_set_connect_state(SSL *ssl);
        void SSL_set_accept_state(SSL *ssl);
        int SSL_is_server(const SSL *ssl);
        int SSL_do_handshake(SSL *ssl);
        
        int SSL_shutdown(SSL *ssl);
        int SSL_get_shutdown(const SSL *ssl);
        int SSL_clear(SSL *ssl);

        ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size, int flags);
        int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written);
        int SSL_write(SSL *ssl, const void *buf, int num);

        int SSL_pending(const SSL *ssl);
        int SSL_has_pending(const SSL *s);
        
        int SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
        int SSL_read(SSL *ssl, void *buf, int num);

        int SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
        int SSL_peek(SSL *ssl, void *buf, int num);

        int SSL_get_error(const SSL *ssl, int ret);

这些SSL函数不包含socket的基础操作,必须跟系统的socket或BIO结合使用。

   The behaviour of SSL_accept() depends on the underlying BIO.

   If the underlying BIO is blocking, SSL_accept() will only return once the handshake has been finished or an error occurred.

   If the underlying BIO is nonblocking, SSL_accept() will also return when the underlying BIO could not satisfy the needs of
   SSL_accept() to continue the handshake, indicating the problem by the return value -1.  In this case a call to SSL_get_error() with
   the return value of SSL_accept() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. The calling process then must repeat the
   call after taking appropriate action to satisfy the needs of SSL_accept().  The action depends on the underlying BIO. When using a
   nonblocking socket, nothing is to be done, but select() can be used to check for the required condition. When using a buffering BIO,
   like a BIO pair, data must be written into or retrieved out of the BIO before being able to continue.

基于BIO的SSL操作

BIO socket 操作

#include <openssl/bio.h>

const BIO_METHOD *BIO_s_accept(void);

long BIO_set_accept_name(BIO *b, char *name);
char *BIO_get_accept_name(BIO *b);

long BIO_set_accept_port(BIO *b, char *port);
char *BIO_get_accept_port(BIO *b);

BIO *BIO_new_accept(char *host_port);

long BIO_set_nbio_accept(BIO *b, int n);
long BIO_set_accept_bios(BIO *b, char *bio);

char *BIO_get_peer_name(BIO *b);
char *BIO_get_peer_port(BIO *b);
long BIO_get_accept_ip_family(BIO *b);
long BIO_set_accept_ip_family(BIO *b, long family);

long BIO_set_bind_mode(BIO *b, long mode);
long BIO_get_bind_mode(BIO *b);

int BIO_do_accept(BIO *b);

注意:上述操作仅是socket操作,没有涉及SSL相关内容,要实现SSL通信,还需要结合以下SSL操作

SSL BIO操作

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

const BIO_METHOD *BIO_f_ssl(void);

long BIO_set_ssl(BIO *b, SSL *ssl, long c);
long BIO_get_ssl(BIO *b, SSL **sslp);
long BIO_set_ssl_mode(BIO *b, long client);
long BIO_set_ssl_renegotiate_bytes(BIO *b, long num);
long BIO_set_ssl_renegotiate_timeout(BIO *b, long seconds);
long BIO_get_num_renegotiates(BIO *b);

BIO *BIO_new_ssl(SSL_CTX *ctx, int client);
BIO *BIO_new_ssl_connect(SSL_CTX *ctx);
BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx);
int BIO_ssl_copy_session_id(BIO *to, BIO *from);
void BIO_ssl_shutdown(BIO *bio);

long BIO_do_handshake(BIO *b);

操作是否需要重试

在非阻塞模式下,操作无法立即完成时会返回错误,应用需要判断是需要重试还是真的有错误,判断是否需要重试、重试的原因和重试的操作:

int BIO_should_read(BIO *b);
int BIO_should_write(BIO *b);
int BIO_should_io_special(iBIO *b);
int BIO_retry_type(BIO *b);
int BIO_should_retry(BIO *b);

BIO *BIO_get_retry_BIO(BIO *bio, int *reason);
int BIO_get_retry_reason(BIO *bio);
void BIO_set_retry_reason(BIO *bio, int reason);

关于accept socket的非阻塞模块

设置非阻塞模式:long BIO_set_nbio_accept(BIO *b, int n);

   If the underlying accept socket is nonblocking and BIO_do_accept() is called to await an incoming connection it is possible for
   BIO_should_io_special() with the reason BIO_RR_ACCEPT. If this happens then it is an indication that an accept attempt would block:
   the application should take appropriate action to wait until the underlying socket has accepted a connection and retry the call.

如果accept socket是非阻塞模式,调用BIO_do_accept来等待连接,当前没有连接时会返回错误,BIO_should_retry会返回true,说明操作需要重试,BIO_should_io_special会返回true并且BIO_get_retry_reason会返回 BIO_RR_ACCEPT,这种情况说明accept操作需要阻塞等待,应用程序应该等待直到底层socket接受一个连接后再重试BIO_do_accept。等待可用使用select/poll/epoll来实现。

demo解读

server-arg

  • 首先需要构建一个用于server端的SSL_CTX对象ctx:ctx = SSL_CTX_new(TLS_server_method());
  • 再构建一个SSL_CONF_CTX对象cctx对ctx进行配置:cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
  • 构建server socket BIO: in = BIO_new_accept(port) 等价于:
    BIO * in = BIO_new(BIO_s_accept());
    BIO_set_accept_name(in, port)
    
  • 构建一个用于server端连接的BIO:ssl_bio = BIO_new_ssl(ctx, 0);
    将ssl_bio指定用于server socket传入连接BIO的处理,每个传入连接会复制ssl_bio对象(链),使用ssl_bio一致的方式处理传入连接,即相当于:
    int fd = accept();
    BIO* conn_io = BIO_new_fd(fd, 1);
    ssl_io = BIO_dup_chain(ssl_bio);
    BIO_push(ssl_io, conn_io);
    
  • 接受连接:BIO_do_accept(in)
  • 接受连接后,进行读写操作BIO_read(in, buf, 512);
    新建连接对应的BIO对象被串到accept BIO之后,即accept->socket,此时读写accept BIO对象,相当于读写socket BIO,而没有接受连接之前读写accept BIO,accept BIO在BIO链的末端,会先等待连接,再在socket BIO上执行读写操作。而之前还调用了BIO_set_accept_bios(),BIO链应该是accept->otherbios->socket。
  • 连接用完之后要关闭它,先将它从BIO链中移除:tmp = BIO_pop(in);
    这里返回的tmp实际是BIO链otherbios->socket,可以关闭它,也可以做其他IO操作,需要并发处理多个连接时,通常在BIO_do_accept之后,执行BIO_pop得到对应连接的BIO,进行后续处理,而accept BIO就可以再次执行BIO_do_accept
  • 关闭socket BIO:BIO_free_all(tmp);
    注意这里是一个BIO链,所以用BIO_free_all全部关闭。
  • 最后关闭accept BIO:IO_free(in);
    关闭accept BIO的同时会自动关闭之前设置的ssl_bio。

client-arg

  • 构建一个用于client端的SSL_CTX对象ctx:ctx = SSL_CTX_new(TLS_client_method());
  • 再构建一个SSL_CONF_CTX对象cctx对ctx进行配置:cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
  • 创建用于客户端连接的BIO对象:sbio = BIO_new_ssl_connect(ctx);
  • 指定服务端地址端口:BIO_set_conn_hostname(sbio, connect_str);
  • 连接到服务器:BIO_do_connect(sbio)
  • 发送请求:BIO_puts(sbio, “GET / HTTP/1.0\n\n”);
  • 读应答:BIO_read(sbio, tmpbuf, 1024);
  • 关闭连接:BIO_free_all(sbio);
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/doushi/article/details/122796627

智能推荐

便携式iv检测仪解析

在应用场景方面,便携式IV功率测试仪广泛应用于光伏电站的日常运维、光伏组件生产过程中的质量控制以及光伏项目的前期评估等环节。在光伏电站运维中,定期对光伏组件进行IV测试,可以及时发现性能下降或损坏的组件,为电站的运维提供有力支持。首先,从工作原理来看,光伏电站便携式IV功率测试仪通过模拟太阳光照射光伏组件,并测量组件在不同电压下的电流输出,从而绘制出IV曲线。此外,测试仪还可以计算光伏组件的功率输出、转换效率等参数,为用户提供全面的性能评估。

postgresql 索引之 hash_load_categories_hash postgres-程序员宅基地

文章浏览阅读3.6k次。os: ubuntu 16.04postgresql: 9.6.8ip 规划192.168.56.102 node2 postgresqlhelp create indexpostgres=# \h create indexCommand: CREATE INDEXDescription: define a new indexSyntax:CREATE [ UNIQUE ..._load_categories_hash postgres

face++实现人脸识别及人脸相似度对比_face++人脸识别 html5-程序员宅基地

文章浏览阅读4.8k次。使用face++,先获取key和secret下方是人脸识别,还添加了画出人脸轮廓的正方形下方是人脸识别,还添加了画出人脸轮廓的正方形 import requests#网络访问控件 from json import JSONDecoder#互联网数据交换标准格式 import cv2 as cv#图像处理控件 http_url =&amp;amp;amp;quot;https://a..._face++人脸识别 html5

desencrypt java md5_Java实现DES加密与解密,md5加密以及Java实现MD5加密解密类-程序员宅基地

文章浏览阅读322次。很多时候要对秘要进行持久化加密,此时的加密采用md5。采用对称加密的时候就采用DES方法了import java.io.IOException;import java.security.MessageDigest;import java.security.SecureRandom;import javax.crypto.Cipher;import javax.crypto.SecretKey;im..._java desencrypt.encrypt(pass)

BZOJ 2818 欧拉函数,线性筛_线性筛预处理质数表, 并求出欧拉函数, 预处理前缀和即可 bzoj2818boj-程序员宅基地

文章浏览阅读145次。题目链接:https://www.acwing.com/problem/content/description/222/给定整数N,求1<=x,y<=N且GCD(x,y)为素数的数对(x,y)有多少对。GCD(x,y)即求x,y的最大公约数。输入格式输入一个整数N输出格式输出一个整数,表示满足条件的数对数量。数据范围1≤N≤10^7输入样例:4..._线性筛预处理质数表, 并求出欧拉函数, 预处理前缀和即可 bzoj2818boj

使用 arm-linux-androideabi-addr2line 工具定位 libunity.so 崩溃问题-程序员宅基地

文章浏览阅读710次,点赞13次,收藏7次。它的名称有点不同 - aarch64-linux-android-addr2line。尽管该实用程序的名称不包含单词arm,但它成功地解密了arm64-v8下的堆栈跟踪,并通常感知arm64-v8的字符。是 NDK 自带的调试工具,可以用来分析 so 崩溃时输出的的内存地址。之后就是通过 cmd 进入到这个路径。找到了 64 位所需的实用程序。_arm-linux-androideabi

随便推点

Zookeeper集群部署和单机部署

【代码】Zookeeper集群部署和单机部署。

linux ipv6端口转发,linux – TCP / IPv6通过ssh隧道-程序员宅基地

文章浏览阅读871次。是的,这是可能的并且不是太困难,但解决方案非常不理想,因为SSH在TCP上运行并且具有合理的开销.服务器必须在其配置文件sshd_config中:PermitTunnel point-to-point然后,您需要在两台计算机上都是root用户.您使用以下命令连接到服务器:ssh -w any root@server连接后,使用两个系统中的命令ip link来了解在每个系统中创建的tunN设备,并在..._ljnux上ipv6转发配置

linux系统编辑神器 -vim用法大全-程序员宅基地

文章浏览阅读88次。vim编辑器文本编辑器,字处理器ASCIInano,sedvi:VisualInterfacevim:VIiMproved全屏编辑器,模式化编辑器vim模式:编辑模式(命令模式)1.输入模式2.末行模式模式转换:编辑-->输入:i:在当前光标所在字符的前面,转为输入模式;a:在当前光标所在字符的后面,转为输入模式;o:在当前光标所在行的下方,新...

计算机专业游戏本推荐,2018高性价比游戏本推荐_游戏笔记本哪个好-太平洋电脑网...-程序员宅基地

文章浏览阅读245次。【PConline海选导购】晃眼间,秋风又起,让人振奋的开学季又要到来了!虽然说没有学习压力的暑假,在家着实悠哉,但想到回校后可以跟小伙伴们一起各种开黑吃鸡,是不是就感到很兴奋呢?说到“吃鸡”这种吃配置的游戏,就不得不说游戏本了,毕竟普通的笔记本电脑很难给我们带来畅快的游戏体验。而近年来游戏本市场俨然成为了各大厂商的必争之地,而随着开学季的到来,各大厂商更是推出了众多促销活动,下面就让我们一起来看..._计应专业游戏本

codePen按钮样式学习

看到codepen里面有的按钮搞得很炫酷,但其实也不是很难,就学习记录一下。

推荐文章

热门文章

相关标签