邓嘉文Jarvan

V1

2022/09/30阅读:15主题:橙心

用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();
}

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<SelectionKey> selectionKeys = selector.selectedKeys();
         Iterator<SelectionKey> 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;
             }
         }
     }
 }

C

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#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);
    }
}

C IO多路复用

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <sys/epoll.h>

#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;
}

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)
 }
}

分类:

后端

标签:

后端

作者介绍

邓嘉文Jarvan
V1