c++开启一个简单的http响应

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++开启一个简单的http响应相关的知识,希望对你有一定的参考价值。

参考技术A #include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib)

#define HTTP_PORT 8080

#define HTTP_BUFFER_SIZE 2048

#define HTTP_FILENAME_LEN   256

struct doc_type//定义文件类型



char *suffix;

char *type;

;

struct doc_type file_type[]=



  "html",    "text/html"  ,

    "gif",     "image/gif"  ,

    "jpeg",    "image/jpeg" ,

     NULL,      NULL        

;

/**************************************************************************

 *

 * 函数功能: 根据文件后缀查找对应的 Content-Type.

 *

 * 参数说明: [IN] suffix, 文件名后缀;

 *

 * 返 回 值: 成功返回文件对应的 Content-Type, 失败返回 NULL.

 *

 **************************************************************************/

c

char *http_get_type_by_suffix(const char*suffix)



struct doc_type *type;

for(type=file_type;type->suffix;type++)



if(strcmp(type->suffix,suffix)==0)

return type->type;



return null;



/**************************************************************************

 *

 * 函数功能: 解析请求行, 得到文件名及其后缀. 请求行格式:

 *           [GET http://www.baidu.com:8080/index.html HTTP/1.1]

 *

 * 参数说明: [IN]  buf, 字符串指针数组;

 *           [IN]  buflen, buf 的长度;

 *           [OUT] file_name, 文件名;

 *           [OUT] suffix, 文件名后缀;

 *

 * 返 回 值: void.

 *

 **************************************************************************/

void heet_parse_request_cmd(char *buffer,int buflen,char *filename,char *suffix)



int length=0;

char *begin,*end,*bias;

begin=strchr(buf,' ');

begin+=1;

