我正在尝试使用SSL_read从Openssl链接套接字中读取数据。我在客户机模式下执行Openssl操作,该模式从真实的服务器发送命令和接收数据。我使用了两个线程,其中一个线程处理所有的Openssl操作,如连接、写入和关闭。我在一个单独的线程中执行SSL_read。当我发出一次SSL_read命令时,我能够正确地读取数据。
但当我尝试执行多个连接、写入、关闭序列时,我遇到了问题。理想情况下,我应该终止执行SSL_read以响应close的线程。这是因为对于下一次连接,我们将获得一个新的ssl指针,因此我们不想在旧的ssl指针上执行读取。但问题是,当我做SSL_read时,我被卡住了,直到SSL缓冲区中有可用的数据。它在SSL指针上被阻塞,即使我已经关闭了另一个线程中的SSL连接。
while(1) {
memset(sbuf, 0, sizeof(uint8_t) * TLS_READ_RCVBUF_MAX_LEN);
read_data_len = SSL_read(con, sbuf, TLS_READ_RCVBUF_MAX_LEN);
switch (SSL_get_error(con, read)) {
case SSL_ERROR_NONE:
.
.
.
}我尝试了这个问题的所有可能的解决方案,但都不起作用。大多数情况下,我尝试指示让我知道SSL缓冲区中可能有数据,但没有一个返回正确的指示。
我试过了:
下面是我使用select阻塞套接字的示例。但是select总是返回零。
while(1) {
// The use of Select here is to timeout
// while waiting for data to read on SSL.
// The timeout is set to 1 second
i = select(width, &readfds, NULL,
NULL, &tv);
if (i < 0) {
// Select Error. Take appropriate action for this error
}
// Check if there is data to be read
if (i > 0) {
if (FD_ISSET(SSL_get_fd(con), &readfds)) {
// TODO: We have data in the SSL buffer. But are we
// sure that the data is from read buffer? If not,
// SSL_read can be stuck indefinitely.
// Maybe we can do SSL_read(con, sbuf, 0) followed
// by SSL_pending to find out?
memset(sbuf, 0, sizeof(uint8_t) * TLS_READ_RCVBUF_MAX_LEN);
read_data_len = SSL_read(con, sbuf, TLS_READ_RCVBUF_MAX_LEN);
error = SSL_get_error(con, read_data_len);
switch (error) {
.
.
}因此,正如您所看到的,我已经尝试了许多方法来使执行SSL_read的线程终止以响应close,但是我没有让它像我预期的那样工作。有人能让SSL_read正常工作吗?非阻塞套接字是我的问题的唯一解决方案吗?对于阻塞套接字,如果你永远得不到命令的响应,你如何解决退出SSL_read的问题?你能给出一个非阻塞套接字读的工作解决方案的例子吗?
发布于 2017-10-03 02:16:36
我可以向您介绍一个使用SSL的非阻塞客户端套接字的工作示例... https://github.com/darrenjs/openssl_examples
它将非阻塞套接字与标准linux IO结合使用(基于轮询事件循环)。从套接字读取原始数据,然后将其送入SSL内存BIO,然后执行解密。
我使用的方法是单线程的。单个线程执行连接、写入和读取。这意味着当一个线程关闭一个套接字,而另一个线程试图使用该套接字时,不会出现任何问题。此外,正如SSL FAQ所指出的,“一个SSL连接不能由多个线程并发使用”(https://www.openssl.org/docs/faq.html#PROG1),因此单线程方法避免了并发SSL写和读的问题。
单线程方法的挑战在于,您需要创建某种类型的同步队列和信号发送机制,用于提交和保存等待出站的数据(例如,您希望从客户端发送到服务器的命令),并让套接字事件循环检测何时有等待写入的数据并将其从队列中拉出等等。为此,我将查看标准的std::list、std::mutex等,以及用于发送事件循环的pipe2或eventfd。
发布于 2020-08-12 04:22:09
OpenSSL调用recv(),该函数反过来遵守套接字的超时时间,默认情况下,超时时间是无限的。您可以按如下方式更改超时:
void socket_timeout_receive_set(SOCKET handle, dword milliseconds)
{
if(handle==SOCKET_HANDLE_NULL)
return;
struct timeval tv = { long(milliseconds / 1000), (milliseconds % 1000) * 1000 };
setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
}不幸的是,ssl_error_get()返回SSL_ERROR_SYSCALL,在其他情况下也会返回,因此很难确定它是否超时。但此函数将帮助您确定连接是否丢失:
bool socket_dropped(SOCKET handle)
{
// Special thanks: "Detecting and terminating aborted TCP/IP connections" by Vinayak Gadkari
if(handle==SOCKET_HANDLE_NULL)
return true;
// create a socket set containing just this socket
fd_set socket_set;
FD_ZERO(&socket_set);
FD_SET(handle, &socket_set);
// if the connection is unreadable, it is not dropped (strange but true)
static struct timeval timeout = { 0, 0 };
int count = select(0, &socket_set, NULL, NULL, &timeout);
if(count <= 0) {
// problem: count==0 on a connection that was cut off ungracefully, presumably by a busy router
// for connections that are open for a long time but may not talk much, call keepalive_set()
return false;
}
if(!FD_ISSET(handle, &socket_set)) // creates a dependency on __WSAFDIsSet()
return false;
// peek at the next character
// recv() returns 0 if the connection was dropped
char dummy;
count = recv(handle, &dummy, 1, MSG_PEEK);
if(count > 0)
return false;
if(count==0)
return true;
return sec==WSAECONNRESET || sec==WSAECONNABORTED || sec==WSAENETRESET || sec==WSAEINVAL;
}https://stackoverflow.com/questions/46517875
复制相似问题