最近公司项目需要把服务器端C++实现的 websocket 改成websocket secure(websocket + ssl)。百度了一下有一个广泛使用的ssl库,openssl。花了两天时间一顿操作之后客户端用js终于连接上服务器上的wss。但是诡异的是,每次客户端来消息后,服务器调用SSL_read函数总是会读完一个字节后返回,第二次调用(epoll控制)才会读完剩下的字节。
比如客户端发了502字节的数据包,服务器这边epoll响应 调用到SSL_read,会先读1个字节,epoll再次响应,再读剩下的501个字节。等于调用了两次read才把一个数据包读完!
百思不得其解啊,在客户端用wrieshark抓包,客户端从来没发过数据长度位1的有效包。说明不是客户端问题。
只好去翻ssl manual ,怀疑是不是什么参数没设置。也没发现有什么参数可设置的。。
最后,怀疑是协议版本的问题。。然后我把把openssl 用的ssl协议从TSL1.0,改成了TSL1.2,就尼玛好了。。可能是js websocket类用的ssl协议版本高于1.0,结果导致这个问题。
好吧,差点怀疑人生。不知道有没有朋友和我遇到过一样的问题。
顺便把我参考网上的代码封装的openssl类贴一下,一个openSSL类,一个SSL管理类。
//
// Created by jamesjiang on 2018/5/15.
//
#ifndef GAME_OPENSSL_H
#define GAME_OPENSSL_H
#include <string>
#include "common_tcpconnection.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
using namespace GameJayo::Server;
class openSSL
{
public:
openSSL();
~openSSL();
public:
// 初始化 openssl 上下文
static int32_t init_ssl_ctx();
static void shutdown_ssl();
int32_t creat_ssl(int32_t fd);
int32_t accept_ssl_fd(int32_t fd);
// return > 0 recv 字节数, < 0 error , == 0 判断
int32_t ssl_recv(char* data, int32_t size);
// return > 0 send 字节数, < 0 error , == 0 判断
int32_t ssl_send(char* data, int32_t size);
private:
SSL* m_ssl;
int32_t m_fd;
static SSL_CTX* m_ctx;
public:
void ShowCerts();
};
#endif //GAME_OPENSSL_H
//
// Created by jamesjiang on 2018/5/15.
//
#include "openSSL.h"
#define CA_CERT_FILE "ssl/123u.com_combined.crt"
#define SERVER_CERT_FILE "ssl/123u.com_combined.crt"
#define SERVER_KEY_FILE "ssl/123u.com.key"
SSL_CTX* openSSL::m_ctx = NULL;
openSSL::openSSL()
{
}
openSSL::~openSSL()
{
/* 关闭 SSL 连接 */
if (m_ssl)
{
SSL_shutdown(m_ssl);
/* 释放 SSL */
SSL_free(m_ssl);
}
m_fd = -1;