You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 17:28:15