ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

计算机网络课程设计实战:基于Flask的电子购物网站与TCP/HTTP网络原理剖析

计算机网络课程设计实战:基于Flask的电子购物网站与TCP/HTTP网络原理剖析 简介在计算机网络课程设计中理解TCP/IP分层模型是构建可靠应用的基础。HTTP协议作为应用层最常用的协议定义了客户端与服务器之间的请求-响应语义而TCP则在传输层通过三次握手、确认重传等机制保障数据可靠到达。实际开发电子购物网站时Flask框架的请求处理、Session机制、数据库连接池等都对应着具体的网络知识点。通过一个可运行的网站项目可以清晰观察到浏览器与后端之间的连接建立、Cookie/Session的状态维持以及并发下单时的数据库事务与行锁。围绕课程设计场景从架构选型到代码实现再到Wireshark抓包验证这些网络原理在工程中的落地方式便一目了然能帮助读者把课本知识转化为可解释的实践能力。1. 这题目不是让你写个网页先弄懂课程设计在考什么“计算机网络课程设计电子购物网站设计”这个题目十个人有九个会理解成“写个购物网页”。我见过太多交上来的作业前端页面挺漂亮一问数据库为什么用连接池、TCP连接是在用户点击的哪一刻建立的就卡壳了。这门课设真正要检验的是你能不能把课本里的分层模型、HTTP协议、Socket通信串进一个能跑的业务系统里。购物网站只是载体网络才是考点。我这里按自己做课程设计的路线从架构、代码实现到抓包验证把每个网络知识点和代码对应的位置讲清楚也把最容易翻车的五个坑提前点掉。适合正在选题的学生也适合想复习网络编程的从业者。2. 选型与架构B/S结构是主流但网络层设计才是评分重点做课程设计的第一步不是打开IDE而是先在纸上把网络拓扑画出来。电子购物网站最常见的形态是B/S结构浏览器当客户端后端服务器处理业务数据库单独跑在一台机器上。老师不会只看你的页面能不能点他会从网络分层的角度追问你用户登录时到底发生了什么所以架构选型上你越能讲清楚每一层的作用得分越高。2.1 三层架构里的网络分层自顶向下设计购物网站逻辑上可以拆成三层表现层浏览器里的页面、业务层处理登录、下单、数据层存储商品和订单。对应到计算机网络就是应用层的HTTP、传输层的TCP、网络层的IP。最常见的教材比如谢希仁《计算机网络》讲的是自顶向下的思路先规定应用层的数据格式再决定传输层怎么传。开发时也按这个顺序来先把接口定好比如商品列表接口返回什么JSON、登录接口传什么字段然后再去写数据库表。这样到了答辩环节你可以直接画一张分层图说清楚一次“查看商品列表”的请求从浏览器到服务器每一层做了什么。HTTP报文在应用层生成TCP给数据分段并做可靠性保障网络层加上源IP和目标IP最后链路层转成帧。服务器收到后逐层剥掉头部最后把请求交给Flask路由处理。2.2 前端、后端、数据库之间的网络连接方式一张表理清端口与协议购物网站里同时存在两段网络连接浏览器到后端后端到数据库。这两段的协议和端口完全不同很多同学把它们混在一起导致出了问题也不知道查哪一段。连接路径协议默认端口典型地址浏览器 → Flask后端HTTP5000或80http://127.0.0.1:5000Flask后端 → MySQLMySQL协议基于TCP3306127.0.0.1:3306本机调试时都用127.0.0.1但到了答辩演示如果想让教室里的另一台电脑也访问你的网站浏览器里输入的就应该是后端的局域网IP比如http://192.168.1.101:5000。这要求后端必须监听0.0.0.0而不是默认的127.0.0.1。数据库连接也是一样的道理如果数据库不在本机host就要改成那台机器的IP。这里能自然引出DNS的作用如果不用IP而是配置一个域名就需要经过DNS解析这也是计算机网络课里的常考知识点。2.3 技术栈选择用Flask做最小闭环Java/Servlet也是备选我不太推荐课设直接用Spring Boot倒不是学不会而是框架封装太多你很难向老师解释清楚“Tomcat到底在哪一步监听了TCP端口”。Flask自带一个基于Werkzeug的开发服务器代码量小适合把网络通信原原本本露出来。当然学校如果指定Java用Servlet也完全没问题。Servlet本身不监听端口真正监听8080端口的是Tomcat。你写的doGet/doPost方法只是在处理已经解析好的HTTP请求。原理是通用的只是描述的时候要说清楚“Servlet容器负责TCP连接与HTTP解析”。我的习惯是用Flask依赖只需要flask和pymysql。安装命令是pip install flask pymysql dbutils这里dbutils是连接池工具后面会用到。如果老师只允许用标准库也可以只用socket手写一个HTTP服务器但工作量大很多Flask能让你把精力放在购物流程本身同时又保留了“监听端口、解析请求”的可解释性。3. 把购物网站拆成四个网络模块从商品浏览到订单提交购物网站看起来功能多但课程设计只要覆盖四个核心模块就能讲清楚全部网络知识点商品展示、用户登录、购物车、提交订单。这四个模块分别对应无状态HTTP请求、Cookie与Session、会话状态维持、TCP可靠传输与事务的一致性。3.1 商品展示一个GET请求背后的HTTP报文商品展示是唯一不用登录就能访问的页面它最能说明HTTP的基础知识。前端访问后端接口后端从数据库查商品再以JSON格式返回。Flask里的写法非常简单from flask import Flask, jsonify app Flask(__name__) app.route(/api/goods, methods[GET]) def goods_list(): goods [ {id: 1, name: 计算机网络第八版教材, price: 59.0, stock: 100}, {id: 2, name: 水晶头网线套装, price: 19.9, stock: 200}, ] return jsonify(code0, datagoods)这段代码里的methods[GET]限定了HTTP方法。浏览器访问这个地址时发出的请求行是GET /api/goods HTTP/1.1请求头里有Host、User-Agent、Accept等字段。服务器返回的响应状态码是200Content-Type是application/json。如果把路径写错Flask会返回404如果浏览器用POST请求访问这个接口会得到405 Method Not Allowed。这三个状态码是计算机网络期末复习里最常刷的基础题做课设时顺手就能验证。接口返回JSON前端拿到后渲染成商品卡片。一定要理解后端不需要返回整个HTML页面返回数据即可这样浏览器和后端的交互就变成了纯粹的数据交换更贴近现在主流的“前后端分离”。3.2 用户登录Cookie与Session的第一次握手登录模块是理解HTTP无状态特性的最佳例子。HTTP本身不记得你上一次干了什么所以需要一套会话机制。Flask里用session来保存登录状态from flask import Flask, request, session app.secret_key coursedesign-secret-key app.route(/login, methods[POST]) def login(): username request.form.get(username) password request.form.get(password) if username admin and password 123456: session[user_id] 1 return {code: 0, msg: 登录成功} return {code: 1, msg: 用户名或密码错误}, 401登录成功时服务器会在响应头里加一个Set-Cookie字段。浏览器收到后把这个Cookie保存下来后续每一次请求都会在请求头里带上它。服务器通过Cookie里的session id找到对应的用户信息。这就是常说的“HTTP无状态Cookie和Session给它加了个记忆”。需要特别说明的是Flask默认的session是把数据用签名算法压缩后直接存在Cookie里不是传统意义上的服务端session。如果你在答辩时说“session存在服务器内存”严格来说在Flask默认实现下不对。想严格走服务端session可以引入Flask-Session扩展把session数据放到Redis中。这一点是区分懂不懂原理的关键也是避免被问住的一个技巧。3.3 购物车无状态HTTP下的状态维持策略购物车可以做成前端存储也可以做成后端存储。课程设计里我更推荐放在session里因为这样可以顺带讲明白“为什么购物车不丢”app.route(/api/cart/add, methods[POST]) def add_cart(): user_id session.get(user_id) if not user_id: return {code: 1, msg: 请先登录}, 401 goods_id request.json.get(goods_id) quantity request.json.get(quantity, 1) cart session.get(cart, {}) cart[str(goods_id)] cart.get(str(goods_id), 0) quantity session[cart] cart return {code: 0, msg: 已加入购物车}购物车数据结构是字典商品ID为key数量为value。因为session跟着浏览器走只要session没过期刷新页面、关闭浏览器再打开购物车里的内容都还在。但Flask的session存在Cookie里有大小限制大约4KB所以不能塞太多商品。真实项目里一般用Redis存购物车Redis过期策略也值得在答辩时讲两句。另一种做法是把购物车存到localStorage不占用服务器内存但换一台电脑购物车就没了。写课程设计时我更侧重后一种方案因为它能自然引出“状态到底该放在客户端还是服务端”这个讨论。你把两种方案都准备好老师问哪边你都有话说。3.4 提交订单TCP可靠传输与数据库事务的配合下单是整个系统里网络和业务结合得最紧的一步。从浏览器发出订单请求到服务器返回订单号数据要经过TCP连接可靠传输。TCP负责分段、确认、重传保证用户提交的订单报文不会丢失。但这只保证了数据“传到了”没保证业务“算对了”。扣库存的正确性要靠数据库事务from mysql.connector import get_db_conn # 自定义连接池函数 app.route(/api/order/create, methods[POST]) def create_order(): user_id session.get(user_id) if not user_id: return {code: 1, msg: 未登录}, 401 cart session.get(cart, {}) if not cart: return {code: 1, msg: 购物车为空}, 400 conn get_db_conn() try: conn.begin() total_price 0 for goods_id, quantity in cart.items(): cur conn.cursor() cur.execute( SELECT price, stock FROM goods WHERE id%s FOR UPDATE, (goods_id,) ) row cur.fetchone() if row[stock] quantity: raise Exception(商品 %s 库存不足 % goods_id) cur.execute( UPDATE goods SET stockstock-%s WHERE id%s, (quantity, goods_id) ) total_price row[price] * quantity cur.execute( INSERT INTO orders(user_id, total_price) VALUES(%s, %s), (user_id, total_price) ) conn.commit() session[cart] {} return {code: 0, order_id: cur.lastrowid, total_price: total_price} except Exception as e: conn.rollback() return {code: 1, msg: str(e)}, 500 finally: conn.close()这里有一个关键参数SELECT ... FOR UPDATE。它会给匹配到的商品行加上行级锁两个并发请求同时操作同一件商品时后一个必须等前一个提交或回滚才能执行。没有这个锁超卖几乎必然发生。TCP保证你的请求不丢数据库事务保证你的库存不错两层一起构成了“可靠”的含义。4. 动手实现基于Flask的购物网站核心代码与网络监听前面把网络模块讲清楚了这一章直接给出一套能跑起来的工程结构。代码不多但每一段都对应一个网络知识点。完整的项目文件大致是app.py、db.py、init_db.sql、templates/index.html。这里我重点讲后端三个文件前端用一个fetch示例带过。4.1 数据库表设计用户、商品、订单与字符集陷阱先用SQL初始化三张表CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shop; CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE goods ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字符集统一用utf8mb4这是很多项目中文乱码的根源。如果你把表和字段的字符集设成latin1或utf8遇到商品名里的特殊符号就会变成问号。另外订单表外键指向用户表便于讲解关系型数据库的约束。值得一提的是InnoDB引擎才支持事务和行锁所以建表时必须指定ENGINEInnoDB。若用MyISAM上一章的库存扣减代码在并发下会失效。数据库连接不能直接写在每个请求里。如果用PyMySQL直连每个请求都要走一次TCP三次握手加MySQL认证成本太高。课程设计里最好用连接池from dbutils.pooled_db import PooledDB import pymysql pool PooledDB( creatorpymysql, maxconnections10, mincached2, maxcached5, blockingTrue, host127.0.0.1, port3306, userroot, passwordyourpassword, databaseshop, charsetutf8mb4, cursorclasspymysql.cursors.DictCursor ) def get_db_conn(): return pool.connection()maxconnections10表示并发最多10个MySQL连接超过后因为blockingTrue会排队等待。连接池预创建2条连接避免第一秒请求刚到的时去现建连接。你说出“这是为了复用与数据库之间的TCP连接”老师就知道你懂网络编程和连接管理的价值。4.2 后端路由与Socket监听app.run那行代码拆开看组装一个最简单的app入口from flask import Flask, request, session, jsonify import db app Flask(__name__) app.secret_key coursedesign-secret-key if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)host“0.0.0.0”表示监听本机所有网卡IP这样局域网内的其他设备可以用http://你的IP:5000访问。如果写成127.0.0.1就只有本机能进这也是很多人演示时翻车的原因。port5000是TCP监听端口可以换成80但Linux下1024以下端口需要root权限课设环境建议用5000或8080。debugFalse是必须的调试模式的浏览器交互会额外带来安全隐患也容易让人误以为服务只跑了一个进程。从Socket编程的角度看app.run的本质是创建了一个TCP服务端socket先bind((0.0.0.0, 5000))再listen(5)然后进入while循环等待客户端连接。浏览器连接成功后Werkzeug读取socket里的字节流按HTTP协议解析出方法、路径和请求头再根据路由表调用你的视图函数。如果你手写过socket HTTP服务器会更容易理解这一行代码背后发生的事情。4.3 登录、加购、下单一条业务链路的网络通信实现把前面模块整合成实际接口重点在“响应状态码”和“会话保持”。登录接口已写过这里再补一个通用的鉴权写法。比如查看购物车接口app.route(/api/cart, methods[GET]) def get_cart(): user_id session.get(user_id) if not user_id: return jsonify(code1, msg未登录), 401 cart session.get(cart, {}) return jsonify(code0, datacart)商品列表接口从数据库读取而不是写死在代码里app.route(/api/goods, methods[GET]) def goods_list(): conn db.get_db_conn() cur conn.cursor() cur.execute(SELECT id, name, price, stock FROM goods) rows cur.fetchall() conn.close() return jsonify(code0, datarows)前端用fetch调用时很多人会遇到“登录成功但购物车接口还是401”。原因大多是fetch默认不带Cookie。需要在请求里加credentialsfetch(/api/cart/add, { method: POST, headers: { Content-Type: application/json }, credentials: include, body: JSON.stringify({ goods_id: 1, quantity: 1 }) });credentials: include告诉浏览器即使是同源请求也把当前域名下的Cookie带上。少了这一行session永远为空这是一个很容易被忽略的网络细节。5. 避坑计算机网络课程设计最常见的五个翻车现场我把这些年批课设和帮人调试的踩坑记录整理一下全是从真实代码和网络抓包里看到的。每一条都对应一个网络层原因整理成“现象 → 原因 → 解决”给读者直接抄。5.1 页面Pending到超时监听地址和防火墙的锅现象浏览器访问127.0.0.1:5000没问题但换到另一台机器上用http://192.168.x.x:5000访问页面一直转圈最后显示连接超时。原因一种可能是Flask启动时host参数写的还是127.0.0.1只监听了回环接口外部数据包根本进不来。另一种可能是系统防火墙拦截了5000端口Windows和Linux默认规则不同教室里的公用电脑还经常锁着端口。解决先把app.run(host0.0.0.0)改好再到防火墙加一条入站规则放行TCP 5000。Linux下用sudo ufw allow 5000/tcpWindows在“高级安全Windows Defender防火墙”里新建入站规则。改完后可以在浏览器所在机器上先ping一下后端IP再telnet IP 5000能通说明网络层通不通就去查防火墙。5.2 中文乱码响应头字符集和数据库编码都没对齐现象商品名称在数据库里是中文查询出来交给前端显示变成“???”或者MySQL命令行里看到的是中文但HTTP返回乱码。原因数据库连接参数charset没设置PyMySQL默认可能是latin1导致从表里取出的utf8mb4字节被错误解码。另一种是后端返回响应时没有在Content-Type里指定UTF-8。Flask的jsonify默认会用UTF-8但如果你直接返回字符串浏览器就可能用GBK解析。解决连接池参数统一加上charsetutf8mb4创建数据库时也用utf8mb4。写一个测试接口用curl查看响应头里有没有Content-Type: application/json; charsetutf-8。没有的话给Flask配置app.json.ensure_ascii False并统一在响应头设置字符集。这一条是数据链路层“透明传输”思想的反面教材。5.3 购物车刷新就丢Session的存储位置和过期时间现象登录成功后往购物车加了两件商品刷新页面购物车变成空的但session的user_id还在登录状态没丢。原因Flask默认session存在浏览器Cookie里Cookie有大小上限大约4KB。如果你往session里放的购物车字典太大或者商品ID太多超过了4KB浏览器会拒绝接收或截断。另一种可能是session过期时间设置太短默认浏览器关闭就过期你刷新页面的方式触发了新的会话。解决把购物车从session搬到数据库表或者用Redis。课程设计为了省事可以把购物车存入一个cart表用user_id做外键这样既不占Cookie空间也能演示“服务端状态”。如果坚持用session至少把session的过期时间调长app.permanent_session_lifetime timedelta(hours2)登录后设置session.permanent True。到答辩时你要说清楚“Flask默认session在Cookie里不是内存”。5.4 库存变负数并发请求下的数据库行锁现象商品库存显示100件但两个用户同时下单买100件都提示成功查数据库发现库存变成了-100。原因扣库存的代码先查stock再算新库存最后UPDATE。两个并发请求同时读到stock100各自计算出0然后互相覆盖最后写回一个错误结果。TCP只保证请求完整到达不保证业务计算顺序这属于并发控制问题。解决在查询库存时加FOR UPDATE让事务持有行锁后一个事务等待前一个事务提交后再读。这是最有效也最容易在课程设计里讲清楚的办法。还有一个隐藏问题是PyMySQL默认autocommitTrue如果你不显式conn.begin()前面的SELECT FOR UPDATE根本没生效所以要牢牢把关在事务里。5.5 局域网其他机器访问不了调试模式只绑定了本机回环现象代码里明明写了host“0.0.0.0”别人访问还是不行但你自己访问正常。原因一种可能是Flash自带的Werkzeug调试模式在debugTrue时会启用调试器进程有些版本还会要求输入PIN码外部访问被卡在PIN验证上。另一种可能是Windows防火墙对Python程序弹出了拦截窗口你没点“允许访问”导致外部连接被静默丢弃。解决真机演示时一定用debugFalse启动避免调试器纠结。然后确认监听地址netstat -ano | findstr :5000看看监听地址是不是0.0.0.0:5000如果是127.0.0.1:5000说明代码没生效或缓存了旧进程。用完netstat查到PID后用任务管理器结束残留进程再重启。这个问题我在课设验收时见了不下十次多半是改完代码后没重启Python进程。6. 把课程设计答进优秀抓包验证网络交互与答辩自检如果你想让课设从“能跑”变成“能讲”建议花半小时做一轮网络级验证。6.1 用Wireshark抓三次握手眼见为实的TCP连接启动Wireshark选择回环接口或局域网网卡访问一次商品列表接口然后停止抓包。在过滤栏输入tcp.port 5000你能看到三次握手的三个包SYN、SYNACK、ACK。再往后能看到带HTTP协议的包右键“追踪流→HTTP流”能看到完整的请求行和响应头。答辩时直接把这些截图放进PPT比空口讲“TCP是可靠的”有说服力得多。6.2 用curl和开发者工具验证HTTP状态码不用打开浏览器直接用curl就能模拟各种请求curl -i http://127.0.0.1:5000/api/goods返回的状态码、Content-Type、响应体都在终端里。再用curl -X POST http://127.0.0.1:5000/login -d usernameadminpassword123456 -c cookies.txt模拟登录并保存Cookie之后加购物车都带上这个Cookie文件就能验证Session是否真的被维持。浏览器开发者工具里的Network面板也能看到每个请求的状态码把状态码和原因对应起来本身就是计算机网络的复习。6.3 一份答辩自检清单我给自己学生出的自检问题就这五个三次握手为什么需要第三次HTTP为什么是有状态还是无状态的Cookie和Session有什么区别购物车数据为什么刷新不丢两个用户同时下单库存会超卖吗如果你能一边指着自己的代码一边回答出“HTTP无状态所以我们用Session记录用户购物车存在服务端所以刷新不丢扣库存用事务加行锁所以不会超卖”这个课设离优秀就不远了。我当年吃亏在只讲功能不讲数据后来习惯做任何项目都先画一遍数据流这个习惯帮我省了很多次翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表