gSOAP SSL服务器报错:soap_ssl_accept()无套接字,求问题排查
问题:gSOAP SSL服务器启动时出现"No socket in soap_ssl_accept()"错误
我正尝试搭建一个带SSL的gSOAP示例服务器,操作步骤如下:
- 生成自签名证书:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes - 将证书合并为一个文件:
cat cert.pem key.pem >both.pem - 运行如下代码:
#include <stdsoap2.h> #include <stdexcept> #include <sstream> #include <iostream> #define PORT 6667 int main(int argc, char **argv) { struct soap engine(SOAP_C_UTFSTRING); engine.bind_flags=SO_REUSEADDR; soap_ssl_noinit(); int rc = soap_ssl_server_context(&engine, SOAP_SSL_DEFAULT, "both.pem", // keyfile nullptr, // password nullptr, // cacert nullptr, // capath nullptr, // dhfile nullptr, // randfile nullptr // serverid ); if (rc) { std::stringstream error; soap_stream_fault(&engine, error); throw std::runtime_error("soap_ssl_server context failed " + error.str()); } rc = soap_bind(&engine, NULL, PORT, 100); if (rc < 0) { throw std::runtime_error("soap_bind failed " + std::to_string(rc)); } int master_socket = rc; std::cout << "master socket: " << master_socket << std::endl; engine.accept_timeout = 50; engine.send_timeout = 50; engine.recv_timeout = 50; rc = soap_ssl_accept(&engine); if (rc) { soap_stream_fault(&engine, std::cout); throw std::runtime_error("soap_ssl_accept failed " + std::to_string(rc)); } std::cout << "soap_ssl_accept " << rc << std::endl; return 0; }
但运行后输出:
master socket: 3 Error 30 fault detected[no subcode] "SSL/TLS error" Detail: No socket in soap_ssl_accept() terminate called after throwing an instance of 'std::runtime_error' what(): soap_ssl_accept failed 30
请问我哪里操作有误?
解答
看起来你在gSOAP SSL服务器的流程上踩了个小坑——问题出在soap_ssl_accept()的调用时机不对!
soap_ssl_accept()的作用是完成SSL握手,但它需要先有一个已经建立的客户端连接socket才行。你的代码在soap_bind()之后直接调用它,可此时soap结构体里只有监听用的主socket,并没有客户端连接的socket,所以才会抛出"No socket in soap_ssl_accept()"的错误。
正确的流程应该是这样:
- 初始化soap结构体并配置SSL上下文(这部分你已经做对了)
- 用
soap_bind()绑定端口,拿到监听用的主socket - 调用
soap_accept()等待客户端连接,这个函数会把客户端的连接socket关联到soap结构体中 - 最后再调用
soap_ssl_accept()完成SSL握手
修正后的代码示例:
#include <stdsoap2.h> #include <stdexcept> #include <sstream> #include <iostream> #define PORT 6667 int main(int argc, char **argv) { struct soap engine(SOAP_C_UTFSTRING); engine.bind_flags=SO_REUSEADDR; soap_ssl_noinit(); int rc = soap_ssl_server_context(&engine, SOAP_SSL_DEFAULT, "both.pem", // keyfile nullptr, // password nullptr, // cacert nullptr, // capath nullptr, // dhfile nullptr, // randfile nullptr // serverid ); if (rc) { std::stringstream error; soap_stream_fault(&engine, error); throw std::runtime_error("soap_ssl_server context failed " + error.str()); } rc = soap_bind(&engine, NULL, PORT, 100); if (rc < 0) { throw std::runtime_error("soap_bind failed " + std::to_string(rc)); } int master_socket = rc; std::cout << "master socket: " << master_socket << std::endl; engine.accept_timeout = 50; engine.send_timeout = 50; engine.recv_timeout = 50; // 先接收客户端的连接请求,拿到客户端socket rc = soap_accept(&engine); if (rc < 0) { throw std::runtime_error("soap_accept failed " + std::to_string(rc)); } std::cout << "Accepted client connection, socket: " << rc << std::endl; // 现在再完成SSL握手 rc = soap_ssl_accept(&engine); if (rc) { soap_stream_fault(&engine, std::cout); throw std::runtime_error("soap_ssl_accept failed " + std::to_string(rc)); } std::cout << "SSL handshake succeeded!" << std::endl; // 这里可以添加处理客户端SOAP请求的逻辑,比如调用soap_serve(&engine) // 记得释放资源 soap_destroy(&engine); soap_end(&engine); soap_done(&engine); return 0; }
额外提醒:
- 实际生产环境的服务器,通常需要套一个循环,不断调用
soap_accept()和soap_ssl_accept()来处理多个客户端连接 - 一定要记得在程序结束时调用
soap_destroy()、soap_end()、soap_done(),避免内存泄漏 - 如果需要验证客户端证书,可以在
soap_ssl_server_context()中传入CA证书路径(cacert参数),并把SOAP_SSL_DEFAULT换成SOAP_SSL_REQUIRE_CLIENT_AUTH这类需要客户端认证的标志
内容的提问来源于stack exchange,提问作者Adam Trhon
相关产品推荐
相关产品推荐

