8

我正在使用类似的东西来使用 C 创建一个服务器。当我127.0.0.1:5000从浏览器访问时,我可以看到"Hello Worlds"我正在将它作为缓冲区发送。但我想127.0.0.1:5000/filename.html工作。但我不知道如何在 Cfilename之后得到它。127.0.0.1:5000

我正在使用它来建立连接:

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(5000);

  bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr));

  connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL);
4

3 回答 3

6

浏览器将向您的服务器发送一个HTTP 请求,其中包含它所追求的 URL。请求可能如下所示:

GET /filename.html HTTP/1.1
Host: 127.0.0.1:5000

您的 C 程序必须从套接字读取此请求并对其进行解析以找到 URL。请注意,请求可能包含比上述更多的信息,但它应该始终以空行结尾(这样您就知道在哪里停止解析)。HTTP 请求中的行应以回车符和换行符 ( "\r\n") 结尾。

您通过用于发送数据的同一个套接字接收数据。读取 HTTP 请求的步骤可能是这样的:

  1. 声明一个足够大的缓冲区,可能是 4096 字节或更多。

  2. read使用您的connfd直到将数据读入此缓冲区:

    1. 您已收到 4095 个字节(在这种情况下,您的服务器应以错误 413 响应)

    2. 您遇到了字符"\r\n\r\n"(这表示空行)

    3. 一段时间过去了,以上都没有发生。为了实现超时,您需要使用select()or poll()

  3. 将 HTTP 请求接收到缓冲区后,解析它:

    1. 第一行是请求行,它规定了请求的方法、URI 和协议版本号。解析此行的一种可能方法是按空格分割它。

    2. 后续行表示 HTTP 标头字段,通常可以解析为Key: Value\r\n. 这些标头字段包含 cookie、有关发出请求的客户端的信息等。

  4. 您还需要形成 HTTP响应。URI 指定有效资源(例如filename.html)时的响应可能是:

    HTTP/1.1 200 OK
    Date: Thu, 25 Jul 2013 03:55:00 GMT
    Server: sadaf2605-server/1.0
    Content-Type: text/html
    Content-Length: 40595
    
    < contents of filename.html follows here >
    

    在上面,Content-Length是指filename.html文件中的字节数。就像请求一样,响应与数据使用空行分隔。

于 2013-07-25T06:41:33.877 回答
1

当以正确的状态接收到数据时,尝试将其解析为 http 请求。在解析任何内容之前等待 CRLF CRLF(指示 http 标头的结尾),

#define CRLF "\r\n"

那么您应该使用在字符串中搜索字符串

strnstr(data,CRLF CRLF,data_len)

然后url就在旁边,做+1,你会发现那里。

于 2013-07-25T07:26:12.653 回答
0

我发现评分最高的答案有助于理解概念上需要做什么,但它不能帮助新的 C/C++ 开发人员理解如何阅读标题。

诀窍是要意识到,您可以通过 Google 找到的大多数 TCP 服务器示例都没有向读者展示如何实际接收请求!您需要使用该recv方法并将请求读入您可以解析的内容。在下面的示例中,我将其读入vector<char>被调用的but(简称buffer)并使用buf.data()来访问底层 char 数组以打印到控制台。

假设您有一个新的客户端套接字...

listen(sock, 5);
while (1) {

    // Make a new socket for the client that just tried to connect
    client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);

    char buffer[1024] = {0};
    int server_fd, new_socket, valread;
    valread = read(sock , buffer, 1024);
    std::cout << buffer << std::endl;

    printf("got connection\n");

    // Handle a case where you can't accept the request
    if (client_fd == -1) {
        perror("Can't accept");
        continue;
    }

    // Recieve data from the new socket that we made for the client
    // We are going to read the header into the vector<char>, and then
    // you can implement a method to parse the header.
    vector<char> buf(5000); // you are using C++ not C
    int bytes = recv(client_fd, buf.data(), buf.size(), 0);
    std::cout << bytes << std::endl;

    std::cout << sizeof(buf);
    std::cout << buf.data() << buf[0] << std::endl;

要阅读有关套接字 API 的更多信息,Wikipedia 文章是一个非常好的资源。 https://en.wikipedia.org/wiki/Berkeley_sockets

于 2019-12-23T22:11:50.367 回答