大家天天开心!!!!!
文章目录
前言
在我们学习TomCat和Servlet之前,我们要知道Tomcat其实就相互于一个Java程序,但是这个Java程序可以处理来自浏览器的Http请求。下面我们就自己来编写一个“服务器”来接收和相应浏览器或客户端发送的请求。
提示:以下是本篇文章正文内容,下面案例可供参考
一、一个简单的Web浏览服务程序
下面是源代码:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTomcat {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(9999)) {
System.out.println("=====我的 web 服务在 9999 端口监听=====");
while (!serverSocket.isClosed()) {
try (Socket socket = serverSocket.accept();
OutputStream outputStream = socket.getOutputStream();
BufferedReader bufferedReader =
new BufferedReader(new FileReader("src/hello.html"))) {
// 发送HTTP响应头
String httpHeader = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/html; charset=utf-8\r\n" +
"\r\n";
outputStream.write(httpHeader.getBytes());
// 发送HTML内容
String line;
while ((line = bufferedReader.readLine()) != null) {
outputStream.write(line.getBytes());
}
} catch (IOException e) {
System.out.println("处理客户端请求时出错: " + e.getMessage());
}
}
} catch (IOException e) {
System.out.println("启动服务器失败: " + e.getMessage());
// 检查端口是否被占用[1,3](@ref)
System.out.println("请确认9999端口没有被其他程序占用");
}
}
}
二、对以上代码的解释:
首先先说明我这个是模拟的普通Web服务程序,让我们来看看Web服务器如何与浏览器“对话”是如何进行的
1.ServerSocket 和 Socket 的生动比喻:
你可以把整个网络通信过程想象成一个客服中心的工作流程:
组件 |
比喻 |
职责 |
---|---|---|
|
客服总机 |
它固定在公司的某个电话号码上(绑定端口,比如9999),只负责一件事:等待并接起打进来的电话( |
|
接通后的分机电话 |
当总机 ( |
所以,在我的代码中:
- 1.
ServerSocket serverSocket = new ServerSocket(9999);
就像是:开通了公司的客服总机,号码是9999。 - 2.
Socket socket = serverSocket.accept();
就像是:总机接线员拿起听筒,等待并接起一个客户的来电。一旦电话接通,你就得到了一条和这位客户专用的通话线路(socket
)。 - 3.
客户端(浏览器)通过输入
localhost:9999
来连接,就相当于:客户拨打你公司的总机号码
2.网络通信的“公司大楼”模型
概念 |
现实比喻 |
对应计算机概念 |
---|---|---|
公司大楼 |
一座有固定地址的办公楼 |
你的电脑(主机) |
公司地址 |
办公楼的具体街道门牌号 |
|
客服总机号码 |
公司对外公布的统一电话号码(如400-xxx) |
端口号(如9999) |
客服总机 (ServerSocket) |
前台的总机电话设备(绑定在公司号码上) |
|
客户来电 |
外部客户拨打公司总机号码 |
浏览器访问 |
分机电话 (Socket) |
总机转接给客服人员的专用分机 |
|
客服沟通 |
客服通过分机与客户对话 |
通过 |
重点概念详解
-
localhost
与127.0.0.1
:- •
它们就像你公司的两个别名,都指向同一栋大楼(你的电脑)。
- •
localhost
是域名,127.0.0.1
是IP地址,效果完全一样。就像“环球金融中心”和“浦东新区世纪大道100号”都指同一栋楼。
- •
-
端口号(如9999):
- •
这相当于公司的分机号。一栋大楼(你的电脑)里可以有很多部门(应用程序),每个部门有自己专属的分机号(端口)。
- •
常见端口:
80
(HTTP)、443
(HTTPS)、3306
(MySQL)... 你用的9999
就像给测试部门临时开的分机。
- •
-
ServerSocket
:- •
它不是大楼本身,而是专门负责接听某个分机号(端口)的电话机。
- •
当它
accept()
时,就像前台拿起听筒:“您好,这里是9999号客服,请问有什么可以帮您?”
- •
-
Socket
:- •
这是接通后建立的专用通信通道。想象客服代表拿起分机:“王先生您好,我是您的专属客服小李,您的问题请讲...”
- •
每个客户端连接都会创建一个新的
Socket
对象(就像每个来电都转接给一个新的客服代表)。
- •
端口是计算机网络中一个非常基础且重要的概念。它就像是计算机与外界通信的“门户”或“通道”,用来区分一台计算机上不同的网络应用程序或服务
为了更具体地理解,这里有一个经典的比喻:
-
IP 地址:相当于一栋大楼的地址(如:上海市浦东新区张江高科技园区博云路2号)。它唯一标识了网络上的某一台设备(计算机、服务器等)
。 -
端口号:相当于这栋大楼里每个房间的门牌号(如:301室、302室)。它唯一标识了这台设备上运行的不同网络程序或服务
。
数据包通过IP地址找到目标计算机后,再通过端口号就能知道自己应该被哪个具体的应用程序(如浏览器、游戏、邮箱客户端)接收和处理,从而实现“多任务并行”和“精准投递”。
类别 |
端口号范围 |
用途 |
常见示例 |
---|---|---|---|
知名端口 |
0 - 1023 |
预留给最通用的网络服务,由国际组织IANA分配和管理。 |
80: HTTP (网页浏览) 443: HTTPS (加密网页浏览) 21: FTP (文件传输) 22: SSH (安全远程登录) 25: SMTP (发送邮件) |
注册端口 |
1024 - 49151 |
松散地绑定于一些服务,许多用户或企业程序可以注册使用这些端口。 |
3306: MySQL数据库 3389: Windows远程桌面(RDP) 5432: PostgreSQL数据库 |
动态/私有端口 |
49152 - 65535 |
通常由客户端程序临时使用,操作系统会动态分配。 |
当你用浏览器访问网站时,你的电脑就会用一个此范围内的端口去连接服务器的80或443端口。 |
常见端口号及其用途
了解一些常见端口号有助于你理解日常的网络活动和服务配置:
端口号 |
协议 |
用途简述 |
---|---|---|
20/21 |
FTP |
文件传输协议(数据/控制) |
22 |
SSH |
安全外壳协议,用于加密远程管理 |
23 |
Telnet |
远程登录协议(明文传输,不安全) |
53 |
DNS |
域名系统,将域名解析为IP地址 |
67/68 |
DHCP |
动态主机配置协议,自动分配IP地址 |
80 |
HTTP |
超文本传输协议,用于网页浏览 |
110 |
POP3 |
邮局协议版本3,用于接收邮件 |
143 |
IMAP |
互联网消息访问协议,用于接收邮件 |
443 |
HTTPS |
安全超文本传输协议,加密的网页浏览 |
3306 |
MySQL |
MySQL数据库服务 |
3389 |
RDP |
远程桌面协议,用于Windows远程控制 |
三、BufferedReader 和 FileReader 的角色
BufferedReader bufferedReader = new BufferedReader(new FileReader("src/hello.html"));
这行代码做了两件事,涉及到两个类,它们是一种经典的“装饰器模式”的应用:
-
FileReader
(文件读取器):- •
它的角色是“基础工人”。它的任务很简单:负责打开文件,并按顺序一个字符一个字符地读取文件内容。
- •
缺点:如果这个“工人”每读一个字符就要跑去文件那里一次(即频繁进行底层I/O操作),当文件很大时,效率会非常低。
- •
-
BufferedReader
(缓冲读取器):- •
它的角色是“高效的调度员”。它自己不直接读文件,而是站在
FileReader
这个“工人”身边指挥它。 - •
工作原理:它会让
FileReader
一次性多读一些内容(比如读取8KB的数据)到一个缓冲区(可以看作是一个“临时仓库”)里。然后,当你调用bufferedReader.readLine()
时,它就不是慢吞吞地去文件里取,而是飞快地从“临时仓库”里把数据给你。 - •
优点:通过减少直接访问文件(磁盘)的次数,大大提高了读取效率,尤其是在读取大文件时。
它还提供了非常方便的readLine()
方法,可以直接读取一整行文本。
- •
所以,new BufferedReader(new FileReader(...))
这种套娃式的写法,意思就是:我想用一个高效的、带缓冲的读取器 (BufferedReader
),来读取一个文件 (FileReader
)。
总结
这就是一个简单的模拟的Web服务程序,大家可以自己试试模拟一下,了解浏览器和服务器是如何交互的,我会持续更新的,谢谢大家。