SSL连接建立过程分析(1)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

1. 应用程序接口
1.1 SSL初始化
SSL_CTX* InitSSL(int server, char *cert, char *key, char *pw)
{
SSL_CTX* ctx;
SSL_METHOD *meth;
int status;
// 算法初始化
// 加载SSL错误信息
SSL_load_error_strings();
// 添加SSL的加密/HASH算法
SSLeay_add_ssl_algorithms();
// 服务器还是客户端
If(server)
meth = SSLv23_server_method();
else
meth = SSLv23_client_method();
// 建立新的SSL上下文
ctx = SSL_CTX_new (meth);
if(!ctx) return NULL;
// 设置证书文件的口令
SSL_CTX_set_default_passwd_cb_userdata(ctx, pw);
//加载本地证书文件
status=SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_ASN1);
if (status <= 0) {
frintf(stderr, "Use cert fail, status=%d\n", status);
goto bad;
}
// 加载私钥文件
if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0) {
fprintf(stderr, "Use private key fail\n");
goto bad;
}
// 检查证书和私钥是否匹配
if (!SSL_CTX_check_private_key(ctx)) {
fprintf("Private key does not match the certificate public key\n");
goto bad;
}
fprintf("Cert and key OK\n");
return ctx;
bad:
SSL_CTX_free (ctx);
return NULL;
}
1.2 建立SSL新连接
服务器:
// 建立SSL
ssl = SSL_new (ctx);
// 将SSL与TCP socket连接
SSL_set_fd (ssl, sd);
//接受新SSL连接
err = SSL_accept (ssl);
客户端:
// 建立SSL
ssl = SSL_new (ctx);
// 将SSL与TCP socket连接
SSL_set_fd (ssl, sd);
// SSL连接
err = SSL_connect (ssl);

服务器的SSL_accept()和客户端的SSL_connect()函数共同完成SSL的握手协商过程。

1.3 SSL通信
和普通的read()/write()调用一样,用下面的函数完成数据的SSL发送和接收,函数输入数据是明文,SSL自动将数据封装进SSL中:
读/接收:SSL_read()
写/发送:SSL_write()
1.4 SSL释放
SSL释放很简单:
SSL_free (ssl);

2. SSL实现分析
以下SSL源代码取自openssl-0.9.7b。

2.1 SSL_load_error_strings
该函数加载错误字符串信息:
void SSL_load_error_strings(void)
{
#ifndef OPENSSL_NO_ERR
ERR_load_crypto_strings();
ERR_load_SSL_strings();
#endif
}
最后将会进入函数:
static void err_load_strings(int lib, ERR_STRING_DATA *str)
{
while (str->error)
{
str->error|=ERR_PACK(lib,0,0);
ERRFN(err_set_item)(str);
str++;
}
}
其中:
#define ERR_PACK(l,f,r) (((((unsigned long)l)&0xffL)*0x1000000)| \
((((unsigned long)f)&0xfffL)*0x1000)| \
((((unsigned long)r)&0xfffL)))
#define ERRFN(a) err_fns->cb_##a
ERRFN(err_set_item)(str)的实际函数实现为:
static ERR_STRING_DATA *int_err_set_item(ERR_STRING_DATA *d)
{
ERR_STRING_DATA *p;
LHASH *hash;
err_fns_check();
hash = ERRFN(err_get)(1);
if (!hash)
return NULL;
CRYPTO_w_lock(CRYPTO_LOCK_ERR);
p = (ERR_STRING_DATA *)lh_insert(hash, d);
CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
return p;
}
Lh_insert()将错误信息插入到一个链表中
如关于加密算法的错误信息:
/* crypto/err/err.c */
static ERR_STRING_DATA ERR_str_functs[]=
……
static ERR_STRING_DATA ERR_str_libraries[]=
……
static ERR_STRING_DATA ERR_str_reasons[]=
……

