news 2026/8/22 12:57:33

从零构建Web服务器:50行代码理解HTTP、路由与中间件核心原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零构建Web服务器:50行代码理解HTTP、路由与中间件核心原理

只用不到 50 行代码,就能从零搭建一个功能完整的 Web 服务器?这听起来像是天方夜谭,但却是现代 Web 开发框架带给我们的现实。如果你对 Node.js 的http模块、Express 或 Koa 的中间件机制感到好奇,或者觉得它们过于“黑盒”,那么这篇文章就是为你准备的。

我们常常使用现成的框架,却很少思考一个请求从网络到达我们的代码,再到返回响应,中间到底发生了什么。今天,我们将通过一个名为Hono的轻量级框架,亲手“搓”一个 Web 服务器,来彻底理解 Web API 与 Web 服务器的核心工作原理。这不仅仅是学习一个新工具,更是一次对 HTTP 协议、请求/响应模型和中间件思想的深度剖析。

读完本文,你将能清晰地回答:一个 Web 服务器最核心的职责是什么?Hono 是如何用极简的 API 封装这些职责的?以及,如何用不到 50 行代码,实现路由、中间件、静态文件服务等常见功能。我们不止于“是什么”,更要探究“为什么”和“怎么做”,让你拥有从底层理解并构建 Web 服务的能力。

1. 为什么我们需要亲手“搓”一个 Web 服务器?

在 Express、NestJS、Fastify 大行其道的今天,为什么还要费心去理解底层,甚至自己动手写一个简单的服务器?原因有三:

第一,破除“黑盒”恐惧,建立掌控感。当你遇到一个棘手的 CORS 问题、一个诡异的中间件执行顺序 Bug,或者性能瓶颈时,如果对请求的生命周期一无所知,调试将如同盲人摸象。亲手实现一次,你会知道 Headers 在哪里被解析,Body 如何被读取,响应如何被序列化发送。这种掌控感是高级开发者区别于初级开发者的关键。

第二,理解框架设计的精髓。所有优秀的框架都是对通用模式的优雅抽象。Hono 以其极简和高效著称,它的设计本身就是一堂生动的软件架构课。通过拆解它,你能学到如何设计可组合的 API、如何实现高性能的路由匹配、以及中间件模式的本质是什么。这些知识是跨框架的,能让你更快地掌握任何新出现的工具。

第三,应对极简场景和边缘环境。不是每个项目都需要庞大的框架。在 Serverless 函数(如 AWS Lambda, Cloudflare Workers)、边缘计算、CLI 工具的内置服务,或者一个快速验证的原型中,一个几十行代码、零依赖的微型服务器可能是最优雅、启动最快、冷启动性能最好的选择。Hono 正是为此类场景而生。

本文将使用 Hono 作为我们的“手术刀”,因为它足够简单、纯粹,且设计理念先进,能让我们在最小的认知负荷下,触及 Web 服务器最核心的本质。

2. Web 服务器核心概念:请求、响应与路由

在动手写代码之前,我们必须统一几个核心概念。一个 Web 服务器,无论多么复杂,其根本任务可以简化为一个公式:

接收一个 HTTP 请求 (Request) → 根据规则处理它 → 返回一个 HTTP 响应 (Response)。

让我们拆解这个公式中的关键组件:

HTTP 请求 (Request):

  • 方法 (Method):GET,POST,PUT,DELETE等。定义了客户端的意图。
  • 路径 (Path):/api/users/。指定了请求的资源位置。
  • 请求头 (Headers):包含元数据,如Content-Type(告知服务器发送的数据格式)、Authorization(认证信息)。
  • 请求体 (Body):POSTPUT请求中携带的实际数据,通常是 JSON、表单数据或纯文本。

HTTP 响应 (Response):

  • 状态码 (Status Code):200(成功)、404(未找到)、500(服务器错误) 等。快速告知客户端结果。
  • 响应头 (Headers):服务器返回的元数据,如Content-Type(告知客户端返回的数据格式)。
  • 响应体 (Body):返回给客户端的实际内容,如 HTML、JSON 或文件流。

