用IO多路复用实现 nginx 静态资源代理(C/Java/Golang)
用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;
}
}
}
}
- 使用前记得修改代理的路径
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 -> 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;
}
- 使用前记得修改代理的路径
base_path为你的静态资源地址 - 完整代码仓库 https://github.com/dengjiawen8955/c_static_server
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/dengjiawen8955/go_static_server
本文由mdnice多平台发布
用IO多路复用实现 nginx 静态资源代理(C/Java/Golang)的更多相关文章
- 06 . Nginx静态资源缓存
Nginx静态资源 Nginx可以处理静态资源 非Web服务器可以运行处理而生成的文件,即服务器只需要从硬盘或者缓存中读取然后直接给客户端响应即可. 常见的静态资源 # 浏览器渲染: html文件,样 ...
- 008.Nginx静态资源
一 Nginx静态资源概述 1.1 静态资源类型 Nginx作为静态资源Web服务器部署配置, 传输非常高效, 常常用于静态资源处理,请求以及动静分离.通常非服务器动态运行生成的文件属于静态资源. 类 ...
- 清除nginx静态资源缓存
之前写过一篇如何配置nginx缓存及手动清除缓存的文章: http://www.cnblogs.com/Eivll0m/p/4921829.html 但如果有大量缓存需要清理,手动一条条清理就比较慢了 ...
- Nginx 静态资源缓存配置
示例 # Media: images, icons, video, audio, HTC location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|m ...
- Nginx——静态资源服务器(一)
java web的项目中,我们经常将项目部署到Tomcat或者jetty上,可以通过Tomcat或者jetty启动的服务来访问静态资源.但是随着Nginx的普及,用Nginx来作为静态资源服务器,似乎 ...
- nginx静态资源设置缓存的方法
nginx静态资源设置缓存的方法 直接加expires 30d; 就是就可以了 缓存时间30天完整如下 <pre> location / { root /home/www/wordpres ...
- nginx静态资源分离部署
修改nginx.conf文件,用于nginx处理静态资源. 主要配置如下(在server配置中加入location配置即可): server { listen 80; server_name 123. ...
- Nginx详解十:Nginx场景实践篇之Nginx静态资源场景配置
一.静态资源WEB服务 1.静态资源类型:非服务器动态运行生成的文件 2.静态资源服务场景-CDN 假设静态资源存储中心在云南,用户在北京去请求一个文件,那么就会造成一个传输的延时,而如果Nginx同 ...
- nginx静态资源缓存策略配置
1. 问题-背景 以前也经常用nginx,但用的不深,通常是简单的设置个location用来做反向代理.直到今天给客户做项目碰到缓存问题:客户有个app,只是用原生做了个壳,里面的内容都是用h5写的, ...
- nginx静态资源缓存与压缩
一.静态资源缓存 参考文章 (1)apache设置max-age或expires 这里需要修改.htaccess文件. <IfModule mod_headers.c> <Files ...
随机推荐
- nginx学习记录【一】在windows上的安装nginx的教程
1.下载地址 http://nginx.org/en/download.html 2.选择windows版本 如下图: 3.解压并运行 解压到指定目录,如下图 打开cmd,然后cd到那个目录,如下图: ...
- Geatpy学习笔记1:官方案例
一.入门 1.求解器模式入门 案例 1 import geatpy as ea import numpy as np # 构建问题 r = 1 # 目标函数需要用到的额外数据 @ea.Problem. ...
- Java爬虫-爬取疫苗批次信息
今年3月份开始,就接到通知, 根据<关于开展有关人群第二剂次脊髓灰质炎灭活疫苗补种工作的通知>国疾控卫免发[2024]1号文件要求,在2016年3月1日至2019年9月30日之间出生的儿童 ...
- U-Boot命令使用
帮助命令 help 所有命令提示: h ? help 某一条命令提示: ? 命令名 help 命令名 信息查询命令 bdinfo 查看板子信息: => bdinfo arch_number = ...
- ts interface
在TypeScript中,接口(interface)用于定义对象的结构,可以描述对象的属性.方法等.下面是一个简单的例子,演示如何使用 TypeScript 接口: // 定义一个接口表示一个人的信息 ...
- JVM垃圾回收器(详解)
引言 垃圾回收(GC,Garbage Collection) 在笔者上一篇文章中(JVM内存模型),介绍了JVM内存模型以及JVM运行时的数据区,堆是JVM内存区域里面最大的一块区域,用于存放实例数据 ...
- 部署jar项目服务命令
部署jar项目服务命令首先使用jenkins打包jar history | grep java 查看ps aux | grep 服务关键字关闭进程,否则启动的时候报错:java.net.BindExc ...
- HDU2062题解 01背包而已
RT,我就不解释了,题目连接http://acm.hdu.edu.cn/showproblem.php?pid=2602. 初学01背包的人可以做做 #include<iostream> ...
- 深入探索 Nuxt3 Composables:掌握目录架构与内置API的高效应用
title: 深入探索 Nuxt3 Composables:掌握目录架构与内置API的高效应用 date: 2024/6/23 updated: 2024/6/23 author: cmdragon ...
- Python加密操作 对称加密/非对称加密
安装包: pycryptodome https://pycryptodome.readthedocs.io/en/latest/src/installation.html#compiling-in-l ...