2.2 SSLeay_add_ssl_algorithms()
这实际是个宏:
#define OpenSSL_add_ssl_algorithms() SSL_library_init()
#define SSLeay_add_ssl_algorithms() SSL_library_init()
实际函数为SSL_library_init(),函数比较简单,就是加载各种加密和HASH算法:
/* ssl/ssl_algs.c */
int SSL_library_init(void)
{
#ifndef OPENSSL_NO_DES
EVP_add_cipher(EVP_des_cbc());
EVP_add_cipher(EVP_des_ede3_cbc());
#endif
#ifndef OPENSSL_NO_IDEA
EVP_add_cipher(EVP_idea_cbc());
#endif
#ifndef OPENSSL_NO_RC4
EVP_add_cipher(EVP_rc4());
#endif
#ifndef OPENSSL_NO_RC2
EVP_add_cipher(EVP_rc2_cbc());
#endif
#ifndef OPENSSL_NO_AES
EVP_add_cipher(EVP_aes_128_cbc());
EVP_add_cipher(EVP_aes_192_cbc());
EVP_add_cipher(EVP_aes_256_cbc());
#endif
#ifndef OPENSSL_NO_MD2
EVP_add_digest(EVP_md2());
#endif
#ifndef OPENSSL_NO_MD5
EVP_add_digest(EVP_md5());
EVP_add_digest_alias(SN_md5,"ssl2-md5");
EVP_add_digest_alias(SN_md5,"ssl3-md5");
#endif
#ifndef OPENSSL_NO_SHA
EVP_add_digest(EVP_sha1()); /* RSA with sha1 */
EVP_add_digest_alias(SN_sha1,"ssl3-sha1");
EVP_add_digest_alias(SN_sha1WithRSAEncryption,SN_sha1WithRSA);
#endif
#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_DSA)
EVP_add_digest(EVP_dss1()); /* DSA with sha1 */
EVP_add_digest_alias(SN_dsaWithSHA1,SN_dsaWithSHA1_2);
EVP_add_digest_alias(SN_dsaWithSHA1,"DSS1");
EVP_add_digest_alias(SN_dsaWithSHA1,"dss1");
#endif
/* If you want support for phased out ciphers, add the following */
#if 0
EVP_add_digest(EVP_sha());
EVP_add_digest(EVP_dss());
#endif
return(1);
}

2.3 SSL23_server_method()
建立服务器端的方法库,这是个通用函数,可动态选择SSL协议。如果想固定协议,可以只用SSLv2_server_method(), SSLv3_server_method() 等函数来初始化,该函数返回一个SSL_METHOD结构:
/* ssl/ssl.h */
/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
typedef struct ssl_method_st
{
int version; // 版本号
int (*ssl_new)(SSL *s); // 建立新SSL
void (*ssl_clear)(SSL *s); // 清除SSL
void (*ssl_free)(SSL *s); // 释放SSL
int (*ssl_accept)(SSL *s); // 服务器接受SSL连接
int (*ssl_connect)(SSL *s); // 客户端的SSL连接
int (*ssl_read)(SSL *s,void *buf,int len); // SSL读
int (*ssl_peek)(SSL *s,void *buf,int len); // SSL查看数据
int (*ssl_write)(SSL *s,const void *buf,int len); // SSL写
int (*ssl_shutdown)(SSL *s); // SSL半关闭
int (*ssl_renegotiate)(SSL *s); // SSL重协商
int (*ssl_renegotiate_check)(SSL *s); // SSL重协商检查
long (*ssl_ctrl)(SSL *s,int cmd,long larg,void *parg); // SSL控制
long (*ssl_ctx_ctrl)(SSL_CTX *ctx,int cmd,long larg,void *parg); //SSL上下文控制
SSL_CIPHER *(*get_cipher_by_char)(const unsigned char *ptr); // 通过名称获取SSL的算法
int (*put_cipher_by_char)(const SSL_CIPHER *cipher,unsigned char *ptr);
int (*ssl_pending)(SSL *s);
int (*num_ciphers)(void); // 算法数
SSL_CIPHER *(*get_cipher)(unsigned ncipher); // 获取算法
struct ssl_method_st *(*get_ssl_method)(int version);
long (*get_timeout)(void); // 超时
struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */ // SSL3加密
int (*ssl_version)(); // SSL版本
long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)()); // SSL控制回调函数
long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)()); //SSL上下文控制回调函数
} SSL_METHOD;

