Sockets OpenSSL阻塞套接字SSL_读取块
我在OpenSSL连接中使用阻塞套接字。SSL_读取块有时会持续几秒钟。在服务器中,BIO_写入用于发送可变缓冲区大小的数据。在客户端中,第一次SSL_读取以获取缓冲区大小成功,但在SSL_读取后几秒钟内获取缓冲区数据块(此问题在2到3分钟后模拟),即使数据发送成功。我等待poll()调用客户端读取函数。如何纠正阻塞套接字中的这些问题 服务器代码Sockets OpenSSL阻塞套接字SSL_读取块,sockets,ssl,openssl,blocking,Sockets,Ssl,Openssl,Blocking,我在OpenSSL连接中使用阻塞套接字。SSL_读取块有时会持续几秒钟。在服务器中,BIO_写入用于发送可变缓冲区大小的数据。在客户端中,第一次SSL_读取以获取缓冲区大小成功,但在SSL_读取后几秒钟内获取缓冲区数据块(此问题在2到3分钟后模拟),即使数据发送成功。我等待poll()调用客户端读取函数。如何纠正阻塞套接字中的这些问题 服务器代码 void process_and_send() { // sending variable size buffer each time
void process_and_send() {
// sending variable size buffer each time
// sbuf - first 4 bytes contains sbuf size information
send_data(sbuf, sbufSize);
}
void send_data(void *sbuf, int pending_len) {
while(pending_len > 0) {
result = BIO_write(bio, sbuf, pending_len);
if(result == 0) {
attempts = 0;
LOG_D("%s", log_str(SSL_CONN_CLOSE));
SSL_FN_TRACE("connection closed\n");
break;
}
else if(result < 0) {
LOG_I("%s", log_str(SSL_WRITE_FAIL));
SSL_FN_TRACE("BIO_write fail\n");
if(errno == EINTR) {
continue;
}
if(errno == EAGAIN) {
attempts++;
continue;
}
if(errno == EWOULDBLOCK) {
attempts++;
continue;
}
break;
}
else {
BIO_flush(bio);
pending_len -= result;
sbuf += result;
}
}
}
void进程和发送(){
//每次发送可变大小的缓冲区
//sbuf-前4个字节包含sbuf大小信息
发送_数据(sbuf、sbufSize);
}
void发送数据(void*sbuf,int待定){
同时(挂起时间>0){
结果=BIO_写入(BIO、sbuf、待处理_len);
如果(结果==0){
尝试次数=0;
LOG_D(“%s”,LOG_str(SSL_CONN_CLOSE));
SSL_FN_跟踪(“连接已关闭”);
打破
}
否则如果(结果<0){
日志I(“%s”,日志str(SSL写入失败));
SSL_FN_跟踪(“BIO_写入失败\n”);
如果(errno==EINTR){
继续;
}
if(errno==EAGAIN){
尝试++;
继续;
}
if(errno==ewoldblock){
尝试++;
继续;
}
打破
}
否则{
生物冲洗(BIO);
待定项-=结果;
sbuf+=结果;
}
}
}
客户端代码
// wait on poll() and call receive_and_process
void receive_and_process() {
int rbufSize = 0;
// get the size of data to read
receive_data((void *)&rbufSize, sizeof(Int));
// this call blocks for few seconds
receive_data(rbuf, rbufSize);
}
void receive_data(void *rbuf, int pending_len) {
while(pending_len > 0) {
result = SSL_read(ssl, rbuf, pending_len);
if(result == 0) {
LOG_D("%s", log_str(SSL_CONN_CLOSE));
SSL_FN_TRACE("connection closed\n");
return NULL;
}
else if(result < 0) {
if(errno == ETIMEDOUT) {
SSL_FN_ERROR("SSL read timeout: \n");
continue;
}
if(errno == EINTR) {
continue;
}
if(errno == EAGAIN) {
continue;
}
if(errno == EWOULDBLOCK) {
continue;
}
SSL_FN_ERROR("SSL read fail error no: %s\n",
ERR_reason_error_string(ERR_get_error()));
LOG_I("%s", log_str(SSL_READ_FAIL));
return NULL;
}
pending_len -= result;
rbuf += result;
FN_ERROR("after read full data pending len %d\n", pending_len);
}
}
//等待poll()并调用receive\u和\u进程
无效接收_和_过程(){
int rbufSize=0;
//获取要读取的数据的大小
接收_数据((void*)&rbufSize,sizeof(Int));
//此呼叫中断几秒钟
接收_数据(rbuf、rbufSize);
}
无效接收数据(无效*rbuf,整数待定){
同时(挂起时间>0){
结果=SSL读取(SSL、rbuf、待定项);
如果(结果==0){
LOG_D(“%s”,LOG_str(SSL_CONN_CLOSE));
SSL_FN_跟踪(“连接已关闭”);
返回NULL;
}
否则如果(结果<0){
if(errno==ETIMEDOUT){
SSL_FN_错误(“SSL读取超时:\n”);
继续;
}
如果(errno==EINTR){
继续;
}
if(errno==EAGAIN){
继续;
}
if(errno==ewoldblock){
继续;
}
SSL\u FN\u错误(“SSL读取失败错误号:%s\n”,
ERR_reason_error_string(ERR_get_error());
日志I(“%s”,日志str(SSL读取失败));
返回NULL;
}
待定项-=结果;
rbuf+=结果;
FN\u错误(“读取完整数据后,挂起长度%d\n”,挂起长度);
}
}
首先,您的客户机代码无法按照所示进行编译,因为receive_data()
具有void
返回类型,因此return NULL
是编译器错误。此外,不能在void*
指针上使用+=
运算符,这也是一个编译器错误
除此之外,如果SSL\u read()
返回<0,则需要使用SSL\u get\u error()
而不是errno
来找出失败的原因。不要使用errno
,除非SSL\u get\u error()
返回SSL\u error\u SYSCALL
。如果SSL\u get\u error()
返回SSL\u error\u SSL
,请改用ERR\u get\u error()
和相关函数。另外,请确保您正在处理SSL\u错误\u希望\u读取
和SSL\u错误\u希望\u写入
错误
此外,在发送多字节整数时,如果要跨机器边界发送,则必须处理endian问题。最好使用诸如htonl()
和ntohl()
之类的函数通过网络字节顺序发送整数
尝试类似以下内容:
服务器:
void process_and_send() {
// sending variable size buffer each time
// sbuf - DO NOT store the size information in the first 4 bytes!
// handle the size separately...
int32_t size = htonl(sbufSize);
if (send_data(&size, sizeof(size)))
send_data(sbuf, sbufSize);
}
bool send_data(void *sbuf, int pending_len) {
unsigned char *pbuf = (unsigned char *) sbuf;
while (pending_len > 0) {
result = BIO_write(bio, pbuf, pending_len);
if (result > 0) {
BIO_flush(bio);
pbuf += result;
pending_len -= result;
}
else if (result == 0) {
attempts = 0;
LOG_D("%s", log_str(SSL_CONN_CLOSE));
SSL_FN_TRACE("connection closed\n");
return false;
}
else if (!BIO_should_retry(bio)) {
LOG_I("%s", log_str(SSL_WRITE_FAIL));
SSL_FN_TRACE("BIO_write fail\n");
return false;
}
else {
++attempts;
}
}
return true;
}
客户:
// wait on poll() and call receive_and_process
void receive_and_process() {
int32_t rbufSize = 0;
// get the size of data to read
if (receive_data(&rbufSize, sizeof(rbufSize))) {
rbufSize = ntohl(rbufSize);
// TODO: make sure rbuf is at least rbufSize in size...
receive_data(rbuf, rbufSize);
}
}
bool receive_data(void *rbuf, int pending_len) {
unsigned char *pbuf = (unsigned char *) rbuf;
while (pending_len > 0) {
result = SSL_read(ssl, pbuf, pending_len);
if (result > 0) {
pbuf += result;
pending_len -= result;
FN_ERROR("after read full data pending len %d\n", pending_len);
}
else {
result = SSL_get_error();
if (result == SSL_ERROR_ZERO_RETURN) {
LOG_D("%s", log_str(SSL_CONN_CLOSE));
SSL_FN_TRACE("connection closed\n");
}
else {
if (result == SSL_ERROR_WANT_READ) {
// TODO: use select() to wait for the socket to be readable before trying again...
continue;
}
else if (result == SSL_ERROR_WANT_WRITE) {
// TODO: use select() to wait for the socket to be writable before trying again...
continue;
}
else if (result == SSL_ERROR_SYSCALL) {
if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
continue;
}
if (errno == ETIMEDOUT) {
SSL_FN_ERROR("SSL read timeout: \n");
continue;
}
SSL_FN_ERROR("SSL read fail error no: %d\n", errno);
}
else if (result == SSL_ERROR_SSL) {
SSL_FN_ERROR("SSL read fail error no: %s\n",
ERR_reason_error_string(ERR_get_error()));
}
else {
SSL_FN_ERROR("SSL read fail error no: %d\n", result);
}
LOG_I("%s", log_str(SSL_READ_FAIL));
}
return false;
}
}
return true;
}
我同意。通过阻塞插座块进行I/O。你的问题?为什么它会阻塞?即使数据发送成功。谢谢雷米。正如你提到的,我对代码做了更改。但SSL_读取块有时会超过2000毫秒,即使数据发送成功。@ArunrajShanmugam那么您可能没有以正确的格式读取数据。如果请求的字节数超过实际发送的字节数,例如如果误读数据大小,则阻塞读取将被阻塞。仔细检查您的读取逻辑OK I will debug,当服务器从多个客户端接收数据时,经常会模拟此问题。它看起来像读取调用,以获取数据的大小
receive_data(&rbufSize,sizeof(rbufSize)))
本身会阻塞几秒钟。