路由 (Routing):这是服务器的“调度中心”。它的工作是将请求的方法和路径,映射到一段特定的处理代码(通常是一个函数,称为“处理器”或“控制器”)。 例如:

  • GET /→ 返回首页 HTML
  • GET /api/users→ 返回用户列表 JSON
  • POST /api/users→ 创建新用户

中间件 (Middleware):这是现代 Web 框架的灵魂。你可以把它想象成请求-响应流水线上的“加工站”。一个请求在到达最终的路由处理器之前,可能会经过多个中间件进行预处理(如日志记录、身份验证、解析请求体);同样,响应在发回客户端之前,也可能经过中间件进行后处理(如压缩、添加统一头部)。

理解了这些,我们就有了清晰的蓝图:我们的代码需要创建一个能监听网络端口的服务,解析传入的 HTTP 请求,根据定义好的路由和中间件规则执行相应逻辑,并构造 HTTP 响应发送回去。

3. 环境准备:Node.js 与 Hono

我们的实践基于 Node.js 环境。请确保你的系统已安装 Node.js(版本 16 或以上,推荐 LTS 版本)。你可以通过以下命令检查:

node --version npm --version

接下来,创建一个新的项目目录并初始化:

mkdir my-hono-server cd my-hono-server npm init -y

现在,安装我们唯一的依赖——Hono:

npm install hono

是的,你没有看错,只有这一个依赖。Hono 本身极其轻量,并且为了兼容各种运行时(Node.js, Deno, Bun, Cloudflare Workers等),它尽可能地减少了外部依赖。这就是我们能用极少代码实现功能的关键。

4. 第一行代码:创建服务器并响应“Hello World”

让我们从一个绝对最小的例子开始,感受 Hono 的简洁。创建一个名为index.js的文件。

// index.js import { Hono } from 'hono' // 1. 创建一个 Hono 应用实例 const app = new Hono() // 2. 定义一个路由:当收到 GET 请求,且路径为 '/' 时,执行这个函数 app.get('/', (c) => { // 参数 `c` 代表 Context(上下文),它封装了 Request 和 Response // 直接返回一个字符串,Hono 会自动将其设置为响应体,状态码为 200 return c.text('Hello, Hono!') }) // 3. 导出这个应用,以便在服务器环境中使用 export default app

这段代码做了什么?

  1. import { Hono } from 'hono': 引入 Hono 库。
  2. const app = new Hono(): 初始化一个应用。这个app对象是我们所有路由和中间件的载体。
  3. app.get('/', (c) => {...}): 定义一个路由。app.get表示监听 GET 方法。第一个参数'/'是路径。第二个参数是一个处理函数,它接收一个上下文对象c
  4. return c.text('Hello, Hono!'): 通过上下文ctext方法,我们创建了一个内容为纯文本的响应。这是 Hono 提供的响应辅助函数之一,非常直观。

但是,这段代码还不能直接运行,因为它只定义了应用逻辑,没有启动一个真正的 HTTP 服务器来监听端口。我们需要一个“适配器”来将它连接到 Node.js 的http模块。为此,我们创建一个server.js文件。

// server.js import { serve } from '@hono/node-server' // Node.js 环境专用的 serve 函数 import app from './index.js' // 导入我们上面定义的应用 // 启动服务器,监听 3000 端口 serve({ fetch: app.fetch, // 将 Hono 应用的 fetch 方法交给 serve 函数 port: 3000 }, (info) => { console.log(`Server is running on http://localhost:${info.port}`) })

现在,在package.json中添加一个启动脚本,并确保使用 ES 模块:

// package.json { "name": "my-hono-server", "version": "1.0.0", "type": "module", // 关键!声明为 ES 模块 "scripts": { "start": "node server.js" }, "dependencies": { "hono": "^4.0.0", "@hono/node-server": "^1.0.0" } }

运行npm install确保@hono/node-server被安装,然后启动服务器:

npm start

打开浏览器,访问http://localhost:3000,你将看到Hello, Hono!

恭喜!你刚刚用不到 10 行核心逻辑代码,完成了一个 Web 服务器的创建、路由定义和请求响应。这已经是一个合法且可用的 Web 服务器了。

5. 核心功能扩展:路由、JSON、参数与中间件