/* ssl/s23_srvr.c */
SSL_METHOD *SSLv23_server_method(void)
{
static int init=1;
// 静态量,每个进程只初始化一次
static SSL_METHOD SSLv23_server_data;
if (init)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);
if (init)
{
// ssl23的基本方法结构
memcpy((char *)&SSLv23_server_data,
(char *)sslv23_base_method(),sizeof(SSL_METHOD));
// 服务器,所以要定义accept方法
SSLv23_server_data.ssl_accept=ssl23_accept;
// 根据SSL的版本设置SSL的具体方法函数
SSLv23_server_data.get_ssl_method=ssl23_get_server_method;
init=0;
}
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);
}
return(&SSLv23_server_data);
}

static SSL_METHOD *ssl23_get_server_method(int ver)
{
#ifndef OPENSSL_NO_SSL2
if (ver == SSL2_VERSION)
return(SSLv2_server_method());
#endif
if (ver == SSL3_VERSION)
return(SSLv3_server_method());
else if (ver == TLS1_VERSION)
return(TLSv1_server_method());
// 随着TLS1.1(RFC4346)的推出,估计不久将出现TLSv1_1_server_method()
else
return(NULL);
}
// SSL23的方法基本数据定义
/* ssl/s23_lib.c */
SSL_METHOD *sslv23_base_method(void)
{
return(&SSLv23_data);
}
static SSL_METHOD SSLv23_data= {
TLS1_VERSION,
tls1_new,
tls1_clear,
tls1_free,
ssl_undefined_function,
ssl_undefined_function,
ssl23_read,
ssl23_peek,
ssl23_write,
ssl_undefined_function,
ssl_undefined_function,
ssl_ok,
ssl3_ctrl,
ssl3_ctx_ctrl,
ssl23_get_cipher_by_char,
ssl23_put_cipher_by_char,
ssl_undefined_function,
ssl23_num_ciphers,
ssl23_get_cipher,
ssl_bad_method,
ssl23_default_timeout,
&ssl3_undef_enc_method,
ssl_undefined_function,
ssl3_callback_ctrl,
ssl3_ctx_callback_ctrl,
};
以SSL3的服务器方法函数为例,其他方法类似:
/* ssl/s3_srvr.c */
SSL_METHOD *SSLv3_server_method(void)
{
static int init=1;
static SSL_METHOD SSLv3_server_data;
// 只初始化一次
if (init)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);
if (init)
{
// ssl3的基本方法结构
memcpy((char *)&SSLv3_server_data,(char *)sslv3_base_method(),
sizeof(SSL_METHOD));
// ssl3的接受方法
SSLv3_server_data.ssl_accept=ssl3_accept;
// ssl3获取服务器的方法函数
SSLv3_server_data.get_ssl_method=ssl3_get_server_method;
init=0;
}

CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);
}
return(&SSLv3_server_data);
}
// SSL3的方法基本数据定义
/* ssl/s3_lib.c */
static SSL_METHOD SSLv3_data= {
SSL3_VERSION,
ssl3_new,
ssl3_clear,
ssl3_free,
ssl_undefined_function,
ssl_undefined_function,
ssl3_read,
ssl3_peek,
ssl3_write,
ssl3_shutdown,
ssl3_renegotiate,
ssl3_renegotiate_check,
ssl3_ctrl,
ssl3_ctx_ctrl,
ssl3_get_cipher_by_char,
ssl3_put_cipher_by_char,
ssl3_pending,
ssl3_num_ciphers,
ssl3_get_cipher,
ssl_bad_method,
ssl3_default_timeout,
&SSLv3_enc_data,
ssl_undefined_function,
ssl3_callback_ctrl,
ssl3_ctx_callback_ctrl,
};

