[toc]

用C语言在windows中使用https协议浏览网页

  • 安装openssl库
  • 在使用OpenSSL的静态链接库时,除了添加 libcrypto.lib;libssl.lib;,还需要添加系统的依赖库:crypt32.lib;WS2_32.lib;

1.用C语言在windows中使用https协议浏览https://www.bilibili.com/

WinClient.c

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// WinClient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "crypt32.lib")
#pragma warning(disable:4996)

#define BUFFER_SIZE 4096

void send_request(SSL* ssl, const char* host, const char* path) {
char request[BUFFER_SIZE];
sprintf(request, "GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: close\r\n\r\n", path, host);
SSL_write(ssl, request, strlen(request));
}

int main() {
SetConsoleOutputCP(CP_UTF8);
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in server;
char response[BUFFER_SIZE];
int bytes_received;

// 初始化WinSock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("Failed to initialize WinSock. Error Code: %d\n", WSAGetLastError());
return 1;
}

// 创建套接字
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
printf("Could not create socket. Error Code: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}

// 设置服务器信息
server.sin_family = AF_INET;
server.sin_port = htons(443); // HTTPS端口

// 将主机名转换为IP地址
struct hostent* host = gethostbyname("www.bilibili.com");
if (host == NULL) {
printf("Could not resolve hostname. Error Code: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
memcpy(&server.sin_addr, host->h_addr, host->h_length);

// 连接到服务器
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
printf("Connect failed. Error Code: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}

// 初始化OpenSSL
SSL_library_init();
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);

// 建立SSL连接
if (SSL_connect(ssl) <= 0) {
printf("SSL connect failed. Error Code: %d\n", ERR_get_error());
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
WSACleanup();
return 1;
}

// 发送初始请求
send_request(ssl, "www.bilibili.com", "/");

// 接收响应并处理重定向
while ((bytes_received = SSL_read(ssl, response, sizeof(response) - 1)) > 0) {
response[bytes_received] = '\0'; // 添加字符串结束符
printf("%s", response); // 打印响应

// 检查是否有重定向
if (strstr(response, "HTTP/1.1 301 Moved Permanently") ||
strstr(response, "HTTP/1.1 302 Found")) {
// 提取Location头
char* location = strstr(response, "Location: ");
if (location) {
location += strlen("Location: ");
char* end = strstr(location, "\r\n");
if (end) {
*end = '\0'; // 结束字符串
printf("\nRedirecting to: %s\n", location);

// 关闭当前SSL连接
SSL_shutdown(ssl);
SSL_free(ssl);
closesocket(sock);

// 重新连接到新的URL
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
printf("Could not create socket. Error Code: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}

// 解析新的主机名
char* new_host = location; // 这里假设Location是完整的URL
char* path = strchr(new_host, '/'); // 找到路径
if (path) {
*path = '\0'; // 分离主机名
path++; // 跳过'/'
}

// 连接到新的主机
host = gethostbyname(new_host);
if (host == NULL) {
printf("Could not resolve hostname. Error Code: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
memcpy(&server.sin_addr, host->h_addr, host->h_length);
server.sin_port = htons(443); // HTTPS端口

// 连接到新的服务器
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
printf("Connect failed. Error Code: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}

// 初始化新的SSL连接
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
if (SSL_connect(ssl) <= 0) {
printf("SSL connect failed. Error Code: %d\n", ERR_get_error());
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
WSACleanup();
return 1;
}

// 发送请求到新的URL
send_request(ssl, new_host, path);
}
}
break; // 处理完重定向后退出循环
}
}

// 关闭SSL连接和套接字
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
WSACleanup();
return 0;
}

2. 用C语言与openssl在windows中使用https协议浏览https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1

WinClient.c

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h> // 用于设置控制台编码
#include <openssl/ssl.h>
#include <openssl/err.h>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "crypt32.lib")
// 初始化OpenSSL
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}

// 清理OpenSSL
void cleanup_openssl() {
EVP_cleanup();
}

// 创建SSL上下文
SSL_CTX* create_context() {
const SSL_METHOD* method;
SSL_CTX* ctx;

method = SSLv23_client_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}

// 配置SSL上下文
void configure_context(SSL_CTX* ctx) {
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
}

// 设置控制台为UTF-8编码
void set_console_utf8() {
SetConsoleOutputCP(CP_UTF8);
}

// 读取HTTP响应
void read_response(BIO* web) {
char buffer[4096];
int bytes_read;

// 逐块读取响应
while ((bytes_read = BIO_read(web, buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytes_read] = '\0'; // 确保字符串以null结尾
printf("%s", buffer);
}

if (bytes_read < 0) {
fprintf(stderr, "Error reading response\n");
ERR_print_errors_fp(stderr);
}
}

// 发送HTTP请求
void send_http_request(BIO* web) {
BIO_puts(web, "GET /?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1 HTTP/1.1\r\n");
BIO_puts(web, "Host: duckduckgo.com\r\n");
BIO_puts(web, "Connection: close\r\n\r\n");
}

// 主函数
int main() {
// 设置控制台为UTF-8编码
set_console_utf8();

init_openssl();
SSL_CTX* ctx = create_context();
configure_context(ctx);

// 创建新的SSL连接
SSL* ssl;
BIO* web = BIO_new_ssl_connect(ctx);
BIO_get_ssl(web, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

// 设置连接到DuckDuckGo
BIO_set_conn_hostname(web, "duckduckgo.com:443");

// 尝试连接
if (BIO_do_connect(web) <= 0) {
fprintf(stderr, "Error connecting to server\n");
ERR_print_errors_fp(stderr);
BIO_free_all(web);
SSL_CTX_free(ctx);
cleanup_openssl();
return EXIT_FAILURE;
}

// 发送HTTP请求
send_http_request(web);

// 读取响应
read_response(web);

// 清理
BIO_free_all(web);
SSL_CTX_free(ctx);
cleanup_openssl();

return EXIT_SUCCESS;
}