end=strchr(begin,' ‘);

*end=0;

bias=strrchr(begin,'/');

length=end-bias;

if((*bias=='/')||(*bias=='\\'))



bias++;

length--;



/* 得到文件名 */

    if (length > 0)

    

        memcpy(file_name, bias, length);

        file_name[length] = 0;

        begin = strchr(file_name, '.');

        if (begin)

            strcpy(suffix, begin + 1);

    



/**************************************************************************

 *

 * 函数功能: 向客户端发送 HTTP 响应.

 *

 * 参数说明: [IN]  buf, 字符串指针数组;

 *           [IN]  buf_len, buf 的长度;

 *

 * 返 回 值: 成功返回非0, 失败返回0.

 *

 **************************************************************************/

int http_send_respone(SOCKER soc,char *buf,int buf_len)



int read_len,file_len,hdr_len,send_len;

char *type;

char read_buf[HTTP_BUF_SIZE];

char http_header[HTTP_BUF_SIZE];

char file_name[HTTP_FILENAME_LEN]="index.html",suffix[16]="html";

FILE *res_file;

  /* 得到文件名和后缀 */

    http_parse_request_cmd(buf, buf_len, file_name, suffix);

res_file = fopen(file_name, "rb+"); /* 用二进制格式打开文件 */

    if (res_file == NULL)

    

        printf("[Web] The file [%s] is not existed\n", file_name);

        return 0;

    

/* 构造 HTTP 首部,并发送 */

    hdr_len = sprintf(http_header, http_res_hdr_tmpl, file_len, type);

    send_len = send(soc, http_header, hdr_len, 0);

    if (send_len == SOCKET_ERROR)

    

        fclose(res_file);

        printf("[Web] Fail to send, error = %d\n", WSAGetLastError());

        return 0;

    

    do /* 发送文件, HTTP 的消息体 */

    

        read_len = fread(read_buf, sizeof(char), HTTP_BUF_SIZE, res_file);

        if (read_len > 0)

        

            send_len = send(soc, read_buf, read_len, 0);

            file_len -= read_len;

        

     while ((read_len > 0) && (file_len > 0));

    fclose(res_file);

    return 1;



Int main()



WSADATA  wsa_data;

SOCKET srv_soc,acpt_soc;

struct sockaddr_in serv_addr;

struct sockaddr_in from_addr;   /* 客户端地址  */

 char recv_buf[HTTP_BUF_SIZE];

  unsigned short port = HTTP_DEF_PORT;

  unsigned long from_len = sizeof(from_addr);

   int result = 0, recv_len;

 if (argc == 2) /* 端口号 */

        port = atoi(argv[1]);

WSAStartup(MAKEWORD(2,0), &wsa_data); /* 初始化 WinSock 资源 */

 srv_soc = socket(AF_INET, SOCK_STREAM, 0); /* 创建 socket */AF的意思是IP4 sock是使用sock流。0是不指定TCP还是udp。根据情况而定

 if (srv_soc == INVALID_SOCKET)

    

        printf("[Web] socket() Fails, error = %d\n", WSAGetLastError());

        return -1; 

    

 /* 服务器地址 */

    serv_addr.sin_family = AF_INET;

    serv_addr.sin_port = htons(port);TCP采用大端传输的方式。这个函数就是实现大端。高字节在低地址。

    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

将主机数转换成无符号长整型的网络 字节顺序 。本函数将一个32位数从主机字节顺序转换成网络字节顺序。

result = bind(srv_soc, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

bind函数把一个本地的协议地址赋予一个套接字,对于网际协议。协议地址是32位de ip4或者128位的ip6与16位的tcp或者udp端口的组合。

 if (result == SOCKET_ERROR) /* 绑定失败 */

    

        closesocket(srv_soc);

        printf("[Web] Fail to bind, error = %d\n", WSAGetLastError());

        return -1; 

    

 result = listen(srv_soc, SOMAXCONN);//SOMAXCONN限制系统中监听该端口的最大连接数。保存的是完成三次握手、等待accept的全连接,而不是半连接。

    printf("[Web] The server is running ... ...\n");

 while (1)

    

        acpt_soc = accept(srv_soc, (struct sockaddr *) &from_addr, &from_len);

        if (acpt_soc == INVALID_SOCKET) /* 接受失败 */

        

            printf("[Web] Fail to accept, error = %d\n", WSAGetLastError());

            break; 

        

        printf("[Web] Accepted address:[%s], port:[%d]\n", 

            inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port));

        recv_len = recv(acpt_soc, recv_buf, HTTP_BUF_SIZE, 0);

        if (recv_len == SOCKET_ERROR) /* 接收失败 */

        

            closesocket(acpt_soc);

            printf("[Web] Fail to recv, error = %d\n", WSAGetLastError());

            break; 

        

        recv_buf[recv_len] = 0;

        /* 向客户端发送响应数据 */

        result = http_send_response(acpt_soc, recv_buf, recv_len);

        closesocket(acpt_soc);

    

  closesocket(srv_soc);

    WSACleanup();

    printf("[Web] The server is stopped.\n");

    return 0;



https://blog.csdn.net/shixin_0125/article/details/42552895

C++ 套接字:当客户端调用 connect() 时,accept() 挂起,但 accept() 响应 HTTP GET 请求

【中文标题】C++ 套接字:当客户端调用 connect() 时,accept() 挂起,但 accept() 响应 HTTP GET 请求【英文标题】:C++ sockets: accept() hangs when client calls connect(), but accept() responds to HTTP GET request 【发布时间】:2022-01-17 11:17:37 【问题描述】:

我正在尝试用 C++ 编写一个演示服务器/客户端程序。我首先在我的 Macbook 上运行我的服务器程序,打开 ngrok 并将 Internet 上的公共地址转发到我机器上的本地地址。当我尝试运行我的客户端程序以连接到服务器时,我看到了一些我不理解的东西:

    如果客户端尝试连接到服务器程序中定义的本地端口的localhost,则服务器按预期接受并且客户端成功连接, 如果客户端尝试连接到 ngrok 服务器地址的 80 端口(默认为 ngrok),则客户端连接,但服务器在接受调用时仍被阻止。 (这个我不明白!) 如果我向 ngrok 服务器地址发送 HTTP GET 请求,则服务器成功接受连接。

为什么我会看到这些?在理想情况下,我希望我的服务器接受来自我的客户端程序的连接,而不仅仅是响应 HTTP GET 请求。

如果有帮助,这是我的代码:对于客户,

#include "helpers.hh"
#include <cstdio>
#include <netdb.h>

// usage: -h [host] -p [port]
int main(int argc, char** argv) 
    const char* host = "x.xx.xx.xx"; // use the server's ip here.
    const char* port = "80";

    // parse arguments
    int opt;
    while ((opt = getopt(argc, argv, "h:p:")) >= 0) 
        if (opt == 'h') 
            host = optarg;
         else if (opt == 'p') 
            port = optarg;
        
    

    // look up host and port
    struct addrinfo hints, *ais;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;        // use IPv4 or IPv6
    hints.ai_socktype = SOCK_STREAM;    // use TCP
    hints.ai_flags = AI_NUMERICSERV;
    if (strcmp(host, "ngrok") == 0) 
        host = "xxxx-xxxx-xxxx-1011-2006-00-27b9.ngrok.io";
    
    int r = getaddrinfo(host, port, &hints, &ais);
    if (r != 0) 
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
        exit(1);
    

    // connect to server
    int fd = -1;
    for (auto ai = ais; ai && fd < 0; ai = ai->ai_next) 
        fd = socket(ai->ai_family, ai->ai_socktype, 0);
        if (fd < 0) 
            perror("socket");
            exit(1);
        

        r = connect(fd, ai->ai_addr, ai->ai_addrlen);
        if (r < 0) 
            close(fd);
            fd = -1;
        
    
    if (fd < 0) 
        perror("connect");
        exit(1);
    
    freeaddrinfo(ais);

    // 
    printf("Connection established at fd %d\n", fd);
    FILE* f = fdopen(fd, "a+");
    fwrite("!", 1, 1, f);
    fclose(f);
    while (true) 
    
    

对于服务器:

#include "helpers.hh"

void handle_connection(int cfd, std::string remote) 
    (void) remote;
    printf("Received incoming connection at cfd: %d\n", cfd);
    usleep(1000000);
    printf("Exiting\n");



int main(int argc, char** argv) 
    int port = 6162;
    if (argc >= 2) 
        port = strtol(argv[1], nullptr, 0);
        assert(port > 0 && port <= 65535);
    

    // Prepare listening socket
    int fd = open_listen_socket(port);
    assert(fd >= 0);
    fprintf(stderr, "Listening on port %d...\n", port);

    while (true) 
        struct sockaddr addr;
        socklen_t addrlen = sizeof(addr);

        // Accept connection on listening socket
        int cfd = accept(fd, &addr, &addrlen);
        if (cfd < 0) 
            perror("accept");
            exit(1);
        

        // Handle connection
        handle_connection(cfd, unparse_sockaddr(&addr, addrlen));
    

【问题讨论】:

老实说,我认为问题在于我的客户端程序从未真正能够正确定位 ngrok 生成的地址。在我的代码中,我试图让客户端程序使用 getaddrinfo 设置主机和端口,而这一次客户端甚至没有连接就卡住了。也许有人应该告诉我如何正确使用 ngrok... 【参考方案1】:

与在本地路由器中完成的典型端口转发相反,ngrok 不是传输级别 (TCP) 的端口转发器,而是 HTTP 级别的请求转发器。

因此,如果客户端通过 TCP 连接到外部 ngrok 服务器,则不会转发任何内容。只有在客户端发送 HTTP 请求后,目的地才会被确定,然后这个请求将被发送到内部机器上的 ngrok 连接器,然后它会启动到内部服务器的连接并转发请求。

【讨论】:

该死!谢谢,这实际上很有意义。抱歉,我对这些概念还是很陌生,因为我实际上是在参加系统编程课程而不是网络课程之后编写这些代码......所以,根据你的回答,我可以在连接后发送 HTTP 请求() 从客户端?也许用一些允许 TCP 级别转发的工具替换 ngrok 对我来说可能更容易?我可以在这里使用什么来使我的代码在本地网络之外工作? - 呃,也许我应该打开一个新问题或在线搜索后一个问题。 顺便说一下,我用的是大学网络,所以恐怕我无法篡改这里的路由器:( @Macrophage:例如,请参阅Poor man's ngrok with tcp proxy and ssh reverse tunnel,了解当您拥有具有 SSH 访问权限的外部服务器时如何进行 TCP 级别转发。

以上是关于c++开启一个简单的http响应的主要内容,如果未能解决你的问题,请参考以下文章

简单讲解一下 http2 的多路复用

简单讲解一下 http2 的多路复用

自主HttpServer实现(C++实战项目)

Beginner:Client libraries-5-实现一个简单的服务和客户端(c++)

Node学习之(第二章:http模块)

一个linux下简单的纯C++实现Http请求类(GET,POST,上传,下载)