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

效果展示

代理 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, 0, strlen(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, 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;
}

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

本文由mdnice多平台发布

用IO多路复用实现 nginx 静态资源代理(C/Java/Golang)的更多相关文章

  1. 06 . Nginx静态资源缓存

    Nginx静态资源 Nginx可以处理静态资源 非Web服务器可以运行处理而生成的文件,即服务器只需要从硬盘或者缓存中读取然后直接给客户端响应即可. 常见的静态资源 # 浏览器渲染: html文件,样 ...

  2. 008.Nginx静态资源

    一 Nginx静态资源概述 1.1 静态资源类型 Nginx作为静态资源Web服务器部署配置, 传输非常高效, 常常用于静态资源处理,请求以及动静分离.通常非服务器动态运行生成的文件属于静态资源. 类 ...

  3. 清除nginx静态资源缓存

    之前写过一篇如何配置nginx缓存及手动清除缓存的文章: http://www.cnblogs.com/Eivll0m/p/4921829.html 但如果有大量缓存需要清理,手动一条条清理就比较慢了 ...

  4. Nginx 静态资源缓存配置

    示例 # Media: images, icons, video, audio, HTC location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|m ...

  5. Nginx——静态资源服务器(一)

    java web的项目中,我们经常将项目部署到Tomcat或者jetty上,可以通过Tomcat或者jetty启动的服务来访问静态资源.但是随着Nginx的普及,用Nginx来作为静态资源服务器,似乎 ...

  6. nginx静态资源设置缓存的方法

    nginx静态资源设置缓存的方法 直接加expires 30d; 就是就可以了 缓存时间30天完整如下 <pre> location / { root /home/www/wordpres ...

  7. nginx静态资源分离部署

    修改nginx.conf文件,用于nginx处理静态资源. 主要配置如下(在server配置中加入location配置即可): server { listen 80; server_name 123. ...

  8. Nginx详解十:Nginx场景实践篇之Nginx静态资源场景配置

    一.静态资源WEB服务 1.静态资源类型:非服务器动态运行生成的文件 2.静态资源服务场景-CDN 假设静态资源存储中心在云南,用户在北京去请求一个文件,那么就会造成一个传输的延时,而如果Nginx同 ...

  9. nginx静态资源缓存策略配置

    1. 问题-背景 以前也经常用nginx,但用的不深,通常是简单的设置个location用来做反向代理.直到今天给客户做项目碰到缓存问题:客户有个app,只是用原生做了个壳,里面的内容都是用h5写的, ...

  10. nginx静态资源缓存与压缩

    一.静态资源缓存 参考文章 (1)apache设置max-age或expires 这里需要修改.htaccess文件. <IfModule mod_headers.c> <Files ...

随机推荐

  1. 使用Docker安装Odoo 17(非Docker Compose)

    使用Docker安装Odoo 17(非Docker Compose) 前言 最近在学习Odoo,先是windows 安装企业版,多年不用windows的服务器操作系统,一看windows的ECS那么贵 ...

  2. Ceph对象网关,多区域网关

    目录 Ceph对象网关,多区域网关 1. 文件系统与对象存储的区别 1.1 对象存储使用场景 1.2 对象存储的接口标准 1.3 桶(bucket) 2. rgw 2.1 对象存储认证 2.2 对象网 ...

  3. 7.26考试总结(NOIP模拟24)[matrix·block·graph]

    你那无聊的幻想,就由我来打破! 前言 补坑中.. 我都不知道自己这场模拟赛怎么打的了. 非常玄学,前三个小时一直在想正解,然后最后 20min 感觉 T1 不太稳,就又加上了一个暴力. 后来一看只有最 ...

  4. FreeRTOS-02-列表和列表项

    说明: 本文仅作为学习FreeRTOS的记录文档,作为初学者肯定很多理解不对甚至错误的地方,望网友指正. FreeRTOS是一个RTOS(实时操作系统)系统,支持抢占式.合作式和时间片调度.适用于微处 ...

  5. 算法金 | 只需十四步:从零开始掌握Python机器学习(附资源)

    大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 1. 引言 1.1 教程目的与读者定位 "启程"往往是最具挑战性的 ...

  6. CF1777E

    problem & blog 反转的边最大权值最小,想到二分. 于是二分代价即可. 反转代价小于二分的代价的边可以反转,所以再建一条反向边即可. 在 DAG 中,存在一个点可以到达所有的点的条 ...

  7. netcore 打包dll发布到nuget服务器

    可参考微软官网:NuGet.org 概述 | Microsoft Docs 一.创建类库 首先创建一个类库,就是你想要发布到nuget的类库,生成项目 二.下载并注册nuget nuget地址:htt ...

  8. Java对象转Map<String,String>

    Java对象转Map<String,String> import org.springframework.beans.BeanUtils; import org.springframewo ...

  9. 记一次 Edge 及谷歌 Chrome 浏览器兼容性冲突的解决

    目录 记一次 Edge 及谷歌 Chrome 浏览器兼容性冲突的解决 浏览器兼容性冲突症状 解决方法 1. 把本机和远程的 8235 和 8237 端口屏蔽,包括 TCP 和 UDP 端口 2. 在 ...

  10. Scrcpy - 开源免费在电脑显示手机画面并控制手机的工具 (投屏/录屏/免Root)

    教程:https://www.iplaysoft.com/scrcpy.html 官方地址:https://github.com/Genymobile/scrcpy