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

成果展现

代理 HTML

代理图片

留神, 动态资源代理基于 HTTP, 能够理解上一篇文章: 几十行代码应用TCP简略实现HTTP(C/Golang/Java) https://blog.csdn.net/jarvan5...

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;             }         }     } }
  • 应用前记得批改代理的门路 basePath 为你的动态资源地址
  • 残缺代码仓库 https://github.com/dengjiawen8955/java_static_server

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, 0, strlen(buff));    if (send_n < 0) {        return  -1;    }    return 0;}//socket -> bind -> listenint 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 10char 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, 0, strlen(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;}
  • 应用前记得批改代理的门路 base_path 为你的动态资源地址
  • 残缺代码仓库 https://github.com/dengjiawen...

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)    }}
  • 应用前记得批改代理的门路 basePath 为你的动态资源地址
  • 残缺代码仓库 https://github.com/dengjiawen...r

本文由mdnice多平台公布