一个“玩具”服务器显然不够。让我们用极少的代码,为它添加真实项目中最常用的功能。

5.1 处理不同的 HTTP 方法与返回 JSON

Web API 的核心就是基于不同方法和路径进行 CRUD 操作。Hono 的 API 设计与此完美契合。

// 在 index.js 的 app 定义后继续添加 // 模拟一个内存中的“数据库” let books = [ { id: 1, title: 'The Hobbit', author: 'J.R.R. Tolkien' }, { id: 2, title: 'The Catcher in the Rye', author: 'J.D. Salinger' } ]; // GET /api/books - 获取所有书籍 app.get('/api/books', (c) => { // 使用 c.json() 辅助函数返回 JSON,自动设置 Content-Type: application/json return c.json(books); }); // GET /api/books/:id - 根据ID获取单本书籍 app.get('/api/books/:id', (c) => { const id = parseInt(c.req.param('id')); // 从路径参数中获取 id const book = books.find(b => b.id === id); if (!book) { // 未找到时,返回 404 状态码和 JSON 错误信息 return c.json({ error: 'Book not found' }, 404); } return c.json(book); }); // POST /api/books - 创建新书籍 app.post('/api/books', async (c) => { try { // 从请求体中解析 JSON 数据。使用 async/await 因为 body 解析可能是异步的。 const newBook = await c.req.json(); // 简单的数据验证 if (!newBook.title || !newBook.author) { return c.json({ error: 'Title and author are required' }, 400); } newBook.id = books.length + 1; books.push(newBook); // 创建成功,返回 201 状态码和新创建的资源 return c.json(newBook, 201); } catch (error) { // 如果请求体不是合法的 JSON,返回 400 错误 return c.json({ error: 'Invalid JSON' }, 400); } });

代码解读:

  • 路径参数::id是一个动态片段。通过c.req.param('id')可以获取到实际的值(如/api/books/1中的1)。
  • 请求体解析:c.req.json()是一个异步方法,用于解析application/json格式的请求体。这是 Hono 对 Fetch API 标准的实现,非常现代和直观。
  • 状态码设置:c.json(data, status)的第二个参数可以方便地设置 HTTP 状态码。
  • 错误处理:我们在处理器内部进行了简单的输入验证和错误返回,这是构建健壮 API 的基础。

5.2 使用中间件:日志、CORS 与认证

中间件是 Hono 的超级能力。它允许你在请求到达最终处理器之前或之后插入逻辑。

import { Hono } from 'hono' import { logger } from 'hono/logger' // 引入官方日志中间件 import { cors } from 'hono/cors' // 引入官方 CORS 中间件 const app = new Hono() // 1. 应用级中间件:对所有请求生效 // 日志中间件,记录每个请求的方法、路径和响应时间 app.use('*', logger()) // CORS 中间件,处理跨域请求 app.use('*', cors({ origin: 'http://localhost:5173', // 允许的前端地址 allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], })) // 2. 路由级中间件:只对特定路径生效 // 一个简单的认证中间件 const authMiddleware = async (c, next) => { // 从请求头中获取 API 密钥 const apiKey = c.req.header('x-api-key'); // 这里进行简单的校验(实际项目中应从数据库或环境变量校验) if (apiKey !== 'my-secret-key') { return c.json({ error: 'Unauthorized' }, 401); } // 校验通过,调用 next() 将控制权交给下一个中间件或路由处理器 await next(); }; // 将认证中间件应用于所有 /api/admin/* 路由 app.use('/api/admin/*', authMiddleware); // 一个受保护的路由 app.get('/api/admin/dashboard', (c) => { return c.json({ message: 'Welcome to the admin dashboard!' }); }); // 之前定义的非 /admin 路由不受影响 app.get('/api/books', (c) => { // ... 之前的代码 });

中间件原理剖析:一个中间件函数接收(c, next)两个参数。

  • c: 与路由处理器中相同的上下文对象。
  • next: 一个函数,调用它意味着“执行链中的下一个中间件或最终的路由处理器”。
  • 执行顺序至关重要。next()之前的代码,在请求到达路由处理器之前执行(预处理)。在next()之后(如果await next())的代码,则在路由处理器执行之后、响应返回之前执行(后处理)。这种模式称为“洋葱模型”。

