• c语言实现https客户端 源码+详细注释(OpenSSL下载,visual studio编译器环境配置)


    OpenSSL的下载和环境配置

    请参考:openssl下载安装教程
    步骤:官网下载->安装到选定目录->配置环境变量->打开命令窗口检查是否安装成功
    注意:
    打开命令窗口(快捷键win+r,在弹出窗口内输入cmd按回车),输入命令openssl version如果显示openssl版本则表示安装成功。
    我出现的问题:明明安装上了却显示

    B:>openssl version
    ‘openssl’ 不是内部或外部命令,也不是可运行的程序
    批处理文件

    解决方法:更改到lib目录,输入命令:

    B:\OpenSSL-Win64\lib>openssl.exe

    因为我的版本openssl.exe文件在lib目录下,可以在OpenSSL-Win64文件夹里直接搜索openssl.exe,就知道在哪个目录下了。
    运行之后再输入openssl version就会显示版本,例:

    B:\OpenSSL-Win64\bin>openssl version
    OpenSSL 3.1.0 14 Mar 2023 (Library: OpenSSL 3.1.0 14 Mar 2023)

    出现版本信息代表安装成功。

    visual studio项目环境配置

    我使用visual studio2019版本,以下步骤仅供参考。
    1.在属性页顶端右边,把平台改成x64(根据自己需要),点击右侧配置管理器按钮,将此项目的平台改成x64(根据自己需要)。
    2.打开项目界面,在上方菜单栏选择项目->属性
    3.在左侧选择VC++目录,选择包含目录,编辑,粘贴openssl的include目录的绝对路径。

    例如:B:\OpenSSL-Win64\include

    选择库目录,编辑,粘贴openssl的lib目录的绝对路径

    例如:B:\OpenSSL-Win64\lib

    4.选择左侧C/C++常规,选择附加包含目录,编辑,粘贴openssl的include目录的绝对路径。

    例如:B:\OpenSSL-Win64\include

    5.选择左侧链接器,选择下侧输入,选择右侧附加依赖项,编辑,写入libssl.lib,libcrypto.lib的路径。在lib目录中,打开自己下载的openssl文件夹看一下具体是哪个路径,不同版本可能不同,我使用的版本是 3.1.0 14

    例如:
    B:\OpenSSL-Win64\lib\libssl.lib
    B:\OpenSSL-Win64\lib\libcrypto.lib

    注意:编译器所有无法识别SSL…的问题都是环境没配置好,再仔细看一下上述步骤,都是一步步配置过的,亲测OK。

    代码实现

    思路

    请添加图片描述

    源码

    #include
    #include 
    #include  
    #include 
    #pragma comment(lib, "Ws2_32.lib")
    int main() {
    	// 初始化 Winsock(windows套接字)
    	/*WSADATA是windows API定义的一个结构,存储关于winsock实现的详细信息*/
    	WSADATA wsaData;
    	/*WSAStartup()这个函数用于初始化Winsock库,
    	MAKEWORD(2, 2)相当于创建了一个对Winsock 2.2的版本请求,
    	&wsaData用于将wsaData地址结构传入函数
    	*/
    	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
    		/*
    		WSAStartup返回了一个非0的值则代表初始化失败
    		*/
    		printf("WSAStartup failed\n");
    		return 1;
    	}
    	printf("客户端Winsock初始化成功\n");
    	printf("————————————————————\n");
    	//创建客户端对象
    	/*声明一个整型变量socket_fd用来存储套接字描述符
    	*/
    	int socket_fd;
    	/*socket()函数用于创建一个新的套接字,
    	AF_INET指定了地址族为ipv4,
    	SOCK_STREAM指定了套接字类型为流式套接字,即TCP
    	第三个参数,通常用来指定协议
    	*/
    	if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0))== 0){
    		perror("socket failed");
    		exit(EXIT_FAILURE);
    	}
    	/*声明一个结构体变量sock_addr,用于存储套接字地址信息(IPv4)
    	*/
    	struct sockaddr_in sock_addr;	
    	sock_addr.sin_family = AF_INET; //使用ipv4地址族
    	/*htons() 函数用于将主机字节顺序转换为网络字节顺序,确保在不同架构的计算机上,端口号的表示都是一致的。
    	*/
    	sock_addr.sin_port = htons(8080); //设置端口号为8080
    	
    	//设置网络ip
    	/*调用inet_pton()函数将字符串形式的IP地址转换为网络字节序的二进制IP地址,
    	并存储到sock_addr.sin_addr中
    	*/
    	if (inet_pton(AF_INET, "192.168.1.128", &sock_addr.sin_addr) <= 0) {
    		/*inet_pton()函数返回值如果小于等于零,表示转换失败*/
    		perror("inet_pton() failed");
    		exit(EXIT_FAILURE);
    	}
    	printf("创建客户端对象成功,尝试连接服务器\n");
    	printf("————————————————————\n");
    	//连接服务器
    	if ((connect(socket_fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0)) {
    		perror("connect failed\n");
    		exit(EXIT_FAILURE);
    	}
    	printf("对服务器发起连接\n");
    
    	//初始化openssl库
    
    	//SSL库初始化
    	SSL_library_init();
    	//载入所有SSL算法
    	SSLeay_add_ssl_algorithms();
    	//载入所有SSL错误消息
    	SSL_load_error_strings();
    	//创建SSL会话环境
    	//使用 TLS_client_method() 函数创建了一个 SSL 上下文结构(SSL_CTX)
    	/*TLS_client_method()
    	const SSL_METHOD *TLS_client_method(void);
    	函数的返回值用于指定SSL/TSL连接使用的方法,这个方法指定了客户端如何与服务器端建立连接。
    	通过调用该函数,获取一个用于SSL/TSL客户端的SSL_METHOD结构,这个结构被传递给SSL_CTX_new()函数,用于创建SSL_CTX上下文对象。
    	  SSL_CTX_new()
    	  SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
    	  根据指定的SSL/TLS方法创建一个SSL/TSL上下文对象,该上下文对象包含了SSL/TSL连接的所有配置信息,包括加密算法、证书、私钥等。
    	  若创建失败返回NULL。
    	*/
    	SSL_CTX* pctxSSL = SSL_CTX_new(TLS_client_method());
    	if (pctxSSL == NULL) {
    		printf("error ssl_ctx_new\n");
    		return -1;
    	}
    	/*
    	基于之前创建的 SSL 上下文(SSL_CTX),创建了一个 SSL 对象,该对象将使用指定的上下文对象来配置 SSL/TLS 连接
    	*/
    	SSL* psslSSL = SSL_new(pctxSSL);
    	if (psslSSL == NULL) {
    		printf("error ssl_new\n");
    	}
    	/*SSL_set_fd()函数用于将一个套接字文件描述符与SSL对象关联起来,从而使SSL对象可以在该套接字上进行SSL/TSL加密通信。
    	*/
    	SSL_set_fd(psslSSL, socket_fd);
    	//使用之前创建的 SSL 结构(SSL),建立了 SSL 连接。
    	/*SSL_connect()函数尝试与远程服务器建立安全连接。iErrorConnect表示连接的状态,如果连接失败返回值小于零。
    	*/
    	INT iErrorConnect = SSL_connect(psslSSL);
    	if (iErrorConnect < 0) {
    		//打印错误码
    		printf("error SSL_connect,iErrorConnect=%d\n", iErrorConnect);
    		//打印当前SSL连接所使用的加密算法
    		/*SSL_get_cipher() 函数返回一个指向当前 SSL 连接所使用的加密算法字符串的指针。
    		*/
    	}
    	/*SSL_get_cipher() 函数返回一个指向当前 SSL 连接所使用的加密算法字符串的指针。
    	*/
    	printf("SSL connection using:%s\n", SSL_get_cipher(psslSSL));
    
    	//准备http请求
    	char http[] = "GET /index.html HTTP/1.1\r\nHost: 192.168.10.124:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nMozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\r\ntext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n";
    	
    	
    	//通过SSL连接发送http get请求,发送成功则返回发送的字节数,否则返回一个负值表示发送失败
    	/*SSL_write()函数用于在SSL连接上写入数据,将http中的数据写入到与psslSSL相关的SSL连接中
    	* 返回值小于零表示写入失败
    	*/
    	INT iErroeWrite = SSL_write(psslSSL,http,strlen(http));
    	if (iErroeWrite<0) {
    		printf("error SSL_write\n");
    		return -1;
    	}
    
    	char buf[2048] = {0};
    	/*
    	
    	*/
    	INT iLength = 1;
    	/*调用SSL_read函数从SSL连接中读取数据
    	第一个参数:SSL连接对象
    	第二个参数:指向接收缓冲区的指针
    	第三个参数:缓冲区的大小
    	函数返回读取到的字节数
    	*/
    	while ((iLength=SSL_read(psslSSL,buf, sizeof(buf)))>=1) {
    	}
    		
    	printf("读取数据成功\n");
    	printf("服务器端给出的回应是%s\n", buf);
    	//关闭SSL连接
    	SSL_shutdown(psslSSL);
    	//释放SSL数据结构
    	SSL_free(psslSSL);
    	//释放SSL_CTX数据结构
    	SSL_CTX_free(pctxSSL);
    	//关闭套接字
    	closesocket(socket_fd);
    	WSACleanup();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    参考链接

    1.c语言https实现客户端
    2.visual studio openssl环境配置
    3.https协议详解

  • 相关阅读:
    Java学习Day018(第四章数组笔记)
    PID控制算法学习笔记分享
    rocketmq-console-1.0.0启动报错
    Pycharm+服务器运行代码
    Java多线程之:队列同步器AbstractQueuedSynchronizer原理剖析
    Buuctf [ACTF2020 新生赛]BackupFile1 WP解析
    Linux中安装MySQL5.7.42
    1.7.3、计算机网络体系结构分层思想举例
    磁盘空间不够引发的控制录像程序崩溃到后端的解决方式
    注解实现策略模式
  • 原文地址:https://blog.csdn.net/qq_44398094/article/details/136743373