用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 ...
随机推荐
- 鸿蒙极速入门(五)-路由管理(Router)
页面路由指在应用程序中实现不同页面之间的跳转和数据传递.HarmonyOS提供了Router模块,通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面. 一.基础使用 Router模块提 ...
- CMake 进行多项目中dll的编译和链接
前言(maybe废话) 最近正在学习cherno的游戏引擎教程,他使用的是vs进行构建的,后面换了premake.而我用的是vscode+cmake,所以在构建整个项目的时候踩了不少的坑,也找了很多资 ...
- Swift 与 Objc Exception
一.背景 Swift具备完善的Error handle机制,对于纯Swift下面的Error,在编码的时候能够正确处理. 在使用try? 处理抛出Error的方法的时候,会忽略Error,直接返回ni ...
- js 禁用右键菜单和禁止复制
大江东去,浪淘尽,千古风流人物.故垒西边,人道是,三国周郎赤壁.乱石穿空,惊涛拍岸,卷起千堆雪.江山如画,一时多少豪杰.遥想公瑾当年,小乔初嫁了,雄姿英发.羽扇纶巾,谈笑间,樯橹灰飞烟灭.故国神游,多 ...
- bash: _get_comp_words_by_ref: command not found 报错
没有安装补全的包 错误信息 bash: _get_comp_words_by_ref: command not found 表明你的 shell 中可能存在补全功能的问题. 通常,这种错误发生在你的系 ...
- edge浏览器禁用搜索工具栏或七七八八的东西
edge浏览器禁用搜索工具栏或七七八八的东西 在浏览器地址里输入: edge://flags/#edge-show-feature-recommendations 把"Show featur ...
- javascript class 方法的this指向问题
踩坑记录 JavaScript 的 class 里面有两种定义方法的方式 普通函数(fun1) 箭头函数(fun2) class Obj { func1() { // write some code. ...
- 阿里云安全扫描漏洞修复fastjson,jackson,xstream,redis, 微信支付xml转对象 ForbiddenClassException
阿里云安全扫描漏洞修复fastjson,jackson,xstream,redis 1.fastjson漏洞fastjson爆发新的反序列化远程代码执行漏洞,黑客利用漏洞,可绕过autoType限制, ...
- CNN --入门MNIST识别
Smiling & Weeping ---- 下次你撑伞低头看水洼, 就会想起我说雨是神的烟花. 简介:主要是看刘二大人的视频讲解:https://www.bilibili.com/video ...
- gitlab自动定时备份文件,备份失败发送邮件
一.需求 为预防gitlab出现故障,每天定时备份,备份完成后把之前的备份文件删除,备份成功或失败的时候自动发送邮件提醒,这里的gitlab为docker部署. 二.备份命令准备 1)备份命令 创建一 ...