通过组合中间件,你可以以声明式和可复用的方式,为应用添加日志、安全、压缩、会话管理等各类功能,而无需污染核心业务逻辑。

6. 静态文件服务与模板渲染

一个完整的 Web 服务器常常需要提供静态文件(如 CSS、JS、图片)或渲染动态 HTML。

6.1 提供静态文件服务

Hono 提供了一个方便的中间件来服务./static目录下的文件。

import { serveStatic } from 'hono/node-server' // 注意:这是 Node.js 版本的静态文件服务 // 假设项目根目录下有一个 `public` 文件夹,里面存放静态资源 app.use('/static/*', serveStatic({ root: './public' })) app.use('/favicon.ico', serveStatic({ path: './public/favicon.ico' })) // 现在,访问 http://localhost:3000/static/style.css 将返回 ./public/style.css 文件

6.2 使用 JSX 渲染动态 HTML(可选)

Hono 的一个特色是内置支持 JSX(无需 React),可以非常直观地渲染 HTML。这需要一点配置。

首先,安装必要的依赖(如果你需要此功能):

npm install --save-dev @hono/jsx-renderer

然后,更新index.js

import { Hono } from 'hono' import { jsxRenderer } from 'hono/jsx-renderer' const app = new Hono() // 配置 JSX 渲染器作为中间件 app.use('*', jsxRenderer()) // 定义一个使用 JSX 的路由 app.get('/page', (c) => { const name = c.req.query('name') || 'Visitor' // 获取查询参数 // 直接返回 JSX,它会被自动渲染成 HTML 字符串 return c.render( <html> <head> <title>Hono JSX Page</title> </head> <body> <h1>Hello, {name}!</h1> <p>This is server-side rendered with JSX.</p> </body> </html> ) })

注意:要使 JSX 语法生效,你需要在项目中使用支持 JSX 的构建工具(如tsxbun或在package.json中配置"type": "module"并使用合适的转译器)。对于快速原型,使用 Bun 运行时是体验此功能最简单的方式。

7. 完整示例代码与运行验证

让我们将以上所有功能整合到一个完整的、可运行的示例中。以下是最终的index.js文件:

// index.js - 完整示例 import { Hono } from 'hono' import { logger } from 'hono/logger' import { cors } from 'hono/cors' // 注意:serveStatic 仅在 Node.js 环境下用于演示,实际部署可能不同 import { serveStatic } from '@hono/node-server' const app = new Hono() // 全局中间件 app.use('*', logger()) app.use('*', cors({ origin: 'http://localhost:5173', allowMethods: ['GET', 'POST', 'PUT', 'DELETE'], })) // 静态文件服务 (确保项目根目录存在 ./public 文件夹) app.use('/static/*', serveStatic({ root: './public' })) // 内存数据存储 let items = [{ id: 1, name: 'Sample Item' }] // 1. 基础路由 app.get('/', (c) => c.text('Hono Server is running!')) // 2. 完整的 CRUD API // 获取所有 app.get('/api/items', (c) => c.json(items)) // 获取单个 app.get('/api/items/:id', (c) => { const id = parseInt(c.req.param('id')) const item = items.find(i => i.id === id) return item ? c.json(item) : c.json({ error: 'Not Found' }, 404) }) // 创建 app.post('/api/items', async (c) => { const newItem = await c.req.json() if (!newItem.name) return c.json({ error: 'Name required' }, 400) newItem.id = items.length + 1 items.push(newItem) return c.json(newItem, 201) }) // 更新 app.put('/api/items/:id', async (c) => { const id = parseInt(c.req.param('id')) const index = items.findIndex(i => i.id === id) if (index === -1) return c.json({ error: 'Not Found' }, 404) const updatedData = await c.req.json() items[index] = { ...items[index], ...updatedData } return c.json(items[index]) }) // 删除 app.delete('/api/items/:id', (c) => { const id = parseInt(c.req.param('id')) const initialLength = items.length items = items.filter(i => i.id !== id) return items.length < initialLength ? c.json({ message: 'Deleted' }) : c.json({ error: 'Not Found' }, 404) }) // 3. 带认证的 Admin 路由 const authMiddleware = async (c, next) => { if (c.req.header('x-api-key') !== 'secret123') { return c.json({ error: 'Unauthorized' }, 401) } await next() } app.use('/admin/*', authMiddleware) app.get('/admin/stats', (c) => c.json({ count: items.length })) // 4. 错误处理示例 app.notFound((c) => c.json({ error: 'Route not found' }, 404)) app.onError((err, c) => { console.error(err) return c.json({ error: 'Internal server error' }, 500) }) export default app

