c自定义netcat用于简单的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替换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)吗?

最佳答案 您在写入任何内容之前关闭套接字.您需要从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.

点赞