C++ SSL客户端与服务器无法双向收发消息及SSL_read不等待问题求助
嘿,我仔细扒了你的代码,发现几个关键错误直接导致了无法收发消息和SSL_read不等待的问题,一个个给你捋清楚:
1. 服务器用了客户端的SSL上下文方法(最致命的问题)
你在SSLServer::initCtx()里写了:
const SSL_METHOD *method = SSLv23_client_method();
这可不行啊!服务器必须用服务器专用的SSL方法,你用客户端的方法,SSL握手根本没法正常完成,后面的读写自然全是问题。
赶紧改成服务器方法:
void SSLServer::initCtx(){ OpenSSL_add_all_algorithms(); SSL_load_error_strings(); // 替换成服务器专用方法 const SSL_METHOD *method = SSLv23_server_method(); mCtx = SSL_CTX_new(method); }
2. Accept调用的地址结构不匹配
在SSLServer::start()里,你用sockaddr_in的地址去接收客户端连接,但第三个参数却是sockaddr_storage的大小,这会导致地址结构长度不匹配,连接建立都可能出问题:
int clientSocket = accept(mServerSocket, (struct sockaddr *) &mSockAddrIn, &mSockLenT);
sockaddr_storage是通用的地址容器,应该用它来接收:
int clientSocket = accept(mServerSocket, (struct sockaddr *) &mSockAddrStorage, &mSockLenT);
3. SSL握手失败就把服务器监听socket关了,这哪行?
你在SSLServer::start()里,只要SSL_accept失败,就直接调用closeServerSocket()把服务器的监听socket关了,那服务器直接就罢工了,再也接不了新连接了!
应该只关当前这个失败的客户端socket,释放对应的SSL对象就行:
if(SSL_accept(ssl) != FAIL){ std::thread client(func, ssl); client.detach(); } else { // 只清理当前失败的客户端资源 SSL_free(ssl); close(clientSocket); // 打印错误信息,方便排查问题 ERR_print_errors_fp(stderr); }
4. SSL读写的返回值处理完全错了
你的sendMsg和recvMsg直接把SSL_read/SSL_write的返回值当bool用,但这俩函数的返回值是实际读写的字节数:
- 正数:成功读写的字节数
- 0:对方主动关了连接
- 负数:真的出错了
直接返回会导致逻辑混乱,比如SSL_read返回0(正常关闭)会被当成接收失败,返回部分字节也会被当成成功但你可能没读全。
给你修复客户端的recvMsg和sendMsg:
// 修复客户端接收逻辑 bool SLLClient::recvMsg(char *buffer, int len){ int bytesRead = SSL_read(mSSL, buffer, len); if(bytesRead > 0){ return true; // 读到数据了 } else if(bytesRead == 0){ std::cerr << "服务器主动关闭了连接" << std::endl; return false; } else { // 打印错误详情 ERR_print_errors_fp(stderr); return false; } } // 修复客户端发送逻辑(确保全部发送的话可以加循环) bool SLLClient::sendMsg(char * msg, int len){ int bytesSent = SSL_write(mSSL, msg, len); if(bytesSent > 0){ // 如果需要确保所有字节都发出去,这里可以加循环重试 return bytesSent == len; } else { ERR_print_errors_fp(stderr); return false; } }
服务器端的回调函数里处理SSL读写时,也要遵循这个逻辑,别再直接返回bool了。
5. 服务器证书加载的冗余代码(可选优化)
你的SSLServer::loadCertificate()里调用了SSL_CTX_load_verify_locations和SSL_CTX_set_default_verify_paths,这俩是用来加载信任的CA证书,验证客户端证书用的。如果你的服务器不需要验证客户端(单向认证),完全可以删掉这两行,只保留加载自身证书和私钥的部分,减少不必要的配置。
6. 类名拼写小错误
你的客户端类名叫SLLClient,应该是SSLClient吧?虽然不影响功能,但以后维护容易搞混,建议改过来。
额外调试小技巧
要是修复后还是有问题,记得在SSL_connect和SSL_accept失败时,调用ERR_print_errors_fp(stderr),OpenSSL会输出详细的错误信息,比如证书无效、密钥不匹配之类的,能帮你快速定位问题。还有,默认socket是阻塞模式的,如果你没改的话,SSL_read本来就会等数据,要是还是不等,大概率是握手没成功,先看错误信息准没错。
内容的提问来源于stack exchange,提问作者Davide