2.4 SSL23_client_method()

和服务器端的其实是相同的,只是不定义结构中的ssl_accept而是定义ssl_connnect:
SSL_METHOD *SSLv23_client_method(void)
{
static int init=1;
static SSL_METHOD SSLv23_client_data;
if (init)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);
if (init)
{
memcpy((char *)&SSLv23_client_data,
(char *)sslv23_base_method(),sizeof(SSL_METHOD));
SSLv23_client_data.ssl_connect=ssl23_connect;
SSLv23_client_data.get_ssl_method=ssl23_get_client_method;
init=0;
}
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);
}
return(&SSLv23_client_data);
}

2.5 SSL_CTX_new ()
该函数根据SSL方法获取一个SSL上下文结构,该结构定义为:
/* ssl/ssl.h */
struct ssl_ctx_st
{
SSL_METHOD *method;
STACK_OF(SSL_CIPHER) *cipher_list;
/* same as above but sorted for lookup */
STACK_OF(SSL_CIPHER) *cipher_list_by_id;
struct x509_store_st /* X509_STORE */ *cert_store;
struct lhash_st /* LHASH */ *sessions; /* a set of SSL_SESSIONs */
/* Most session-ids that will be cached, default is
* SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited. */
unsigned long session_cache_size;
struct ssl_session_st *session_cache_head;
struct ssl_session_st *session_cache_tail;
/* This can have one of 2 values, ored together,
* SSL_SESS_CACHE_CLIENT,
* SSL_SESS_CACHE_SERVER,
* Default is SSL_SESSION_CACHE_SERVER, which means only
* SSL_accept which cache SSL_SESSIONS. */
int session_cache_mode;
/* If timeout is not 0, it is the default timeout value set
* when SSL_new() is called. This has been put in to make
* life easier to set things up */
long session_timeout;
/* If this callback is not null, it will be called each
* time a session id is added to the cache. If this function
* returns 1, it means that the callback will do a
* SSL_SESSION_free() when it has finished using it. Otherwise,
* on 0, it means the callback has finished with it.
* If remove_session_cb is not null, it will be called when
* a session-id is removed from the cache. After the call,
* OpenSSL will SSL_SESSION_free() it. */
int (*new_session_cb)(struct ssl_st *ssl,SSL_SESSION *sess);
void (*remove_session_cb)(struct ssl_ctx_st *ctx,SSL_SESSION *sess);
SSL_SESSION *(*get_session_cb)(struct ssl_st *ssl,
unsigned char *data,int len,int *copy);
struct
{
int sess_connect; /* SSL new conn - started */
int sess_connect_renegotiate;/* SSL reneg - requested */
int sess_connect_good; /* SSL new conne/reneg - finished */
int sess_accept; /* SSL new accept - started */
int sess_accept_renegotiate;/* SSL reneg - requested */
int sess_accept_good; /* SSL accept/reneg - finished */
int sess_miss; /* session lookup misses */
int sess_timeout; /* reuse attempt on timeouted session */
int sess_cache_full; /* session removed due to full cache */
int sess_hit; /* session reuse actually done */
int sess_cb_hit; /* session-id that was not
* in the cache was
* passed back via the callback. This
* indicates that the application is
* supplying session-id's from other
* processes - spooky :-) */
} stats;
int references;
/* if defined, these override the X509_verify_cert() calls */
int (*app_verify_callback)(X509_STORE_CTX *, void *);
void *app_verify_arg;
/* before OpenSSL 0.9.7, 'app_verify_arg' was ignored
* ('app_verify_callback' was called with just one argument) */
/* Default password callback. */
pem_password_cb *default_passwd_callback;
/* Default password callback user data. */
void *default_passwd_callback_userdata;
/* get client cert callback */
int (*client_cert_cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
CRYPTO_EX_DATA ex_data;
const EVP_MD *rsa_md5;/* For SSLv2 - name is 'ssl2-md5' */
const EVP_MD *md5; /* For SSLv3/TLSv1 'ssl3-md5' */
const EVP_MD *sha1; /* For SSLv3/TLSv1 'ssl3->sha1' */
STACK_OF(X509) *extra_certs;
STACK_OF(SSL_COMP) *comp_methods; /* stack of SSL_COMP, SSLv3/TLSv1 */

/* Default values used when no per-SSL value is defined follow */
void (*info_callback)(const SSL *ssl,int type,int val); /* used if SSL's info_callback is NULL */
/* what we put in client cert requests */
STACK_OF(X509_NAME) *client_CA;

/* Default values to use in SSL structures follow (these are copied by SSL_new) */
unsigned long options;
unsigned long mode;
long max_cert_list;
struct cert_st /* CERT */ *cert;
int read_ahead;
/* callback that allows applications to peek at protocol messages */
void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
void *msg_callback_arg;
int verify_mode;
int verify_depth;
unsigned int sid_ctx_length;
unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
int (*default_verify_callback)(int ok,X509_STORE_CTX *ctx); /* called 'verify_callback' in the SSL */
/* Default generate session ID callback. */
GEN_SESSION_CB generate_session_id;
int purpose; /* Purpose setting */
int trust; /* Trust setting */
int quiet_shutdown;
};

typedef struct ssl_ctx_st SSL_CTX;
/* ssl/ssl_lib.h */
SSL_CTX *SSL_CTX_new(SSL_METHOD *meth)
{
SSL_CTX *ret=NULL;

if (meth == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_NULL_SSL_METHOD_PASSED);
return(NULL);
}
if (SSL_get_ex_data_X509_STORE_CTX_idx() < 0)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_X509_VERIFICATION_SETUP_PROBLEMS);
goto err;
}
// 分配上下文的内存空间
ret=(SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));
if (ret == NULL)
goto err;
memset(ret,0,sizeof(SSL_CTX));

