我有一个 TCP 网络库,它实现了一堆协议(redis、http 等),它们是使用 BSD 套接字实现的。
许多代码使用 select() 和其他用于 BSD 套接字的函数。我是否正确假设这不适用于 SSL 套接字?还是他们会按原样工作?
我只是想知道 SSL 和 BSD 套接字是否如此不同以至于它们需要完全不同的实现方法。
假设您指的是 OpenSSL,它位于套接字的顶部,不会取代它。因此,所有直接套接字操作,如select()
,仍然有效。但是,不同之处在于 OpenSSL 为您处理读取和写入,因此您可以替换recv()
为ssl_read()
和send()
,ssl_write()
但您仍然可以(并且在某些情况下需要)select()
直接使用。但是,您不能随时调用它,您必须等到 OpenSSL 告诉您调用它。因此,例如,如果您有一个读取循环select()
首先调用,然后recv()
仅在select()
报告可读性时调用,您将不得不交换该逻辑。先调用ssl_read()
,再调用select
() 仅当ssl_read()
返回SSL_ERROR_WANT_READ
或时SSL_ERROR_WANT_WRITE
(注意ssl_read()
可以在内部进行写操作,也ssl_write()
可以在内部进行读操作)。
想到的一件事是,您不应该在运行 ssl 连接的 fd 上进行选择。那是因为它可能会说你可以在 ssl_read 阻塞时读取它。例如,这是由于 SSL 进行密钥重新协商而不是应用程序数据变得可用而导致的。这就是陷阱之一。
这可能会迟到,但可能会为未来的用户提供很好的参考。在 url http://developerweb.net/viewtopic.php?id=6824上使用 select() 函数有一个很好的线程。引用的一个例子如下
int sslsock_handle_nbio (ssl, ret, totv)
void *ssl; /* -> the SSL info */
int ret; /* the SSL I/O function return value */
struct timeval *totv; /* -> timeout info, or NULL */
{
int sfd, i;
fd_set rset, wset;
sfd = SSL_get_fd (ssl);
i = SSL_get_error (ssl, ret);
if (i == SSL_ERROR_WANT_READ) {
do {
FD_ZERO (&rset);
FD_SET (sfd, &rset);
i = select (sfd + 1, &rset, NULL, NULL, totv);
} while ((i < 0) && (errno == EINTR));
if (i == 0) {
/* the select() timed out */
ret = -2;
errno = ETIMEDOUT;
} else {
/* either an error, or it's readable */
ret = i;
}
} else if (i == SSL_ERROR_WANT_WRITE) {
do {
FD_ZERO (&wset);
FD_SET (sfd, &wset);
i = select (sfd + 1, NULL, &wset, NULL, totv);
} while ((i < 0) && (errno == EINTR));
if (i == 0) {
/* the select() timed out */
ret = -2;
errno = ETIMEDOUT;
} else {
/* either an error, or it's writable */
ret = i;
}
}
/* else, leave "ret" alone, and return it as-is */
return (ret);
}
这仅在执行SSL_read()
or之后SSL_write()
。