• 用IO多路复用实现 nginx 静态资源代理(C/Java/Golang)


    用IO多路复用实现 nginx 静态资源代理(C/Java/Golang)

    效果展示

    代理 HTML

    代理 HTML
    代理 HTML

    代理图片

    代理图片
    代理图片

    注意, 静态资源代理基于 HTTP, 可以了解上一篇文章: 几十行代码使用TCP简单实现HTTP(C/Golang/Java) https://blog.csdn.net/jarvan5/article/details/117601456

    Java

    public static void main(String[] args) throws IOException {
        String basePath = "D:\\Enviroment\\java\\java_static_server";
        int port = 9000;
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("port:"+port);
        while (true) {
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            String reqUrl = getReqUrl(inputStream);
            String[] split = reqUrl.split("/");
            //1.header
            writeHeader(outputStream,split[split.length-1]);
            //2.data
            Path path = Paths.get(basePath,reqUrl);
            try {
                Files.copy(path, outputStream);
            } catch (IOException ioException) {
                System.out.println("FileNotFound:"+path);
                continue;
            }
            outputStream.close();
            socket.close();
        }
    }

    private static String getReqUrl(InputStream inputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String line = reader.readLine();
        String[] strs = line.split(" ");
        //GET /index.html HTTP/1.1...
        return strs[1];
    }
    private static void writeHeader(OutputStream outputStream,String contentType) throws IOException {
        StringBuilder respBuf = new StringBuilder();
        respBuf.append("HTTP/1.1 200 OK\n");
        respBuf.append("Content-Type:");
        respBuf.append(contentType);
        respBuf.append(";charset=UTF-8\n\n");
        outputStream.write(respBuf.toString().getBytes());
        outputStream.flush();
    }
    • 1

    Java IO多路复用

    public static void main(String[] args) throws IOException {
         String basePath = "D:\\Enviroment\\java\\java_static_server";
         int port = 9001;
         System.out.println("port:" + port);
         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
         Selector selector = Selector.open();
         serverSocketChannel.socket().bind(new InetSocketAddress(port));
         serverSocketChannel.configureBlocking(false);
         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
         while (true) {
             if (selector.select(1000) == 0) {
                 System.out.print(".");
                 continue;
             }
             Set selectionKeys = selector.selectedKeys();
             Iterator keyIterator = selectionKeys.iterator();
             while (keyIterator.hasNext()) {
                 try {
                     SelectionKey key = keyIterator.next();
                     keyIterator.remove();
                     if (key.isAcceptable() && !key.isReadable()) {
                         SocketChannel channel = serverSocketChannel.accept();
                         channel.configureBlocking(false);
                         channel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                     }
                     if (key.isReadable()) {
                         SocketChannel channel = (SocketChannel) key.channel();
                         byte[] readBuf = new byte[1024];
                         channel.read(ByteBuffer.wrap(readBuf));
                         String readString = new String(readBuf);
                         String[] splits = readString.split(" ");
                         String reqUrl = splits[1];
                         String[] po = reqUrl.split("/");
                         System.out.println("客户端发送数据:" + reqUrl);
                         //1.header
                         StringBuilder sb = new StringBuilder();
                         sb.append("HTTP/1.1 200 OK\n");
                         sb.append("Content-Type:");
                         sb.append(po[po.length - 1]);
                         sb.append(";charset=UTF-8\n\n");
                         channel.write(ByteBuffer.wrap(sb.toString().getBytes()));
                         //2.data
                         FileInputStream fi;
                         fi = new FileInputStream(basePath + reqUrl);

                         int len = 0;
                         byte[] buf = new byte[1024];
                         while ((len = fi.read(buf)) > 0) {
                             channel.write(ByteBuffer.wrap(buf));
                         }
                         key.cancel();
                         channel.close();
                     }
                 }catch(Exception e){
                     continue;
                 }
             }
         }
     }
    • 1

    C

    #include
    #include
    #include
    #include
    #include
    #include "stdlib.h"
    #define PORT 9000
    #define BASE_PATH "/root/CLionProjects/c_static_server"
    #define BUF_SIZE 1024
    #define NOT_FOUND_DATA "NOT_FOUND"
    char buff[BUF_SIZE];
    char *get_param_form_request(char *request){
        char *s = strstr(request, "/");
        char *s2 = strstr(s, " ");
        int size = strlen(s) - strlen(s2);
        char *result = malloc(sizeof(char*) * size);
        strncpy(result, s, size);
        return result;
    }
    char *str_join(char* str1,char* str2){
        char *result = malloc(sizeof(char *) * (strlen(str1) + strlen(str2)));
        strcat(result, str1);
        strcat(result, str2);
        return result;
    }
    int send_header_with_content_type(int client_socket_fd,char *content_type){
        sprintf(buff, "HTTP/1.1 200 OK\r\nContent-Type:%s:charset=UTF-8\r\n\r\n", content_type);
        int send_n = send(client_socket_fd, buff, strlen(buff), 0);
        memset(buff, 0strlen(buff));
        if (send_n < 0) {
            return  -1;
        }
        return 0;
    }
    //socket -> bind -> listen
    int main(int argc, char *argv[]) {
        int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sockfd < 0) {
            perror("socket error\n");
            return -1;
        }
        struct sockaddr_in lst_addr;
        lst_addr.sin_family = AF_INET;
        lst_addr.sin_port = htons(PORT);
        lst_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        socklen_t len = sizeof(struct sockaddr_in);
        int ret = bind(sockfd, (struct sockaddr *) &lst_addr, len);
        if (ret < 0) {
            perror("bind error\n");
            return -1;
        }
        if (listen(sockfd, 5) < 0) {
            perror("listen error\n");
            return -1;
        }
        while (1) {
            struct sockaddr_in cli_addr;
            int client_accept_fd = accept(sockfd, (struct sockaddr *) &cli_addr, &len);
            if (client_accept_fd < 0) {
                perror("accept error\n");
                continue;
            }
            int ret = recv(client_accept_fd, buff, BUF_SIZE, 0);
            if (ret > 0) {
                printf("req:%s\n", buff);
            }
            char *param = get_param_form_request(buff);
            memset(buff, 0x00, BUF_SIZE);
            char *path = str_join(BASE_PATH, param);
            if (access(path, F_OK|R_OK)==0) {
                //header.
                char *post_fix = strstr(param, ".");
                post_fix++;
                send_header_with_content_type(client_accept_fd, post_fix);
                //data.
                FILE *p = fopen(path, "rb");
                int read2_n;
                while ((read2_n = fread(buff, sizeof(char), BUF_SIZE, p)) != 0) {
                    send(client_accept_fd, buff, read2_n, 0);
                    memset(buff, 0, read2_n);
                }
                fclose(p);
            } else{
                //header
                send_header_with_content_type(client_accept_fd, "html");
                //data
                send(client_accept_fd, NOT_FOUND_DATA, strlen(NOT_FOUND_DATA), 0);
            }
            send(client_accept_fd, buff, strlen(buff), 0);
            close(client_accept_fd);
        }
    }
    • 1

    C IO多路复用

    #include 
    #include 
    #include 
    #include 

    #include 
    #include 
    #include 
    #include 

    #include 

    #define EPOLL_MAX_NUM 2048
    #define BUF_SIZE 4096
    #define LISTEN_N 10
    char buffer[BUF_SIZE];
    char *get_param_form_request(char *request){
        char *s = strstr(request, "/");
        char *s2 = strstr(s, " ");
        int size = strlen(s) - strlen(s2);
        char *result = malloc(sizeof(char*) * size);
        strncpy(result, s, size);
        return result;
    }
    char *str_join(char* str1,char* str2){
        char *result = malloc(sizeof(char *) * (strlen(str1) + strlen(str2)));
        strcat(result, str1);
        strcat(result, str2);
        return result;
    }
    int send_header_with_content_type(int client_socket_fd,char *content_type){
        sprintf(buffer, "HTTP/1.1 200 OK\r\nContent-Type:%s:charset=UTF-8\r\n\r\n", content_type);
        int send_n = send(client_socket_fd, buffer, strlen(buffer), 0);
        memset(buffer, 0strlen(buffer));
        if (send_n < 0) {
            return  -1;
        }
        return 0;
    }
    int main(int argc, char **argv) {
        char *base_path = "/root/CLionProjects/c_static_server";
        int port = 9000;
        printf("base_path:%s\nport:%d\n", base_path, port);
        int listen_fd = 0;
        int client_fd = 0;
        struct sockaddr_in server_addr;
        struct sockaddr_in client_addr;
        socklen_t client_len;
        int epfd = 0;
        struct epoll_event event, *my_events;
        listen_fd = socket(AF_INET, SOCK_STREAM, 0);
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        server_addr.sin_port = htons(port);
        int bind_i = bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
        if (bind_i == -1) {
            printf("bind error\n");
            return  -1;
        }
        int listen_i = listen(listen_fd, LISTEN_N);
        if (listen_i == -1) {
            printf("listen error\n");
            return  -1;
        }
        epfd = epoll_create(EPOLL_MAX_NUM);
        if (epfd < 0) {
            perror("epoll create");
            goto END;
        }
        event.events = EPOLLIN;
        event.data.fd = listen_fd;
        if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event) < 0) {
            perror("epoll ctl add listen_fd ");
            goto END;
        }
        my_events = malloc(sizeof(struct epoll_event) * EPOLL_MAX_NUM);
        while (1) {
            int active_fds_cnt = epoll_wait(epfd, my_events, EPOLL_MAX_NUM, -1);
            int i = 0;
            for (i = 0; i < active_fds_cnt; i++) {
                if (my_events[i].data.fd == listen_fd) {
                    client_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_len);
                    if (client_fd < 0) {
                        perror("accept");
                        continue;
                    }
                    char ip[20];
                    printf("new connection[%s:%d]\n", inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip)),ntohs(client_addr.sin_port));
                    event.events = EPOLLIN | EPOLLET;
                    event.data.fd = client_fd;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event);
                } else if (my_events[i].events & EPOLLIN) {
                    client_fd = my_events[i].data.fd;
                    int n = read(client_fd, buffer, BUF_SIZE);
                    if (n < 0) {
                        perror("read");
                        continue;
                    } else if (n == 0) {
                        epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, &event);
                        close(client_fd);
                    } else {
                        printf("[read]: %s\n", buffer);
                        char *param = get_param_form_request(buffer);
                        memset(buffer, 0, BUF_SIZE);
                        char *path = str_join(base_path, param);
                        if (access(path, F_OK|R_OK)==0) {
                            //header.
                            char *post_fix = strstr(param, ".");
                            post_fix++;
                            send_header_with_content_type(client_fd, post_fix);
                            //data.
                            FILE *p = fopen(path, "rb");
                            int read2_n;
                            while ((read2_n = fread(buffer, sizeof(char), BUF_SIZE, p)) != 0) {
                                send(client_fd, buffer, read2_n, 0);
                                memset(buffer, 0x00, read2_n);
                            }
                            fclose(p);
                        } else{
                            //header
                            send_header_with_content_type(client_fd, "html");
                            //data
                            send(client_fd, "NOT FOUND"strlen("NOT FOUND"), 0);
                        }
                        close(client_fd);
                    }
                }
            }
        }
        END:
        close(epfd);
        close(listen_fd);
        return 0;
    }
    • 1

    Golang

    Golang 底层的 netpoller 基于 IO 多路复用, 所以 go 是原生支持 IO 多路复用的

    type Req struct {
     reqUrl,contentType string
    }
    func main() {
     basePath := "/root/go/src/go_static_server"
     address := ":9000"
     listen, _ := net.Listen("tcp",address )
     log.Printf("listen=%#v\n", address)
     for  {
      conn, _ := listen.Accept()
      go func() {
       defer conn.Close()
       log.Printf("NEW CLIENT:%s\n", conn.RemoteAddr().String())
       //get request url
       req := getReq(conn)
       path := basePath+req.reqUrl
       file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
       if err!=nil {
        return
       }
       //write back header
       writeHeader(conn,req.contentType)
       //write back file
       writeFile(conn,file)
      }()
     }
    }
    func getReq(conn net.Conn) *Req {
     reader := bufio.NewReader(conn)
     line, _, _ := reader.ReadLine()
     splits := strings.Split(string(line), " ")
     reqUrl := splits[1]
     paths := strings.Split(reqUrl, "/")
     contentType := paths[len(paths)-1]
     return &Req{reqUrl: reqUrl,contentType: contentType}
    }
    func writeHeader(conn net.Conn,contentType string)  {
     buffer := bytes.Buffer{}
     buffer.WriteString("HTTP/1.1 200 OK\r\n")
     buffer.WriteString("Content-Type:")
     buffer.WriteString(contentType)
     buffer.WriteString(";charset=UTF-8\r\n\r\n")
     conn.Write(buffer.Bytes())
    }
    func writeFile(conn net.Conn,file *os.File)  {
     buf := make([]byte,1024)
     for  {
      readN, _ := file.Read(buf)
      if readN <= 0  {
       break
      }
      conn.Write(buf)
     }
    }
    • 1

    本文由 mdnice 多平台发布

  • 相关阅读:
    Dubbo框架基本使用
    YOLOv7移植经验分享
    Cesium加载离线地图和离线地形
    Netty源码研究笔记(1)——开篇
    LeetCode 317 周赛
    “五之链”第十六期沙龙活动在呆马科技成功举办
    MySQL系列:索引失效场景总结
    方圆的秒杀系统优化方案实战,(六)分布式缓存
    Verilog:【1】时钟分频电路(clk_divider.sv)
    loadrunner脚本--参数化
  • 原文地址:https://blog.csdn.net/jarvan5/article/details/127120385