用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. C# 关于图片转ICO的代码整理(无损,不需要第三方类库)

    概述(Overview) 感觉网上文章整理的不全,我这边做个专栏,专门做这个事情吧,节省大家搜索.筛选.整理的时间精力.有用可以点个赞.引用本文章请注明出处,谢谢. (I feel that the ...

  2. NOIP模拟65

    T1 网格图 解题思路 60pts 就是个zz做法..(我考场上造了一个 \(500\times 500\) 的 X,一看挺快,就以为 \(n^4\) 可以切,然而..) 正解有一点难度,对于每一个节 ...

  3. Java synchronized与ReentrantLock的区别

    synchronized与ReentrantLock的区别 synchronized是一个关键字,ReentrantLock是一个类 synchronized修饰代码块和方法,ReentrantLoc ...

  4. .NET周刊【6月第1期 2024-06-02】

    国内文章 一文带你了解.NET能做什么? https://www.cnblogs.com/Can-daydayup/p/18214473 .NET是一个免费.开源.跨平台的开发平台框架,广泛应用于桌面 ...

  5. kettle从入门到精通 第三十一课 mysql 数据连接连接池配置

    无论开发应用程序还是做ETL研发,都离不开连接池的应用,如下是kettle中mysql 连接池设置界面,今天重点讲解下连接池中的参数配置. defaultAutoCommit 当 defaultAut ...

  6. ASP.NET MVC 查询加分页

    使用了LinqKit.PagedList.Mvc.EntityFramework 等DLL 直接使用nuget安装即可. 1.表模型: using System.ComponentModel.Data ...

  7. 请写出常用的linux指令

    a.cd /home 进入 '/ home' 目录' b.cd .. 返回上一级目录 c.cd ../.. 返回上两级目录 d.mkdir dir1 创建一个叫做 'dir1' 的目录' e.mkdi ...

  8. Mysql联合索引生效、失效条件

    引言 联合索引又叫复合索引.两个或更多个列上的索引被称作复合索引. 对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分.例如索引是key inde ...

  9. 硬件开发笔记(十九):Altium Designer 21软件介绍和安装过程

    前言   AD硬件设计软件之一,前面说了allego,但是allego对项目的管理.原理图生成PCB,PCB反向原理图等方面比较复杂,对于一般的硬件(非多个高速电路),选择AD能够加大的节省开发工作量 ...

  10. MySQL bit类型增加索引后查询结果不正确案例浅析

    昨天同事遇到的一个案例,这里简单描述一下:一个表里面有一个bit类型的字段,同事在优化相关SQL的过程中,给这个表的bit类型的字段新增了一个索引,然后测试验证 时,居然发现SQL语句执行结果跟不加索 ...