为何调用SSL_read读取Socket前会出现数秒暂停?
SSL_write后SSL_read挂起10秒的解决方法
问题描述
我正在编写一个极简程序,用于连接服务器、发送请求并读取响应,该程序使用OpenSSL提供TLS支持。代码可编译运行,但在调用SSL_write后执行到SSL_read函数时,程序会挂起约10秒才开始读取数据。请问该如何避免此等待?
原代码
#include <netdb.h> #include <openssl/ssl.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #define HOST "geminiprotocol.net" #define REQUEST "gemini://geminiprotocol.net/" #define PORT "1965" #define BUFMAX 100 int create_socket(int *fd) { int res, sock; struct addrinfo hints, *servinfo, *p; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((res = getaddrinfo(HOST, PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res)); return res; } for (p = servinfo; p != NULL; p = p->ai_next) { if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { continue; } if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) { continue; } break; } if (p == NULL) { return -1; } freeaddrinfo(servinfo); *fd = sock; return 0; } int read_body(SSL *ssl) { char buf[BUFMAX + 1]; int ret; while ((ret = SSL_read(ssl, &buf, BUFMAX)) > 0) { buf[ret] = '\0'; printf("%s", buf); } if (ret == 0) { return 0; } return -1; } int main(void) { int fd, ret; SSL_CTX* ctx = SSL_CTX_new(TLS_client_method()); SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); if (!ctx) { fprintf(stderr, "Could not create SSL CTX\n"); exit(1); } printf("Created Context\n"); SSL* ssl = SSL_new(ctx); if (!ssl) { fprintf(stderr, "Could not create SSL\n"); exit(2); } printf("Created SSL\n"); if (create_socket(&fd)) { fprintf(stderr, "Could not create socket\n"); exit(7); } printf("Created Socket\n"); if (!SSL_set_fd(ssl, fd)) { fprintf(stderr, "Could not set ssl fd\n"); exit(3); } printf("Set FD\n"); SSL_set_connect_state(ssl); if ((ret = SSL_connect(ssl)) != 1) { fprintf(stderr, "SSL_connect: %s\n", strerror(SSL_get_error(ssl, ret))); exit(4); } printf("SSL connected\n"); if ((ret = SSL_write(ssl, REQUEST, strlen(REQUEST))) <= 0) { fprintf(stderr, "SSL_write: %s\n", strerror(SSL_get_error(ssl, ret))); exit(5); } printf("SENT Request\n\n"); read_body(ssl); printf("\nREAD Response\n\n"); if (fd) { close(fd); } SSL_free(ssl); SSL_CTX_free(ctx); }
问题原因
核心问题是请求不符合Gemini协议规范:
Gemini协议要求客户端发送的请求是**单个UTF-8编码的URL,后跟换行符(CRLF或LF)**作为请求结束的标记。你的请求REQUEST没有添加换行符,导致服务器一直在等待完整的请求,直到超时(约10秒)才返回响应,最终表现为SSL_read挂起。
另外,原代码缺少<string.h>和<stdlib.h>头文件,编译时会出现警告,已在上述代码中补充。
解决方案
1. 修正请求格式
在请求末尾添加换行符,让服务器能立即识别请求已结束:
#define REQUEST "gemini://geminiprotocol.net/\r\n"
仅用LF(\n)也符合Gemini规范,服务器通常都能兼容处理。
2. 可选:设置Socket超时(避免极端无响应情况)
如果担心后续仍出现服务器无响应的情况,可以给socket设置读写超时,避免程序无限挂起。在create_socket函数的connect成功后添加以下代码:
struct timeval timeout; timeout.tv_sec = 5; // 设置5秒超时 timeout.tv_usec = 0; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt SO_RCVTIMEO"); close(sock); continue; } if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt SO_SNDTIMEO"); close(sock); continue; }
设置超时后,SSL_read/SSL_write会返回超时错误,需要在代码中针对性处理该错误类型。
修改后的关键代码片段
// 修正请求格式 #define REQUEST "gemini://geminiprotocol.net/\r\n" // 在create_socket的connect成功后添加超时设置 if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) { continue; } struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt SO_RCVTIMEO"); close(sock); continue; } if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt SO_SNDTIMEO"); close(sock); continue; } break;
验证效果
修改请求格式后,服务器会立即识别到完整请求并返回响应,SSL_read不会再出现10秒挂起的情况。
内容的提问来源于stack exchange,提问作者Phandal
相关产品推荐
相关产品推荐

