socket编程进行http/https通信

Aki 发布于 2023-10-24 186 次阅读


HTTP

#include<iostream>
#include<sys/socket.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstring>
#include<ctype.h>
#include<string>
#include<netdb.h>
using namespace std;

//ip地址
string IP;

//域名,主机名称
string HOST = "hcl.baidu.com";

//请求资源路径
string PATH = "index.html";

//端口
const u_short PORT = 80;

//错误处理
void error(const char* msg) noexcept
{
        cout << msg << endl;
}

int main(int args,char* argv[],char* envp[])
{

        //通过主机名解析ip地址
        hostent* host = gethostbyname(HOST.c_str());
        if (host == nullptr)
        {
                error("get host infomation failed !");
        }

        //解析获取一个主机名对应的ip地址
        in_addr** addr_list = reinterpret_cast<in_addr**>(host->h_addr_list);
        for (size_t i = 0; addr_list[i] != nullptr; ++i) 
        {
                //把网络字节序转化为可识别的字节序列
                IP = inet_ntoa(*addr_list[i]);
                break;
        }

        if(IP.size() == 0)
        {
                error("解析ip地址错误 !");
        }

        //http请求头
        string request = "GET /" + PATH + " HTTP/1.1\r\nHost: " + HOST + "\r\nConnect: close\r\n\r\n";

        //创建套接字
        int server = socket(AF_INET,SOCK_STREAM,0);
        if(server < 0)
        {
                error("create socket failed !");
        }

        //为套接字绑定信息
        sockaddr_in addr;
        memset(&addr,0,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT);
        if(inet_pton(AF_INET,IP.c_str(),&addr.sin_addr) < 0)
        {
                error("translate ip address failed !");
        }

        //主动发起tcp连接
        if(connect(server,(sockaddr*)&addr,sizeof(addr)) == 0)
        {
                int res = send(server,request.c_str(),request.size(),0);

                if(res < 0)
                {
                        error("send erro !");
                }

                //缓冲区
                char buffer[2048];
                bzero(buffer,sizeof(buffer));
                //recv接收来自socket缓冲区的数据,当缓冲区没有数据可取时,recv会一直处于阻塞状态,直到缓冲区至少又一个字节数据可读取,或者对端关闭,并读取所有数据后返回.
                //网络协议规定一次传输最大字节为1500字节
                //可能缓冲器中存放着超过1500字节的数据,需要循环读取直到读取全部数据
                while(true)
                {
                        res = recv(server,buffer,sizeof(buffer) - 1,0);
                        if(res > 0)
                        {
                                buffer[res] = '\0';
                                cout << buffer;
                        }
                        else
                        {
                                break;
                        }
                }
        }
        else
        {
                error("connect failed !");
        }

        close(server);

        return 0;
}
CC = g++
CFLAG = -w -std=c++2a -O2 -lpthread 

FILES = ./main 

all : $(FILES)

clean :
        rm -f $(FILES)

main : main.cpp
        $(CC) main.cpp -o main $(CFLAG)

HTTPS

#include <netdb.h>
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
using namespace std;


//ip地址
string IP;

//主机名,域名
string HOST = "hcl.baidu.com";

//端口号
const u_short PORT = 443;

//GET请求文件路径
const string PATH = "index.html";

//错误处理
void error(const char* msg) noexcept
{
        cout << msg << endl;
}

//1.创建套接字,为套接字绑定信息
//2.和服务器建立tcp连接
//3.初始化SSL库,将ssl和套接字绑定在一起
//4.进行tls握手
//5.使用ssl库的send和recv函数进行发送和接受消息


int main() 
{

        //通过主机名解析ip地址
        hostent* host = gethostbyname(HOST.c_str());
        if (host == nullptr)
        {
                error("get host infomation failed !");
        }

        //解析获取一个主机名对应的ip地址
        in_addr** addr_list = reinterpret_cast<in_addr**>(host->h_addr_list);
        for (size_t i = 0; addr_list[i] != nullptr; ++i) 
        {
                //把网络字节序转化为可识别的字节序列
                IP = inet_ntoa(*addr_list[i]);
                break;
        }

        // 初始化OpenSSL
        SSL_library_init();
        ERR_load_BIO_strings();
        SSL_load_error_strings();

        // 创建SSL上下文
        SSL_CTX* ctx = SSL_CTX_new(TLSv1_2_client_method());
        if(!ctx)
        {
                error("create SSL_CTX failed !");
        }

        // 创建TCP套接字
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd < 0)
        {
                error("create socket failed !");
        }

        // 设置服务器地址,协议,端口信息
        sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(PORT);
        if(inet_pton(AF_INET, IP.c_str(), &(server_addr.sin_addr)) < 0)
        {
                error("translate ip address failed !");
        }

        // 连接服务器,首先建立tcp链接
        if(connect(sockfd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0)
        {
                error("connect server failed !");
        }

        // 创建SSL对象
        SSL* ssl = SSL_new(ctx);
        if(!ssl)
        {
                error("create SSL* failed !");
        }

        //将ssl对象和套接字绑定
        if(SSL_set_fd(ssl, sockfd) == 0)
        {
                error("failed to bind socket with ssl !");
        }

        // 然后建立SSL连接
        if(SSL_connect(ssl) <= 0)
        {
                error("failed to build ssl connect !");
        }

        // 发送HTTPS请求
        string request = "GET /" + PATH + " HTTP/1.1\r\nHost: " + HOST + "\r\nConnection: close\r\n\r\n";
        int len = SSL_write(ssl, request.c_str(), request.size() + 1);
        if(len <= 0)
        {
                error("send failed !");
        }

        // 接收并打印服务器https响应
        string file;
        char buffer[4096];
        memset(buffer,0,sizeof(buffer));
        while ((len = SSL_read(ssl, buffer, sizeof(buffer) - 1)) > 0) 
        {
                buffer[len] = '\0';
                file += buffer;
        }

        cout << file;

        // 关闭连接
        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(sockfd);
        SSL_CTX_free(ctx);

        return 0;
}
CC = g++
CFLAG = -w -std=c++2a -O2 -lpthread -lssl -lcrypto 

FILES = ./main 

all : $(FILES)

clean :
        rm -f $(FILES)

main : main.cpp
        $(CC) main.cpp -o main $(CFLAG)