我使用的是OpenSSL 3.0.9。我正在尝试编写C++代码来实现这一点:
openssl s_client -connect <host>:<port> -tls1_2 -no_ticket -CAfile <file>
当服务器超时结束时,它发送一个“关闭通知”通知,应用程序关闭响应。
但是,在我的代码中,获取消息的唯一方法是BIO_read()
,它会隐藏SSL连接状态。然后使用if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
。如果bio被阻塞,那么BIO_read
将导致应用程序在服务器超时时挂起。我不能在BIO_write()
之前BIO_read()
。
如何在发送请求之前了解连接状态?
如何初始化连接状态?
有没有可能通过阻止生物来做到这一点?
我的代码:
string receive_some_data(BIO* bio)
{
SSL* ssl;
BIO_get_ssl(bio, &ssl);
char buffer[1024];
int len = BIO_read(bio, buffer, sizeof(buffer));
if (len < 0) {
reportAndExit("Error in reading response");
}
else if (len > 0) {
return std::string(buffer, len);
}
else if (BIO_should_retry(bio)) {
return receive_some_data(bio);
}
else if (len == 0 && (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN) ) {
reportAndExit("Get 'close notify' from server");
}
else {
reportAndExit("Empty response");
}
}
...
SSL_CTX* ctx;
BIO* tmpBio = nullptr;
SSL* tmpSsl = nullptr;
auto method_ = TLS_client_method();
if (NULL == method_) reportAndExit("Error initializations of the client method");
tmpBio = BIO_new_ssl_connect(ctx_);
if (NULL == tmpBio) reportAndExit("Error creating a new TLS connection");
BIO_get_ssl(tmpBio, &tmpSsl);
if (tmpSsl == NULL) {
reportAndExit("Get session error");
}
SSL_set_mode(tmpSsl, SSL_MODE_AUTO_RETRY);
setBioConnectParam(tmpBio, host, port);
if (BIO_do_handshake(tmpBio) <= 0) {
reportAndExit("Handshake error");
}
while (true) {
auto connectionStatus = SSL_get_shutdown(tmpSsl);
string msg = "Reset connect, because ";
if (connectionStatus == SSL_SENT_SHUTDOWN) {
msg += "SSL_SENT_SHUTDOWN";
}
if (connectionStatus == SSL_RECEIVED_SHUTDOWN) {
msg += "SSL_RECEIVED_SHUTDOWN";
}
if (connectionStatus > 0) {
// reconnect after shutdown
BIO_reset(tmpBio);
BOOST_LOG_TRIVIAL(info) << msg;
}
std::string request = "GET " + pageName + " HTTP/1.1\r\n";
request += "Host: " + domen + "\r\n";
request += "Connection: keep-alive\r\n";
request += "\r\n";
int n = BIO_write(tmpBio, request.data(), request.size());
BIO_flush(tmpBio);
std::string headers = receive_some_data(tmpBio);
char* end_of_headers = strstr(&headers[0], "\r\n\r\n");
while (end_of_headers == nullptr) {
headers += receive_some_data(tmpBio);
end_of_headers = strstr(&headers[0], "\r\n\r\n");
}
std::string body = std::string(end_of_headers + 4, &headers[headers.size()]);
headers.resize(end_of_headers + 2 - &headers[0]);
size_t content_length = 0;
for (const std::string& line : splitHeaders(headers)) {
if (const char* colon = strchr(line.c_str(), ':')) {
auto header_name = std::string(&line[0], colon);
if (header_name == "Content-Length") {
content_length = std::stoul(colon + 1);
}
}
}
while (body.size() < content_length) {
body += receive_some_data(bio_);
}
std::string result = headers + "\r\n" + body;
std::this_thread::sleep_for(7s);
}
我查看了浏览器在收到有关关闭连接的消息时的工作方式:captured for browser
但是我的程序:captured for my program
我的程序在关闭通知后发送一个请求,但不应该这样做。
1条答案
按热度按时间qlckcl4x1#
在写数据之前,我没有找到比使用
select()
更好的方法。