// 初始化上下文的结构参数
ret->method=meth;
ret->cert_store=NULL;
ret->session_cache_mode=SSL_SESS_CACHE_SERVER;
ret->session_cache_size=SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
ret->session_cache_head=NULL;
ret->session_cache_tail=NULL;
/* We take the system default */
ret->session_timeout=meth->get_timeout();
ret->new_session_cb=0;
ret->remove_session_cb=0;
ret->get_session_cb=0;
ret->generate_session_id=0;
memset((char *)&ret->stats,0,sizeof(ret->stats));
ret->references=1;
ret->quiet_shutdown=0;
/* ret->cipher=NULL;*/
/* ret->s2->challenge=NULL;
ret->master_key=NULL;
ret->key_arg=NULL;
ret->s2->conn_id=NULL; */
ret->info_callback=NULL;
ret->app_verify_callback=0;
ret->app_verify_arg=NULL;
ret->max_cert_list=SSL_MAX_CERT_LIST_DEFAULT;
ret->read_ahead=0;
ret->msg_callback=0;
ret->msg_callback_arg=NULL;
ret->verify_mode=SSL_VERIFY_NONE;
ret->verify_depth=-1; /* Don't impose a limit (but x509_lu.c does) */
ret->sid_ctx_length=0;
ret->default_verify_callback=NULL;
if ((ret->cert=ssl_cert_new()) == NULL)
goto err;
ret->default_passwd_callback=0;
ret->default_passwd_callback_userdata=NULL;
ret->client_cert_cb=0;
ret->sessions=lh_new(LHASH_HASH_FN(SSL_SESSION_hash),
LHASH_COMP_FN(SSL_SESSION_cmp));
if (ret->sessions == NULL) goto err;
ret->cert_store=X509_STORE_new();
if (ret->cert_store == NULL) goto err;