使用之前创建的server.js启动服务。现在,你可以使用curl、Postman 或浏览器来全面测试这个 API 服务器:

# 1. 启动服务器 npm start # 2. 测试基础路由 (新终端) curl http://localhost:3000/ # 输出: Hono Server is running! # 3. 测试获取所有 Items curl http://localhost:3000/api/items # 输出: [{"id":1,"name":"Sample Item"}] # 4. 测试创建新 Item curl -X POST http://localhost:3000/api/items \ -H "Content-Type: application/json" \ -d '{"name":"New Item"}' # 输出: {"id":2,"name":"New Item"} (状态码 201) # 5. 测试受保护的路由 (无密钥) curl http://localhost:3000/admin/stats # 输出: {"error":"Unauthorized"} (状态码 401) # 6. 测试受保护的路由 (带密钥) curl -H "x-api-key: secret123" http://localhost:3000/admin/stats # 输出: {"count":2} # 7. 测试不存在的路由 curl http://localhost:3000/not-exist # 输出: {"error":"Route not found"} (状态码 404)

通过这一系列测试,你已经验证了一个具备路由、CRUD、中间件、认证和错误处理等核心功能的 Web 服务器。全部核心逻辑代码,确实在 50 行以内。

8. 常见问题与排查思路

在实践过程中,你可能会遇到以下问题:

问题现象可能原因排查方式解决方案
服务器无法启动,提示Cannot find module1. 依赖未安装。
2. 使用了错误的导入路径(如未安装@hono/node-server)。
1. 检查node_modules是否存在。
2. 检查package.json中的dependencies
运行npm install。确保安装了hono@hono/node-server
访问路由返回4041. 路由路径定义错误(大小写、斜杠)。
2. 请求方法(GET/POST)不匹配。
3. 中间件拦截了请求未调用next()
1. 仔细核对浏览器/工具中的 URL 和方法。
2. 在路由处理函数开头添加console.log确认是否执行。
修正路由定义。确保中间件在通过时调用了await next()
POST请求无法解析req.json()1. 请求头未设置Content-Type: application/json
2. 请求体不是合法的 JSON 格式。
1. 使用开发者工具或 curl 检查请求头。
2. 在try...catch中包裹c.req.json()
确保客户端发送正确的请求头。在服务器端添加错误处理。
静态文件返回4041.serveStatic中间件根路径配置错误。
2. 请求的静态文件在目录中不存在。
1. 确认root选项指向的目录是否存在。
2. 检查文件路径是否匹配(如/static/前缀)。
调整root路径。确保请求路径与中间件匹配规则一致。
CORS 请求失败1. 未配置 CORS 中间件。
2. CORS 配置(如origin)与前端地址不匹配。
3. 复杂请求(如带自定义头)未处理OPTIONS方法。
1. 浏览器控制台查看 CORS 错误信息。
2. 检查前端发起请求的源(Origin)。
正确配置cors()中间件,确保allowMethods包含OPTIONS
使用 JSX 报语法错误1. 项目未配置支持 JSX 的运行时或构建工具。
2. 文件扩展名或配置不正确。
1. 检查是否安装了@hono/jsx-renderer
2. 确认运行环境(如使用bun或配置了tsx)。
对于简单测试,使用 Bun 运行时 (bun run server.js)。或配置构建工具。

9. 最佳实践与工程化建议

将这个“手搓”的服务器用于真实项目,你还需要考虑以下几点:

1. 项目结构:当代码超过一个文件时,合理的组织至关重要。推荐按功能模块划分:

src/ ├── index.js # 应用入口,初始化 Hono 并加载中间件 ├── routes/ # 路由定义 │ ├── api/ │ │ ├── items.js │ │ └── users.js │ └── web.js ├── middleware/ # 自定义中间件 │ ├── auth.js │ └── logger.js └── utils/ # 工具函数

