我尝试了tls1.2和ntls。在弱网(1-3mb/s)环境下会话断开频率过高
客户端: SSL error: error string: SSL routines::sslv3 alert bad record mac
服务器:error:0A000139:SSL routines::record layer failure
客户端版本 5.40 (golang sdk)
服务器版本 Tongsuo version 8.5.0-pre1 for target linux-x86_64 (Underlying OpenSSL version 3.5.4)
剥去tongsuo TLS连接正常
同样适用弱网环境,tcp会话测试正常。
使用非弱网环境(15-35mb/s)会话没碰到过异常。也有可能是我测试数据量不够大,我传输2G数据,弱网情况基本没有传输成功的。
客户端服务器IO都是单线程模型。
客户端代码
//go:build cgo && tongsuo
// +build cgo,tongsuo
package client
import (
"fmt"
"net"
"os"
"path/filepath"
"client/common/config"
"strings"
"github.com/sirupsen/logrus"
ts "github.com/tongsuo-project/tongsuo-go-sdk"
"github.com/tongsuo-project/tongsuo-go-sdk/crypto"
)
func loadCertPem(file string) (*crypto.Certificate, error) {
certPem, err := os.ReadFile(file)
if err != nil {
return nil, err
}
cert, err := crypto.LoadCertificateFromPEM(certPem)
if err != nil {
return nil, err
}
return cert, nil
}
func loadKeyPem(file string) (crypto.PrivateKey, error) {
keyPem, err := os.ReadFile(file)
if err != nil {
return nil, err
}
key, err := crypto.LoadPrivateKeyFromPEM(keyPem)
if err != nil {
return nil, err
}
return key, nil
}
func resolveConfigPath(file string) string {
file = strings.TrimSpace(file)
if file == "" || filepath.IsAbs(file) {
return file
}
candidates := []string{
filepath.Join(config.GetCurrentPath(), file),
filepath.Join(filepath.Dir(config.GetClientConfigPath()), file),
filepath.Join(filepath.Dir(filepath.Dir(config.GetClientConfigPath())), file),
}
for _, candidate := range candidates {
if _, err := os.Stat(candidate); err == nil {
return candidate
}
}
return filepath.Join(filepath.Dir(config.GetClientConfigPath()), file)
}
func loadVerifyLocations(ctx *ts.Ctx, ca string) error {
ca = resolveConfigPath(ca)
if ca == "" {
return nil
}
if info, err := os.Stat(ca); err == nil && info.IsDir() {
return ctx.LoadVerifyLocations("", ca)
}
return ctx.LoadVerifyLocations(ca, "")
}
func ntlsVersion(method config.EncryptionMethod) ts.SSLVersion {
switch strings.ToUpper(string(method)) {
case "TLS", "TLSV1.2":
return ts.TLSv1_2
case "TLSV1.3":
return ts.TLSv1_3
case "TLSV1.1":
return ts.TLSv1_1
case "TLSV1":
return ts.TLSv1
case "NTLS":
return ts.NTLS
default:
return ts.NTLS
}
}
func configureCipherSuites(ctx *ts.Ctx, version ts.SSLVersion, cryptoConf config.CryptoConfig) error {
if version >= ts.TLSv1_3 {
if strings.TrimSpace(cryptoConf.ChiperSuites) == "" {
return nil
}
return ctx.SetCipherSuites(cryptoConf.ChiperSuites)
}
if strings.TrimSpace(cryptoConf.ChiperList) == "" {
return nil
}
return ctx.SetCipherList(cryptoConf.ChiperList)
}
func loadTLSClientCertificate(ctx *ts.Ctx, cryptoConf config.CryptoConfig) error {
certFile := strings.TrimSpace(cryptoConf.Cert)
keyFile := strings.TrimSpace(cryptoConf.Key)
if certFile == "" && keyFile == "" {
return nil
}
if certFile == "" || keyFile == "" {
return fmt.Errorf("tls client cert and key must be configured together")
}
cert, err := loadCertPem(resolveConfigPath(certFile))
if err != nil {
return err
}
if err := ctx.UseCertificate(cert); err != nil {
return err
}
key, err := loadKeyPem(resolveConfigPath(keyFile))
if err != nil {
return err
}
return ctx.UsePrivateKey(key)
}
func loadNTLSClientCertificates(ctx *ts.Ctx, cryptoConf config.CryptoConfig) error {
if cert, err := loadCertPem(resolveConfigPath(cryptoConf.SignCert)); err != nil {
return err
} else if err := ctx.UseSignCertificate(cert); err != nil {
return err
}
if cert, err := loadCertPem(resolveConfigPath(cryptoConf.EncCert)); err != nil {
return err
} else if err := ctx.UseEncryptCertificate(cert); err != nil {
return err
}
if key, err := loadKeyPem(resolveConfigPath(cryptoConf.SignKey)); err != nil {
return err
} else if err := ctx.UseSignPrivateKey(key); err != nil {
return err
}
if key, err := loadKeyPem(resolveConfigPath(cryptoConf.EncKey)); err != nil {
return err
} else if err := ctx.UseEncryptPrivateKey(key); err != nil {
return err
}
return nil
}
func (cli *Client) initNtls() error {
return cli.initNtlsWithConn(nil)
}
func (cli *Client) initNtlsWithConn(baseConn net.Conn) error {
version := ntlsVersion(cli.config.Crypto.Method)
ctx, err := ts.NewCtxWithVersion(version)
if err != nil {
return fmt.Errorf("%s %s", err, cli.config.Crypto.Method)
}
logrus.Infof("%04x", version)
cryptoConf := cli.config.Crypto
if err := configureCipherSuites(ctx, version, cryptoConf); err != nil {
return fmt.Errorf("configureCipherSuites: %s", err)
}
if version == ts.NTLS {
if err := loadNTLSClientCertificates(ctx, cryptoConf); err != nil {
return fmt.Errorf("loadNTLSClientCertificates: %s", err)
}
} else if err := loadTLSClientCertificate(ctx, cryptoConf); err != nil {
return fmt.Errorf("loadTLSClientCertificate: %s", err)
}
if err := loadVerifyLocations(ctx, cryptoConf.ChainCa); err != nil {
return fmt.Errorf("loadVerifyLocations: %s", err)
}
cli.state.Store(STATE_HANDSHAKE)
server := net.JoinHostPort(cli.gatewayAddr, cli.gatewayPort)
var conn *ts.Conn
if baseConn != nil {
logrus.Infof("ntls transport handshake start: mode=reused_conn addr=%s", server)
conn, err = ts.Client(baseConn, ctx)
if err != nil {
_ = baseConn.Close()
return fmt.Errorf("Client: %s", err)
}
if err := conn.Handshake(); err != nil {
_ = conn.Close()
return fmt.Errorf("Handshake: %s", err)
}
logrus.Infof("ntls transport handshake ready: mode=reused_conn addr=%s", server)
} else {
logrus.Infof("ntls transport handshake start: mode=direct_dial addr=%s", server)
conn, err = ts.Dial("tcp", server, ctx, ts.InsecureSkipHostVerification, "")
if err != nil {
return fmt.Errorf("Dial: %s", err)
}
logrus.Infof("ntls transport handshake ready: mode=direct_dial addr=%s", server)
}
cli.state.Store(STATE_RUNNING)
cli.ntls.ctx = ctx
cli.ntls.conn = conn
cli.conn = conn
return nil
}
服务器代码
#include "_tls.h"
#include <openssl/err.h>
#include <openssl/ntls.h>
#include <openssl/opensslv.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _tls_ctx
{
SSL_CTX *ssl_ctx;
_tls_config_t config;
};
struct _tls_conn
{
SSL *ssl;
int fd;
_tls_ctx_t *ctx;
};
static _Thread_local char tls_error[512];
static int set_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(tls_error, sizeof(tls_error), fmt, ap);
va_end(ap);
return -1;
}
static int set_openssl_error(const char *op)
{
unsigned long err = ERR_get_error();
char detail[256];
if (err != 0)
{
ERR_error_string_n(err, detail, sizeof(detail));
return set_error("%s failed: %s", op, detail);
}
if (errno != 0) return set_error("%s failed: %s", op, strerror(errno));
return set_error("%s failed", op);
}
static bool str_empty(const char *s) { return !s || s[0] == '\0'; }
static const SSL_METHOD *select_method(const _tls_config_t *config)
{
if (config->version == TLS_VERSION_NTLS)
{
return config->role == _TLS_ROLE_CLIENT ? NTLS_client_method()
: NTLS_server_method();
}
return config->role == _TLS_ROLE_CLIENT ? TLS_client_method()
: TLS_server_method();
}
static int configure_protocol_version(SSL_CTX *ctx,
const _tls_config_t *config)
{
int version = 0;
switch (config->version)
{
case TLS_VERSION_TLS1: version = TLS1_VERSION; break;
case TLS_VERSION_TLS1_1: version = TLS1_1_VERSION; break;
case TLS_VERSION_TLS1_2: version = TLS1_2_VERSION; break;
case TLS_VERSION_TLS1_3: version = TLS1_3_VERSION; break;
case TLS_VERSION_NTLS: return 0;
default:
return set_error("unsupported TLS version: %d", config->version);
}
if (SSL_CTX_set_min_proto_version(ctx, version) != 1)
return set_openssl_error("SSL_CTX_set_min_proto_version");
if (SSL_CTX_set_max_proto_version(ctx, version) != 1)
return set_openssl_error("SSL_CTX_set_max_proto_version");
return 0;
}
static int validate_config(const _tls_config_t *config)
{
if (!config) return set_error("TLS config is NULL");
if (config->role != _TLS_ROLE_SERVER &&
config->role != _TLS_ROLE_CLIENT)
return set_error("invalid TLS role: %d", config->role);
if (config->version == TLS_VERSION_NTLS)
{
if (str_empty(config->sign_cert_file) ||
str_empty(config->sign_key_file) ||
str_empty(config->enc_cert_file) || str_empty(config->enc_key_file))
return set_error(
"NTLS requires sign and enc certificate/key files");
return 0;
}
if (config->role == _TLS_ROLE_SERVER &&
(str_empty(config->cert_file) || str_empty(config->key_file)))
return set_error("TLS server requires cert_file and key_file");
if (!str_empty(config->cert_file) && str_empty(config->key_file))
return set_error("key_file is required when cert_file is set");
if (str_empty(config->cert_file) && !str_empty(config->key_file))
return set_error("cert_file is required when key_file is set");
return 0;
}
static int load_standard_certificates(SSL_CTX *ctx,
const _tls_config_t *config)
{
if (str_empty(config->cert_file)) return 0;
if (!str_empty(config->chain_file))
{
if (SSL_CTX_use_certificate_chain_file(ctx, config->chain_file) != 1)
return set_openssl_error("SSL_CTX_use_certificate_chain_file");
}
else if (SSL_CTX_use_certificate_file(ctx, config->cert_file,
SSL_FILETYPE_PEM) != 1)
{
return set_openssl_error("SSL_CTX_use_certificate_file");
}
if (SSL_CTX_use_PrivateKey_file(ctx, config->key_file, SSL_FILETYPE_PEM) !=
1)
return set_openssl_error("SSL_CTX_use_PrivateKey_file");
if (SSL_CTX_check_private_key(ctx) != 1)
return set_openssl_error("SSL_CTX_check_private_key");
return 0;
}
static int load_ntls_certificates(SSL_CTX *ctx,
const _tls_config_t *config)
{
if (SSL_CTX_use_sign_certificate_file(ctx, config->sign_cert_file,
SSL_FILETYPE_PEM) != 1)
return set_openssl_error("SSL_CTX_use_sign_certificate_file");
if (SSL_CTX_use_sign_PrivateKey_file(ctx, config->sign_key_file,
SSL_FILETYPE_PEM) != 1)
return set_openssl_error("SSL_CTX_use_sign_PrivateKey_file");
if (SSL_CTX_use_enc_certificate_file(ctx, config->enc_cert_file,
SSL_FILETYPE_PEM) != 1)
return set_openssl_error("SSL_CTX_use_enc_certificate_file");
if (SSL_CTX_use_enc_PrivateKey_file(ctx, config->enc_key_file,
SSL_FILETYPE_PEM) != 1)
return set_openssl_error("SSL_CTX_use_enc_PrivateKey_file");
SSL_CTX_enable_ntls(ctx);
return 0;
}
static int configure_trust(SSL_CTX *ctx, const _tls_config_t *config)
{
int verify_mode = SSL_VERIFY_NONE;
if (!str_empty(config->ca_file) || !str_empty(config->ca_path))
{
if (SSL_CTX_load_verify_locations(ctx, config->ca_file,
config->ca_path) != 1)
return set_openssl_error("SSL_CTX_load_verify_locations");
}
else if (config->verify_peer)
{
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
return set_openssl_error("SSL_CTX_set_default_verify_paths");
}
if (config->verify_peer || config->require_client_cert)
verify_mode |= SSL_VERIFY_PEER;
if (config->role == _TLS_ROLE_SERVER && config->require_client_cert)
verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
SSL_CTX_set_verify(ctx, verify_mode, NULL);
if (config->verify_depth > 0)
SSL_CTX_set_verify_depth(ctx, config->verify_depth);
return 0;
}
static int configure_ciphers(SSL_CTX *ctx, const _tls_config_t *config)
{
if (!str_empty(config->cipher_list) &&
SSL_CTX_set_cipher_list(ctx, config->cipher_list) != 1)
return set_openssl_error("SSL_CTX_set_cipher_list");
if (config->version == TLS_VERSION_TLS1_3 &&
!str_empty(config->cipher_suites) &&
SSL_CTX_set_ciphersuites(ctx, config->cipher_suites) != 1)
return set_openssl_error("SSL_CTX_set_ciphersuites");
if (!str_empty(config->groups_list) &&
SSL_CTX_set1_groups_list(ctx, config->groups_list) != 1)
return set_openssl_error("SSL_CTX_set1_groups_list");
return 0;
}
static _tls_status_t map_ssl_status(SSL *ssl, int ret, const char *op)
{
int err = SSL_get_error(ssl, ret);
switch (err)
{
case SSL_ERROR_WANT_READ: return _TLS_WANT_READ;
case SSL_ERROR_WANT_WRITE: return _TLS_WANT_WRITE;
case SSL_ERROR_ZERO_RETURN: return _TLS_CLOSED;
case SSL_ERROR_SYSCALL:
if (ret == 0)
{
set_error("%s failed: peer closed connection", op);
return _TLS_CLOSED;
}
set_openssl_error(op);
return _TLS_ERROR;
case SSL_ERROR_SSL:
default: set_openssl_error(op); return _TLS_ERROR;
}
}
int _tls_library_init(void)
{
if (OPENSSL_init_ssl(
OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
NULL) != 1)
return set_openssl_error("OPENSSL_init_ssl");
return 0;
}
void _tls_library_cleanup(void) { OPENSSL_cleanup(); }
const char *_tls_last_error(void) { return tls_error; }
const char *_tls_status_name(_tls_status_t status)
{
switch (status)
{
case _TLS_OK: return "ok";
case _TLS_WANT_READ: return "want_read";
case _TLS_WANT_WRITE: return "want_write";
case _TLS_CLOSED: return "closed";
case _TLS_ERROR: return "error";
default: return "unknown";
}
}
const char *_tls_version_string(void)
{
return OpenSSL_version(OPENSSL_VERSION);
}
int _tls_ctx_create(const _tls_config_t *config,
_tls_ctx_t **out)
{
_tls_ctx_t *ctx = NULL;
const SSL_METHOD *method = NULL;
if (!out) return set_error("out TLS ctx is NULL");
*out = NULL;
if (_tls_library_init() != 0) return -1;
if (validate_config(config) != 0) return -1;
method = select_method(config);
if (!method) return set_error("failed to select TLS method");
ctx = calloc(1, sizeof(*ctx));
if (!ctx) return set_error("calloc _tls_ctx failed");
ctx->config = *config;
ctx->ssl_ctx = SSL_CTX_new(method);
if (!ctx->ssl_ctx)
{
set_openssl_error("SSL_CTX_new");
goto error;
}
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION | config->options);
SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
config->mode);
if (configure_protocol_version(ctx->ssl_ctx, config) != 0) goto error;
if (config->version == TLS_VERSION_NTLS)
{
if (load_ntls_certificates(ctx->ssl_ctx, config) != 0) goto error;
}
else if (load_standard_certificates(ctx->ssl_ctx, config) != 0)
{
goto error;
}
if (configure_trust(ctx->ssl_ctx, config) != 0) goto error;
if (configure_ciphers(ctx->ssl_ctx, config) != 0) goto error;
SSL_CTX_set_session_cache_mode(
ctx->ssl_ctx,
config->enable_session_cache
? (config->role == _TLS_ROLE_CLIENT ? SSL_SESS_CACHE_CLIENT
: SSL_SESS_CACHE_SERVER)
: SSL_SESS_CACHE_OFF);
*out = ctx;
return 0;
error:
_tls_ctx_free(ctx);
return -1;
}
void _tls_ctx_free(_tls_ctx_t *ctx)
{
if (!ctx) return;
if (ctx->ssl_ctx) SSL_CTX_free(ctx->ssl_ctx);
free(ctx);
}
SSL_CTX *_tls_ctx_get_ssl_ctx(_tls_ctx_t *ctx)
{
return ctx ? ctx->ssl_ctx : NULL;
}
int _tls_conn_create(_tls_ctx_t *ctx, int fd, _tls_conn_t **out)
{
_tls_conn_t *conn = NULL;
if (!out) return set_error("out TLS conn is NULL");
*out = NULL;
if (!ctx || !ctx->ssl_ctx) return set_error("TLS ctx is NULL");
if (fd < 0) return set_error("invalid TLS fd: %d", fd);
conn = calloc(1, sizeof(*conn));
if (!conn) return set_error("calloc _tls_conn failed");
conn->ssl = SSL_new(ctx->ssl_ctx);
if (!conn->ssl)
{
set_openssl_error("SSL_new");
free(conn);
return -1;
}
if (SSL_set_fd(conn->ssl, fd) != 1)
{
set_openssl_error("SSL_set_fd");
SSL_free(conn->ssl);
free(conn);
return -1;
}
conn->fd = fd;
conn->ctx = ctx;
if (ctx->config.role == _TLS_ROLE_CLIENT)
SSL_set_connect_state(conn->ssl);
else
SSL_set_accept_state(conn->ssl);
*out = conn;
return 0;
}
void _tls_conn_free(_tls_conn_t *conn)
{
if (!conn) return;
if (conn->ssl) SSL_free(conn->ssl);
free(conn);
}
SSL *_tls_conn_get_ssl(_tls_conn_t *conn)
{
return conn ? conn->ssl : NULL;
}
int _tls_conn_fd(const _tls_conn_t *conn)
{
return conn ? conn->fd : -1;
}
_tls_status_t _tls_accept(_tls_conn_t *conn)
{
int ret;
if (!conn || !conn->ssl)
{
set_error("TLS conn is NULL");
return _TLS_ERROR;
}
ret = SSL_accept(conn->ssl);
return ret == 1 ? _TLS_OK
: map_ssl_status(conn->ssl, ret, "SSL_accept");
}
_tls_status_t _tls_connect(_tls_conn_t *conn)
{
int ret;
if (!conn || !conn->ssl)
{
set_error("TLS conn is NULL");
return _TLS_ERROR;
}
ret = SSL_connect(conn->ssl);
return ret == 1 ? _TLS_OK
: map_ssl_status(conn->ssl, ret, "SSL_connect");
}
_tls_status_t _tls_read(_tls_conn_t *conn, void *buf, size_t len,
ssize_t *nread)
{
int ret;
if (nread) *nread = 0;
if (!conn || !conn->ssl || !buf)
{
set_error("invalid TLS read arguments");
return _TLS_ERROR;
}
ret = SSL_read(conn->ssl, buf, (int)len);
if (ret > 0)
{
if (nread) *nread = ret;
return _TLS_OK;
}
return map_ssl_status(conn->ssl, ret, "SSL_read");
}
_tls_status_t _tls_write(_tls_conn_t *conn, const void *buf,
size_t len, ssize_t *nwritten)
{
int ret;
if (nwritten) *nwritten = 0;
if (!conn || !conn->ssl || !buf)
{
set_error("invalid TLS write arguments");
return _TLS_ERROR;
}
ret = SSL_write(conn->ssl, buf, (int)len);
if (ret > 0)
{
if (nwritten) *nwritten = ret;
return _TLS_OK;
}
return map_ssl_status(conn->ssl, ret, "SSL_write");
}
_tls_status_t _tls_shutdown(_tls_conn_t *conn)
{
int ret;
if (!conn || !conn->ssl)
{
set_error("TLS conn is NULL");
return _TLS_ERROR;
}
ret = SSL_shutdown(conn->ssl);
if (ret == 1) return _TLS_OK;
if (ret == 0) return _TLS_WANT_READ;
return map_ssl_status(conn->ssl, ret, "SSL_shutdown");
}
SSL_CTX *_tls_create_ctx(_tls_config_t *config)
{
_tls_ctx_t *ctx = NULL;
SSL_CTX *ssl_ctx = NULL;
if (_tls_ctx_create(config, &ctx) != 0) return NULL;
ssl_ctx = ctx->ssl_ctx;
ctx->ssl_ctx = NULL;
_tls_ctx_free(ctx);
return ssl_ctx;
}
void _tls_free_ctx(SSL_CTX *ctx)
{
if (ctx) SSL_CTX_free(ctx);
}
#pragma once
#include <openssl/ssl.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum
{
_TLS_ROLE_SERVER = 0,
_TLS_ROLE_CLIENT = 1,
} _tls_role_t;
typedef enum
{
TLS_VERSION_TLS1 = 0,
TLS_VERSION_TLS1_1,
TLS_VERSION_TLS1_2,
TLS_VERSION_TLS1_3,
TLS_VERSION_NTLS,
} _tls_version_t;
typedef _tls_version_t tls_version_t;
typedef enum
{
_TLS_OK = 0,
_TLS_WANT_READ = 1,
_TLS_WANT_WRITE = 2,
_TLS_CLOSED = 3,
_TLS_ERROR = -1,
} _tls_status_t;
typedef struct _tls_config
{
_tls_role_t role;
_tls_version_t version;
const char *cert_file;
const char *key_file;
const char *chain_file;
const char *sign_cert_file;
const char *sign_key_file;
const char *enc_cert_file;
const char *enc_key_file;
const char *ca_file;
const char *ca_path;
bool verify_peer;
bool require_client_cert;
int verify_depth;
const char *cipher_list;
const char *cipher_suites;
const char *groups_list;
bool enable_session_cache;
long options;
long mode;
} _tls_config_t;
typedef struct _tls_ctx _tls_ctx_t;
typedef struct _tls_conn _tls_conn_t;
int _tls_library_init(void);
void _tls_library_cleanup(void);
const char *_tls_last_error(void);
const char *_tls_status_name(_tls_status_t status);
const char *_tls_version_string(void);
int _tls_ctx_create(const _tls_config_t *config,
_tls_ctx_t **out);
void _tls_ctx_free(_tls_ctx_t *ctx);
SSL_CTX *_tls_ctx_get_ssl_ctx(_tls_ctx_t *ctx);
int _tls_conn_create(_tls_ctx_t *ctx, int fd,
_tls_conn_t **out);
void _tls_conn_free(_tls_conn_t *conn);
SSL *_tls_conn_get_ssl(_tls_conn_t *conn);
int _tls_conn_fd(const _tls_conn_t *conn);
_tls_status_t _tls_accept(_tls_conn_t *conn);
_tls_status_t _tls_connect(_tls_conn_t *conn);
_tls_status_t _tls_read(_tls_conn_t *conn, void *buf,
size_t len, ssize_t *nread);
_tls_status_t _tls_write(_tls_conn_t *conn, const void *buf,
size_t len, ssize_t *nwritten);
_tls_status_t _tls_shutdown(_tls_conn_t *conn);
SSL_CTX *_tls_create_ctx(_tls_config_t *config);
void _tls_free_ctx(SSL_CTX *ctx);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
}
#endif
我尝试了tls1.2和ntls。在弱网(1-3mb/s)环境下会话断开频率过高
客户端: SSL error: error string: SSL routines::sslv3 alert bad record mac
服务器:error:0A000139:SSL routines::record layer failure
客户端版本 5.40 (golang sdk)
服务器版本 Tongsuo version 8.5.0-pre1 for target linux-x86_64 (Underlying OpenSSL version 3.5.4)
剥去tongsuo TLS连接正常
同样适用弱网环境,tcp会话测试正常。
使用非弱网环境(15-35mb/s)会话没碰到过异常。也有可能是我测试数据量不够大,我传输2G数据,弱网情况基本没有传输成功的。
客户端服务器IO都是单线程模型。
客户端代码
服务器代码