// 建立加密算法链表
ssl_create_cipher_list(ret->method,
&ret->cipher_list,&ret->cipher_list_by_id,
SSL_DEFAULT_CIPHER_LIST);
if (ret->cipher_list == NULL
|| sk_SSL_CIPHER_num(ret->cipher_list) <= 0)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_LIBRARY_HAS_NO_CIPHERS);
goto err2;
}

// 定义上下文结构中HASH算法
if ((ret->rsa_md5=EVP_get_digestbyname("ssl2-md5")) == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES);
goto err2;
}
if ((ret->md5=EVP_get_digestbyname("ssl3-md5")) == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES);
goto err2;
}
if ((ret->sha1=EVP_get_digestbyname("ssl3-sha1")) == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES);
goto err2;
}
if ((ret->client_CA=sk_X509_NAME_new_null()) == NULL)
goto err;

CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_CTX, ret, &ret->ex_data);
ret->extra_certs=NULL;
// 压缩算法
ret->comp_methods=SSL_COMP_get_compression_methods();
return(ret);
err:
SSLerr(SSL_F_SSL_CTX_NEW,ERR_R_MALLOC_FAILURE);
err2:
if (ret != NULL) SSL_CTX_free(ret);
return(NULL);
}

...待续...

发表于: 2006-10-23,修改于: 2006-10-23 08:54,已浏览11014次,有评论12条 推荐 投诉
网友: 本站网友 时间:2006-10-23 13:00:32 IP地址:61.51.122.★


兄弟,你是搞嵌入史的吧,是哪个公司的?/


网友: yfydz 时间:2006-10-23 13:58:39 IP地址:218.247.216.★


不要问这种无聊问题


网友: 本站网友 时间:2006-12-22 09:07:59 IP地址:211.94.130.★


使用GPL发布还不允许用于商业用途,哥哥你好有趣哦。


网友: yfydz 时间:2006-12-22 11:45:10 IP地址:218.247.216.★


这是对GPL的补充,GPL只是我的一个子集而已


网友: 屎密斯 时间:2007-05-10 09:46:07 IP地址:210.21.51.★


顶,太有用了


网友: 本站网友 时间:2007-06-14 16:58:48 IP地址:124.42.1.★


请问您知道怎么清除IE的SSL状态吗 ?望不吝赐教QQ:17593789


网友: chengwei1985 时间:2008-04-21 09:41:15 IP地址:221.222.167.★


能给个SSL的实现过程的图吗?看代码之前在脑中建立个模型比较容易领会,谢谢!!希望高手能不吝赐教!!在下挣在学习SSL过程中,看到大哥的BLOG,有如沙漠中看到了绿洲!!!太感谢了!


网友: 本站网友 时间:2008-05-16 17:23:14 IP地址:124.79.227.★


楼主你好,我用了SSL_connect和SSL_accept进行handshake。但是这个handshake没有对client分配虚拟ip,请问楼主,如何才能做到给client端分配虚拟ip


网友: 本站网友 时间:2008-05-16 17:52:19 IP地址:124.79.227.★


还有,在这个handshake里好像没有检查用户名和用户密码的过程,请问楼主,如何实现检查用户名和密码


网友: yfydz 时间:2008-05-17 22:56:14 IP地址:58.31.250.★


本来就没有,最多是认证一下证书


网友: 本站网友 时间:2009-06-24 09:46:47 IP地址:219.133.0.★


请问能不能在SSL协议的通信阶段不依赖socket发送接收数据包?

也就是说先利用socket建立连接通道,然后我以我自己喜欢的方法将数据包发送过去?


网友: 本站网友 时间:2010-05-28 15:36:12 IP地址:124.162.118.★


你好,我正在标准boa上加入ssl功能。但在SSL_accept时总是返回-1,是怎么回事呢?


给我留言
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值