使用socket模拟tomcat实现静态资源处理

步骤:

1.服务端使用ServerSocket,监听指定的端口。
2.调用ServerSocket的accept方法接收客户端连接,得到Socket对象。
3.根据Socket对象的getInputStream()方法得到输入流,从而拿到浏览器发送的http请求的基本信息。

GET /htmlfiles/test2.jsp HTTP/1.1
Host: localhost:9191
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_9bd56a6d0766b887592ee921aa94763f=1500436957; __atuvc=47%7C35; _ga=GA1.1.2016651800.1504071340; Hm_lvt_4e003ba2028a4a83d95714c602ee7df5=1507970222; Hm_lvt_47acec2d282c3986f1b600abdc11c7ab=1520665960,1521097911,1521252255,1521540649

4.解析http请求,提取要访问的资源文件的位置,使用Socket的getOutputStream拿到输出流,将文件的内容写到输出流。
5.资源请求完毕,关闭相关的流和Socket。

代码

public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9191);
            System.out.println("Server已在端口9191启动!");
            while (true) {
                socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                Request request = new Request(inputStream);
                OutputStream outputStream = socket.getOutputStream();
                Response response = new Response(outputStream, request);
                response.response();
                outputStream.close();
                inputStream.close();
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Request {
    private String uri;
    public Request(InputStream in) {
        try {
            String content = IOUtils.getRequestContent(in);
            if (null == content || "".equals(content)) {
                System.out.println("Bad request.");
                return;
            }
            System.out.println("客户端请求:\n" + content);
            parseUri(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void parseUri(String content) {
        this.uri = content.substring(content.indexOf("/"),content.indexOf("HTTP/")-1);
    }
    public String getUri() {
        return uri;
    }
    public void setUri(String uri) {
        this.uri = uri;
    }
}
public class Response {
    private Request request;
    private OutputStream os;
    public Response(OutputStream os, Request request) {
        this.os = os;
        this.request = request;
    }
    public void response() {
        String uri = request.getUri();
        if (null != uri && !"".equals(uri)) {
            if (isResource()) {
                String path = Http_Server.WEB_ROOT + uri.substring(1);
                if (IOUtils.isFileExist(path)) {
                    IOUtils.writeFile(os, path);
                } else {
                    String errorMessage = buildResponse("File Not Found","404","");
                    IOUtils.write(os, errorMessage.getBytes());
                }
            } else {
                String errorMessage = buildResponse("动态请求暂不支持","200","OK");
                IOUtils.write(os, errorMessage.getBytes());
            }
        }
    }
    private String buildResponse(String content, String statusCode, String statusMsg) {
        String html = buildHTML(content);
        int chineseCount = StringUtils.getChineseCharCount(html);
        StringBuffer buffer = new StringBuffer();
        buffer.append("HTTP/1.1 ")
                .append(statusCode).append(" ")
                .append(statusMsg).append("\r\n")
                .append("Content-Type: text/html\r\n")
//                .append("Content-Length: ")
//                .append(html.length()+2*chineseCount) // 1个中文字符占3个字节
                .append("\r\n\r\n")
                .append(html);
        return buffer.toString();
    }
    private String buildHTML(String content) {
        StringBuffer html = new StringBuffer();
        html.append("<html>\n");
        html.append("<head>\n");
        html.append("<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n");
        html.append("</head>\n");
        html.append("<body>\n");
        html.append("<h1>").append(content).append("</h1>\n");
        html.append("</body>\n");
        html.append("</html>");
        return html.toString();
    }
    private boolean isResource() {
        String[] suffixs = {"html", "js", "css", "jpg", "jpeg", "gif", "bmp"};
        for (String suf : suffixs) {
            if (request.getUri().endsWith("." + suf)) {
                return true;
            }
        }
        return false;
    }
}
public class IOUtils {
    public static String getRequestContent(InputStream inputStream) throws IOException {
        byte[] data = new byte[2048];
        int len = inputStream.read(data);
        if (len > 0) {
            return new String(data,0,len);
        }
        return null;
    }
    public static boolean isFileExist(String path) {
        File file = new File(path);
        return file.exists();
    }
    public static byte[] getFileContent(String path) {
        try {
            File file = new File(path);
            if (file.exists()) {
                byte[] data = new byte[(int) file.length()];
                FileInputStream fis = new FileInputStream(file);
                fis.read(data);
                return data;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static void write(OutputStream os,byte[] data) {
        try {
            os.write(data);
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void writeFile(OutputStream os,String path) {
        FileInputStream fis = null;
        try {
            int BUFFER_SIZE = 1024;
            fis = new FileInputStream(path);
            byte[] data = new byte[BUFFER_SIZE];
            int len = fis.read(data,0,BUFFER_SIZE);
            boolean isFirst = true;
            while (len != -1) {
                if (isFirst) {
                    os.write("HTTP/1.0 200 OK\r\n".getBytes());
                    os.write("\r\n".getBytes());;// 根据 HTTP 协议, 空行将结束头信息
                    isFirst = false;
                }
                os.write(data,0,len);
                len = fis.read(data,0,BUFFER_SIZE);
                os.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class Http_Server {
    public final static String WEB_ROOT = Http_Server.class.getResource("/").getPath();
}

测试HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试页面</title>
    <link href="/htmlfiles/css/test.css" rel="stylesheet">
</head>
<body>
    <h1>这是测试页面。<br></h1>
    <a href="http://localhost:9191/htmlfiles/images/1.jpg">点击打开图片</a>
</body>
</html>

资源文件放在src目录下。如图:

测试

1.动态请求测试

2.不存在的静态文件

3.存在的静态文件

4.图片等二进制文件测试

注意

1.你得通过响应头告诉浏览器请求的状态码和资源类型,否则浏览器会告诉你这是无效的http响应。

os.write("HTTP/1.0 200 OK\r\n".getBytes());
os.write("\r\n".getBytes());;// 根据 HTTP 协议, 空行将结束头信息

2.如果指定了Content-Length,需要注意中文1个字符占用3个字节,否则会导致浏览器显示不全。

buffer.append("Content-Length: ")
buffer.append(html.length()+2*chineseCount) // 1个中文字符占3个字节
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这里提供一个简单的基于socket的滑动窗口模拟实现,代码如下: ```python import socket import time # 定义发送方和接收方的IP和端口号 sender_ip = '127.0.0.1' sender_port = 8888 receiver_ip = '127.0.0.1' receiver_port = 9999 # 定义数据包大小和窗口大小 packet_size = 1024 window_size = 4 # 创建UDP socket sender_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) receiver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定接收方的IP和端口号 receiver_socket.bind((receiver_ip, receiver_port)) # 定义发送方和接收方的初始序号和确认序号 sender_seq = 0 sender_ack = 0 receiver_seq = 0 receiver_ack = 0 # 定义发送方和接收方的窗口 sender_window = [] receiver_window = [] # 发送数据 def send_data(data): global sender_seq, sender_ack, sender_window # 将数据分割成数据包 packets = [data[i:i+packet_size] for i in range(0, len(data), packet_size)] for packet in packets: # 将数据包打包成字节串 packet_bytes = str(sender_seq).encode() + b':' + packet # 发送数据包 sender_socket.sendto(packet_bytes, (receiver_ip, receiver_port)) # 将数据包加入发送方窗口 sender_window.append(packet_bytes) # 更新发送方序号 sender_seq += len(packet) # 如果发送方窗口大小超过了窗口大小,等待接收方的确认,直到发送方窗口大小小于等于窗口大小 while len(sender_window) > window_size: time.sleep(0.1) receive_ack() # 接收数据 def receive_data(): global receiver_seq, receiver_ack, receiver_window # 接收数据包 packet_bytes, _ = receiver_socket.recvfrom(packet_size + 32) # 解析数据包 seq_bytes, packet = packet_bytes.split(b':', 1) seq = int(seq_bytes.decode()) # 如果序号不是期望的序号,直接丢弃数据包 if seq != receiver_seq: return # 将数据包加入接收方窗口 receiver_window.append(packet) # 更新接收方序号 receiver_seq += len(packet) # 更新接收方确认序号 receiver_ack = receiver_seq # 发送确认消息 send_ack() # 发送确认消息 def send_ack(): global receiver_ack # 发送确认消息,确认序号为接收方序号 receiver_socket.sendto(str(receiver_ack).encode(), (sender_ip, sender_port)) # 接收确认消息 def receive_ack(): global sender_ack, sender_window # 接收确认消息 ack_bytes, _ = sender_socket.recvfrom(packet_size) # 解析确认消息 ack = int(ack_bytes.decode()) # 更新发送方确认序号 sender_ack = ack # 从发送方窗口中删除已经确认的数据包 while sender_window and int(sender_window[0].split(b':', 1)[0].decode()) < sender_ack: sender_window.pop(0) if __name__ == '__main__': # 发送数据 send_data('hello world') # 接收数据 while True: receive_data() if receiver_ack == sender_seq: break # 关闭socket sender_socket.close() receiver_socket.close() ``` 该代码实现了一个简单的滑动窗口模拟,通过UDP socket发送和接收数据包,并实现了滑动窗口的功能。在发送数据时,将数据分割成数据包,并将数据包打包成字节串发送。在接收数据时,解析数据包,并将数据包加入接收方窗口,更新接收方序号和确认序号,然后发送确认消息。在发送确认消息时,确认序号为接收方序号。在接收确认消息时,更新发送方确认序号,并从发送方窗口中删除已经确认的数据包。通过循环接收数据,并等待接收方确认,直到所有数据都被接收方确认。最后关闭socket

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值