index.js中,使用app.route()方法挂载子路由:

import items from './routes/api/items.js' app.route('/api', items)

2. 环境配置与安全:

  • 密钥管理:永远不要将 API 密钥、数据库密码等硬编码在代码中。使用dotenv库从.env文件或环境变量读取。
    npm install dotenv
    import 'dotenv/config'; const API_KEY = process.env.API_KEY;
  • 输入验证与清理:对用户输入(路径参数、查询参数、请求体)进行严格的验证和清理,防止注入攻击。可以考虑使用zod等验证库。

3. 错误处理标准化:创建一个统一的错误响应格式和全局错误处理中间件,让 API 的错误反馈更友好、一致。

app.notFound((c) => { return c.json({ code: 404, message: 'Resource not found' }, 404) }) app.onError((err, c) => { console.error(err) // 可以根据 err 的类型返回不同的状态码和信息 return c.json({ code: 500, message: 'Internal server error' }, 500) })

4. 日志与监控:

  • 使用logger()中间件记录访问日志。
  • 对于生产环境,考虑将日志结构化并输出到文件或日志服务(如 Winston, Pino)。
  • 添加健康检查端点 (GET /health),方便监控系统探测服务状态。

5. 性能与部署:

  • Hono 本身性能极高。瓶颈通常出现在 I/O(数据库、外部 API 调用)。确保这些操作是异步的。
  • 在 Node.js 环境部署时,使用pm2systemd进行进程管理,实现自动重启和负载均衡。
  • 考虑将无状态的应用部署到 Serverless 平台(如 Vercel, Netlify)或边缘网络(Cloudflare Workers),Hono 对此有原生支持。

通过这次“手搓” Web 服务器的旅程,我们清晰地看到,一个现代 Web 服务器的核心并非深不可测的“黑魔法”,而是一系列对 HTTP 协议和请求/响应模型的直观抽象。Hono 以其极简的 API 设计,完美地充当了我们理解这些概念的透镜。

你学到的不仅仅是 Hono 的用法,更是路由、中间件、上下文、请求/响应生命周期这些构成所有 Web 框架基石的通用模式。无论你将来使用 Express、Koa、Fastify 还是其他任何框架,这些核心概念都是相通的。

下一步,你可以尝试:

  1. 连接真实数据库:将内存数组items替换为对 PostgreSQL、MongoDB 或 SQLite 的操作。
  2. 实现用户认证:使用bcrypt哈希密码,用jsonwebtoken(JWT) 实现 token 认证。
  3. 编写单元测试:使用jestvitest为你的路由处理器编写测试。
  4. 探索 Hono 生态:了解其对于 WebSocket、Server-Sent Events (SSE)、OpenAPI 集成等更多高级功能的支持。

记住,理解底层原理的最佳方式就是动手实现。现在,你已经拥有了从零构建和定制 Web 服务的自信与能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/22 12:48:09

ThinkPad X390 装 macOS 完整指南:一份开箱即用的 OpenCore EFI

ThinkPad X390 装 macOS 完整指南&#xff1a;一份开箱即用的 OpenCore EFI 【免费下载链接】ThinkpadX390-Opencore-EFI macOS Catalina & Big Sur & Monterey on ThinkPad X390 (Hackintosh) 项目地址: https://gitcode.com/gh_mirrors/th/ThinkpadX390-Opencore-E…

作者头像 李华
网站建设 2026/8/22 12:47:32

题解:洛谷 P8901 [USACO22DEC] Circular Barn S

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大家订阅我的专栏:算法…

作者头像 李华
网站建设 2026/8/22 12:45:23

【计算机网络 实验七】LINUX环境下UDP通信程序设计

实验记录&#xff0c;可能存在错误&#xff0c;仅供学习参考第1关UDP套接字创建与端口绑定1、双击打开桌面上的工作区文件夹workspace&#xff0c;再双击实训文件夹myshixun&#xff0c;打开文件message7-1.txt。2、在/home/headless/Desktop/workspace/myshixun/client/目录下…

作者头像 李华