手撕JavaWeb服务器01——对一个请求的简单响应

前言

2024年对于自己来说是一个新的开始,从今年开始我将会专注于技术能力的提升,以博客的方式记录自己成长的脚步。今年的主线就是这个专栏------手撕 JavaWeb 服务器系列。我将会以一个后端开发者的视角去探索 Web 服务器的底层运行机制,通过猜想->实践->验证的基本流程去学习,即以 JavaWeb 服务器所具备的各种能力,去猜想它的实现原理,然后通过实践去实现,最后去看看已有的开源项目(tomcat、jboss等)的源码并思考如此设计的原因。感兴趣的朋友可以关注一下,并在疑惑的地方提出您的宝贵意见,一起探讨进步!

1. 从网络讲起

谈到 Web 服务器,就离不开网络。通常,我们需要先通过网络发送一个请求到 Web 服务器,Web 服务器根据我们的请求信息再通过网络返回对应的响应信息。信息是怎么通过网络传输地呢?接下来我们简单地聊一下(ps:讲的很浅,请耐心看完哈)

1.1. 寄快递

我们可以把网络传输数据的过程想象成物流公司运送快递,其中的很多原理是相通的。对于普通人来说,快递是怎么包装、运输、拆卸的,他们不关心,他们只需要找到快递驿站去寄送快递和取快递。同样,对于web服务器来说,它只管从指定的地方去发送和取数据即可。

寄过快递的同学应该清楚,我们在寄快递的时候需要填写自己的地址和目的地地址,类比到服务器也是一样,需要指明数据的目的地。那么在网络中如何定位一个网络设备?

1.2. 地址

相信这个问题难不倒大家,就是 ip 地址,我们可以通过 ip 地址去定位一个网络设备。这里我们也不做展开,有兴趣的同学可以自己去搜索相关的资料。我们可以把 ip 地址看作是网络中的 xxx 省 xxx 市 xxx 区,按照一定的规则可以通过这串字符定位到唯一的一个网络设备。

广义上来说,接入网络的设备都可以称作为网络设备,包括我们的个人电脑、手机、公司的服务器等等。找到了网络设备我们是不是就能直接发送数据了呢?

1.3. 端口

答案是:否。在一个网络设备中往往存在多个进程,打个比方把同一台电脑中的 qq 和 微信 当作两个不同的进程。如果只通过 ip 地址去区分数据,那么我的 qq 的数据有可能发到微信,微信的也有可能发送到 qq。因此在 ip 地址的基础上,同一个网络设备还需要去区分该设备中的多个应用。那么如何区分呢?这个时候就要用到端口了。

端口,大家可以简单的理解为网络设备中派送数据的门口。每个进程守在一个门口前面,网络设备会对这些"门"进行编号,根据数据包上的端口号找到对应的门,把数据包丢过去。

2. Socket 编程

通过上面的叙述,我们知道了网络数据传输数据的逻辑过程,具体的实现有时间大家可以去研究一下,这里我们主要讲一下代码层面的东西。

既然我们需要守着一个端口,在 Java 里面我们怎么实现呢?

java 复制代码
    public static void main(String[] args) throws IOException {
        //监听8080端口
        ServerSocket serverSocket = new ServerSocket(8080);
    }

在上面的程序中,我们可以通过创建 ServerSocket 类,并且在构造方法中传入端口号的方式去监听指定的端口。关于端口号,我们需要再解释一点,那就是它的取值范围,一般端口号的取值范围是 1到65535,用了 16 位二进制来表示端口号。

2.1. 接收数据

现在我们已经监听了 8080 端口,我们如何获取传输过来的数据呢?

java 复制代码
        ServerSocket serverSocket = new ServerSocket(8080);
        Socket accept = serverSocket.accept();
        InputStream inputStream = accept.getInputStream();
        int i;
        while((i=inputStream.read())!=-1){
            System.out.print((char)i);
        }

创建 serverSocket 对象之后,我们通过调用 accept() 方法去获取 Socket 对象,这个方法会阻塞等待直到有数据到达我们监听的端口。数据就被封装在 IO 流中,我们可以通过 Socket 对象去获取输入流获取,以操作输入流的方式去获取网络中的数据。

2.2. 返回响应

同样,我们也可以通过操作输出流去向客户端返回响应的数据。

java 复制代码
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        Socket accept = serverSocket.accept();
        InputStream inputStream = accept.getInputStream();
        byte[] bytes = new byte[1024];
        int i;
        //输出请求信息
        while((i=inputStream.read(bytes,0,bytes.length))!=-1){
            System.out.println(new String(bytes,0,i,StandardCharsets.UTF_8));
            if (i<1024){
                break;
            }
        }
        //响应内容
        String responseContext = "HTTP/1.1 200 OK\n" +
                "Content-Type: text/plain\n" +
                "Content-Length: 11\n" +
                "\n" +
                "hello world\n";
        OutputStream outputStream = accept.getOutputStream();
        outputStream.write(responseContext.getBytes(StandardCharsets.UTF_8));
        //清空缓存区,刷新到目的空间
        outputStream.flush();
        outputStream.close();
        inputStream.close();
    }

这段代码比较关键的地方就是 flush 方法,如果不调用这个方法,浏览器是不会立即得到响应信息的,该方法的作用就是将缓存区的数据立即返回。

上面就是我们本次博客要介绍的完整代码了。我们返回了一个 http 报文格式的响应信息,内容是 hello world。在启动项目之后,我们使用浏览器访问 http://localhost:8080/ 即可得到下面的结果

3. 总结

  1. 首先我们浅谈了一下在应用服务器,网络以及数据之间的关系
  2. 然后我们通过 Java 代码的方式实现了对于指定端口数据的接收和响应

到这里,手撕 JavaWeb 服务器的第一篇就结束了,是不是很简单?接下来我将会一边学习,一边分享,然后将自己的学习感悟和心得以博客的方式分享出来,感兴趣的同学也可以关注我,以评论和私信的方式和我一起讨论。

相关推荐
钱多多_qdd6 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha8 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_192849990619 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
Code_流苏21 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境
良许Linux23 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥35 分钟前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
禁默1 小时前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端