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

智能推荐

前端开发之vue-grid-layout的使用和实例-程序员宅基地

文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout

Power Apps-上传附件控件_powerapps点击按钮上传附件-程序员宅基地

文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件

C++ 面向对象(Object-Oriented)的特征 & 构造函数& 析构函数_"object(cnofd[\"ofdrender\"])十条"-程序员宅基地

文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"

修改node_modules源码,并保存,使用patch-package打补丁,git提交代码后,所有人可以用到修改后的_修改 node_modules-程序员宅基地

文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules

【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure-程序员宅基地

文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure

整理5个优秀的微信小程序开源项目_微信小程序开源模板-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板

随便推点

Centos7最简搭建NFS服务器_centos7 搭建nfs server-程序员宅基地

文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server

Springboot整合Mybatis-Plus使用总结(mybatis 坑补充)_mybaitis-plus ruledataobjectattributemapper' and '-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d

EECE 1080C / Programming for ECESummer 2022 Laboratory 4: Global Functions Practice_eece1080c-程序员宅基地

文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c

洛谷p4777 【模板】扩展中国剩余定理-程序员宅基地

文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...

android 退出应用没有走ondestory方法,[Android基础论]为何Activity退出之后,系统没有调用onDestroy方法?...-程序员宅基地

文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy

SylixOS快问快答_select函数 导致堆栈溢出 sylixos-程序员宅基地

文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos

推荐文章

热门文章

相关标签