用于简单网络服务器的 c 自定义 netcat

c custom netcat for simple webserver

我正在尝试使用 c 和 bash 复制 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) 时,它只是抛出一个错误 Unable to connect.

问题在于从 bash 发送数据。如果我在 customnc.c 中使用 write 发送响应,浏览器将显示该消息,但我必须从 bash.

发送它

编辑: 真正的问题是我想在 bash 中解析 http 请求并使用 bash 将文件发送到 c。所以它是这样的: 1. Bash 启动c服务器 2.用户在浏览器中打开localhost 3. C 将收到请求并将其发送回 bash 4. Bash 将解析请求并将响应(文本)发送到 c 5、C会收到response,通过socket发送给浏览器 6. C 将关闭套接字

我正在尝试实现此功能,但我无法进行这种双向通信。我可以使用命名管道 (2) 来实现吗?

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

之前添加它
char c;
while ((c = getchar()) != EOF) {
    write(new_socket, &c, 1);
}

这是我的完整代码,我还添加了更多错误检查,添加了对 fflush(stdout) 的调用,删除了 printf 调用,这样它们就不会被 bash 解析脚本,删除了对 malloc 的不必要调用,并在套接字上设置 SO_REUSEADDR 以便可以快速重用(参见 this answer for more info)。

#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。