我需要使用OpenSSL传输序列化数据。我的代码有效,但在某些情况下,传输的数据有一些差异。
这是序列化数据http://s000.tinyupload.com/?file_id=02935614701824936895
int sslWrite(SSL *ssl, const void *buf, int num)
{
const int MAX_SIZE = 0x10000;
const int INT_SIZE = sizeof(int);
const int numv = INT_SIZE + num;
if (numv > MAX_SIZE) {
return -1;
}
std::vector<char> bufv(numv);
memcpy(&bufv[0], &numv, INT_SIZE);
if (num != 0) {
memcpy(&bufv[INT_SIZE], buf, num);
}
int n = 0;
while (n < num) {
int w = SSL_write(ssl, &bufv[0], numv - n);
if (w < 0) {
return 0;
}
n += w;
}
return n;
}
int sslRead(SSL *ssl, void *buf, int num)
{
const int INT_SIZE = sizeof(int);
const int BUF_SIZE = 0x10000;
char bufv[BUF_SIZE];
int m = SSL_read(ssl, (char *)bufv, num);
if (m < 0) {
return m;
}
const int n = *(int *)(bufv);
if (n > BUF_SIZE - INT_SIZE) {
return -1;
}
while (m < n) {
int k = SSL_read(ssl, (char *)bufv + m, num - m);
if (k < 0) {
return k;
}
m += k;
}
memcpy(buf, &bufv[INT_SIZE], n - INT_SIZE);
return n - INT_SIZE;
}
-
// Server
#include <fstream>
#include <WS2tcpip.h>
#include <Winsock2.h>
#define _WINSOCKAPI_
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>
int OpenListener(int port)
{
int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
perror("can't bind port");
abort();
}
if (listen(sd, 10) != 0) {
perror("Can't configure listening port");
abort();
}
return sd;
}
SSL_CTX* InitServerCTX(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv2_server_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
if (SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
int main()
{
SSL_CTX *ctx;
int server;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SSL_library_init();
ctx = InitServerCTX();
LoadCertificates(ctx, "sert.crt", "sert.key");
server = OpenListener(3456);
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int client = accept(server, (struct sockaddr*)&addr, &len);
SSL *ssl;
printf("Connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) == -1) {
ERR_print_errors_fp(stderr);
}
else {
std::ifstream fin("data", std::ios::binary);
std::string data((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>());
fin.close();
sslWrite(ssl, &data[0], (int)data.size());
const int BUF_SIZE = 0x10000;
std::string data1;
data1.resize(BUF_SIZE);
int n = sslRead(ssl, &data1[0], BUF_SIZE);
data1.resize(n);
/////////////////////////////////////////
bool b = (data == data1); // b - FALSE!!!
/////////////////////////////////////////
}
SSL_free(ssl);
closesocket(client);
}
-
// Client
#include <string>
#include <WS2tcpip.h>
#include <Winsock2.h>
#define _WINSOCKAPI_
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>
SSL_CTX *InitCTX(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv2_client_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
int OpenConnection(const char *hostName, int port)
{
struct hostent *host;
struct sockaddr_in addr;
if ((host = gethostbyname(hostName)) == NULL) {
perror(hostName);
abort();
}
int sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if (connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
closesocket(sd);
perror(hostName);
abort();
}
return sd;
}
int main()
{
const std::string &hostName = "localhost";
const int port = 3456;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SSL_library_init();
SSL_CTX *ctx = InitCTX();
int server = OpenConnection(hostName.c_str(), port);
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, server);
if (SSL_connect(ssl) == -1) {
ERR_print_errors_fp(stderr);
}
else {
const int BUF_SIZE = 0x10000;
std::string data;
data.resize(BUF_SIZE);
int n = sslRead(ssl, &data[0], BUF_SIZE);
data.resize(n);
sslWrite(ssl, &data[0], (int)data.size());
}
}
答案 0 :(得分:2)
您必须处理部分写入和部分读取。
对于SSL_write()
,您需要检查返回值以确保您的数据已完全写入。如果您的数据只是部分写入,则需要再次为剩余数据调用SSL_write()
。重复,直到写完所有数据。
对于SSL_read()
,您需要继续阅读,直到获得零返回值,然后检查干净关闭。执行此操作时,您需要附加到缓冲区并在接收数据时进行构建。
更新
评论中的@dascandy是正确的。
您需要知道何时停止阅读以及处理部分阅读。单独关闭连接在您的情况下将不起作用,因为当您关闭与SSL的连接时,您将无法在同一连接上发送回复。 SSL没有&#34;半开&#34;像TCP这样的连接。
一种合理的方法是在发送时为每个消息添加一个字节数,并在接收第一个读取字节数时(使用任何编码方便 - 固定长度,带分隔符的文本,等等),然后准确读取该数量字节进入缓冲区。