用于简单 Web 服务器的 C 自定义 Netcat

4
我正在尝试使用C和Bash创建一个用于Web服务器目的的Netcat副本。以下是我的Bash代码:

#!/bin/bash

rm -f out
mkfifo out
trap "rm -f out" EXIT

cat out | nc -l 1500 > >(
    while read line
    do
       echo $line
       # parse http request from line
       # some other things that doesnt matter

       printf "HTTP/1.1 200 OK\nContent-Type:text/html\n\ntest" > out
    done


)

当我访问localhost:1500时,会打印出“test”。我想用自己的替换netcat(nc)。
cat out | customnc -l 1500 > >(
 #
)

这是customnc.c文件。
#include<netinet/in.h>    
#include<stdio.h>    
#include<stdlib.h>    
#include<sys/socket.h>    
#include<sys/stat.h>    
#include<sys/types.h>    
#include<unistd.h>    

int main() {    
   int create_socket, new_socket;    
   socklen_t addrlen;    
   int bufsize = 1024;    
   char *buffer = malloc(bufsize);    
   struct sockaddr_in address;    

   if ((create_socket = socket(AF_INET, SOCK_STREAM, 0)) > 0){    
      printf("The socket was created\n");
   }

   address.sin_family = AF_INET;    
   address.sin_addr.s_addr = INADDR_ANY;    
   address.sin_port = htons(1600);    

   if (bind(create_socket, (struct sockaddr *) &address, sizeof(address)) == 0){    
      printf("Binding Socket\n");
   }


      if (listen(create_socket, 10) < 0) {    
         perror("server: listen");    
         exit(1);    
      }    

      if ((new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen)) < 0) {    
         perror("server: accept");    
         exit(1);    
      }    

      if (new_socket > 0){    
         printf("The Client is connected...\n");
      }

      recv(new_socket, buffer, bufsize, 0);    
      printf("%s", buffer);    
      close(new_socket);    

   close(create_socket);    
   return 0;    
}

我的代码有什么问题?当我访问localhost:1600(customnc)时,它只会抛出一个“无法连接”的错误。
问题在于从bash发送数据。如果我使用write在customnc.c中发送响应,浏览器将显示消息,但我必须从bash发送它。
编辑:真正的问题是我想在bash中解析http请求并使用bash将文件发送到c。所以它的流程如下: 1. Bash启动c服务器 2. 用户在浏览器中打开localhost 3. C将获取请求并将其发送回bash 4. Bash将解析请求并将响应(文本)发送到c 5. C将接收响应并通过套接字将其发送到浏览器 6. C将关闭套接字
我正在尝试实现这个过程,但我无法进行双向通信。我可以使用命名管道(2)吗?

也许你关闭了套接字太快了? - Bjorn A.
我在关闭套接字之前添加了sleep(4),但没有起到作用。 - dontHaveName
已添加。只有一个按行读取的循环。 - dontHaveName
你发布的Bash代码无法工作。while read line 循环会一直等待 nc 终止。 - Tim
进一步澄清,你只能打印到 out 先进先出管道 一次,因为当 cat out 读取到 EOF 时将终止。 - Tim
显示剩余6条评论
1个回答

2

在向套接字写入任何内容之前,您正在关闭套接字。您需要从 stdin 读取数据并将其写入套接字,然后再关闭。尝试在调用 close(new_socket); 之前添加以下内容:

char c;
while ((c = getchar()) != EOF) {
    write(new_socket, &c, 1);
}

这是我的完整代码,我还添加了更多的错误检查,添加了对fflush(stdout)的调用,删除了printf调用,以免被bash脚本解析,删除了不必要的malloc调用,并在套接字上设置SO_REUSEADDR,以便它可以快速重用(有关更多信息,请参见此答案)。

#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define BUFSIZE 1024
int main()
{
    int create_socket, new_socket;
    socklen_t addrlen;
    char buffer[BUFSIZE];
    struct sockaddr_in address;
    char c;
    int true = 1;

    if ((create_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("server: socket");
        exit(1);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(1600);

    setsockopt(create_socket,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int));

    if (bind(create_socket, (struct sockaddr *)&address, sizeof(address)) < 0)
    {
        perror("server: bind");
        exit(1);
    }
    if (listen(create_socket, 10) < 0)
    {
        perror("server: listen");
        exit(1);
    }
    if ((new_socket = accept(create_socket, (struct sockaddr *)&address, &addrlen)) < 0)
    {
        perror("server: accept");
        exit(1);
    }
    if (recv(new_socket, buffer, BUFSIZE, 0) < 0)
    {
        perror("server: recv");
        exit(1);
    }
    printf("%s", buffer);
    fflush(stdout);

    while ((c = getchar()) != EOF)
    {
        if (write(new_socket, &c, 1) < 0)
        {
            perror("server: write");
            exit(1);
        }
    }
    close(new_socket);
    close(create_socket);
    return 0;
}

这可以通过以下的bash脚本实现。请注意,我添加了一个检查空行的步骤,以表示GET请求已结束。
#!/bin/bash
rm -f out
mkfifo out
trap "rm -f out" EXIT

cat out | ./a.out > >(
    while read line
    do
       # strip carriage return
       line=$(echo $line | tr -d '\r')
       echo $line
       if [[ $line == "" ]]; then
           # reached end of GET request
           break
       fi
    done

    printf "HTTP/1.1 200 OK\nContent-Type:text/html\n\ntest" > out
)

如果您想完全替换nc命令,则需要进行一些参数解析以获取端口号(-l),而不是将其硬编码为1600。


它在浏览器中显示“test”,但现在我无法解析bash中的请求,因为它没有接收到任何消息,所以对我没有用。你解决了一件事,但却破坏了另一件事。 - dontHaveName

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接