[英]Non-blocking BIO and hang after BIO_do_connect
我正在使用openssl在C中编写一个小小的IRC bot来启动安全套接字。 它不是编写得最精美的机器人,但它主要是为了了解openssl API的工作原理。 目前我有以下代码:
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
int main() {
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
BIO *bio;
SSL_CTX * ctx = SSL_CTX_new(SSLv23_client_method());
SSL * ssl;
SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs/");
bio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(bio, & ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_nbio(bio, 1);
BIO_set_conn_hostname(bio, "irc.freenode.net:6697");
BIO_do_connect(bio);
if(SSL_get_verify_result(ssl) != X509_V_OK) {
printf("error\n");
}
char irc1[] = "NICK bartender\r\n";
char irc2[] = "USER bartender * * :serve(&drinks);\r\n";
BIO_write(bio, irc1, strlen(irc1));
BIO_write(bio, irc2, strlen(irc2));
fd_set read_set;
int sock = BIO_get_fd(bio, NULL);
while(1) {
FD_ZERO(&read_set);
FD_SET(sock, &read_set);
struct timeval timeout = { 0, 0 };
select(sock+1, &read_set, NULL, NULL, &timeout);
if(FD_ISSET(sock, &read_set)) {
char buf[21];
size_t x = BIO_read(bio, buf, 20);
if(x == 0) {
continue;
} else if(x == -1){
int code = ERR_get_error();
if(code == 0) {
continue;
}
printf("(%d)%s\n", code, ERR_error_string(code, NULL));
} else {
buf[x] = '\0';
printf("%s", buf);
}
}
}
}
每当我编译并运行此代码时,它只会挂起并打印任何内容。 但是,如果我删除第20行(当前将套接字置于非阻塞模式),它可以正常工作。 为什么将套接字置于非阻塞模式会导致此行为? 谢谢你,祝你有个美好的一天!
每当我运行此代码时,它只会挂起并打印任何内容。 但是,如果我删除第20行(当前将套接字置于非阻塞模式),它可以正常工作。
BIO_do_connect
立即以非阻塞模式返回。 你应该循环BIO_should_retry
。 以下是手册页有关BIO_do_connect
:
BIO_do_connect()尝试连接提供的BIO。 如果连接成功建立,则返回1。 如果无法建立连接,则返回零值或负值,应将呼叫BIO_should_retry()用于非阻塞连接BIO以确定是否应重试该呼叫。
为什么将套接字置于非阻塞模式会导致此行为?
对BIO_do_connect
的调用立即返回; socket / bio可能尚未准备好数据(尚未)。
循环BIO_do_connect
/ BIO_should_retry
的替代方法是等待底层文件描述符。 它是OpenSSL在ocsp
子命令中使用的技术(源代码可以在<openssl src>/apps/ocsp.c
):
if (req_timeout != -1)
BIO_set_nbio(cbio, 1);
rv = BIO_do_connect(cbio);
if ((rv <= 0) && ((req_timeout == -1) || !BIO_should_retry(cbio))) {
BIO_puts(err, "Error connecting BIO\n");
return NULL;
}
if (BIO_get_fd(cbio, &fd) < 0) {
BIO_puts(bio_err, "Can't get connection fd\n");
goto err;
}
if (req_timeout != -1 && rv <= 0) {
FD_ZERO(&confds);
openssl_fdset(fd, &confds);
tv.tv_usec = 0;
tv.tv_sec = req_timeout;
rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv);
if (rv == 0) {
BIO_puts(err, "Timeout on connect\n");
return NULL;
}
}
另请参阅OpenSSL Users邮件列表中的非阻塞BIO和BIO_do_connect问题 。 Stack Overflow上也有一些点击 ,但我不确定哪个最适合这个问题:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.