COMP5416 Week 07 Socket 编程实验与期中反馈总结

COMP5416 Week 07 总结:Socket 编程实验 + 期中反馈

课程:COMP5416/COMP4416 — Advanced Network Technologies,Semester 2 2026 讲师:Professor Vincent Gramoli 来源:W7_Lab.pdf / W7_Lab_solution.pdf(Lab 5: TCP Socket)、ClientServerCode.zip(4 个 Python 文件)、Exam Prep/W7-QuizFeedback.pdf(期中 quiz 讲评,8 页)。本周没有转录稿。

本周讲课内容:Week 7–8 共用一份 Multimedia 讲义(W7-Multimedia.pdf,放在 Week 8 文件夹,文件名标注 "Weeks 7 and 8"), 讲义内容统一写在 Week 08 Multimedia 讲课总结,本文只整理 Week 7 独有的实验和 quiz 反馈,避免重复。

为什么单独成篇:这个 lab 是动手写代码,和 Week 6 的 TCP 理论关系密切(能在 Wireshark 里亲眼看到三次握手), 但内容(socket API)在 Week 06 TCP 讲义里只出现了两行 Java 伪代码,单独写更清楚。


第一部分:Socket 是什么

一、Socket 的位置

Socket(套接字)是应用进程与传输层之间的「门」:应用把数据从门里递出去,由操作系统里的 TCP/UDP 负责送到对面那扇门。

层 谁控制
应用层(进程) 应用开发者
─── socket ─── 两者的接口
传输层及以下 操作系统

两种 socket 对应两种传输服务:

TCP socket UDP socket
Python 类型 socket(AF_INET, SOCK_STREAM) socket(AF_INET, SOCK_DGRAM)
连接 先建连接(三次握手) 无连接
每次发送是否带目的地址 否(连接已绑定对方) 是(sendto(msg, (ip, port)))
服务器端套接字数 欢迎套接字 1 个 + 每客户 1 个连接套接字 1 个套接字服务所有客户
数据形态 字节流 数据报

这一张表就是 Week 05 「UDP 二元组 vs TCP 四元组」在代码层面的样子。


第二部分:TCP 客户端与服务器代码逐行解读

二、TCPLabServer.py

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(1)
print ("The server is ready to receive")
while 1:
connectionSocket, addr = serverSocket.accept()
sentence = connectionSocket.recv(1024)
capitalizedSentence = sentence.decode('utf-8').upper().encode('utf-8')
connectionSocket.send(capitalizedSentence)
connectionSocket.close()
行 作用 对应的 TCP 行为
socket(AF_INET, SOCK_STREAM) 建 IPv4 + TCP 套接字 —
bind(('', 12000)) 绑定端口 12000;'' 表示监听本机所有网卡地址 确定「目的端口」
listen(1) 进入 LISTEN 状态,参数是等待队列长度 服务器 LISTEN
accept() 阻塞,直到有客户端完成三次握手;返回一个新的 connectionSocket SYN RCVD → ESTAB
recv(1024) 最多读 1024 字节 从接收缓冲取数据(rwnd 随之变化)
send(...) 回送大写字符串 —
connectionSocket.close() 只关这个客户的连接,欢迎套接字继续监听 发 FIN

两个套接字的分工(高频概念题): - serverSocket(欢迎套接字 welcoming socket):只负责「接门铃」,一直在 LISTEN。 - connectionSocket(连接套接字):每个客户一个,由四元组唯一标识,真正收发数据。

这正是 Week 05 讲的「Web 服务器为每个连接的客户建立不同的套接字」。

三、TCPLabClient.py

from socket import *
serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName,serverPort))
sentence = input('Input lowercase sentence:')
clientSocket.send(sentence.encode('utf-8')) ## Do not specify serverName,serverPort
modifiedSentence = clientSocket.recv(1024)
print ('From Server:', modifiedSentence.decode('utf-8'))
clientSocket.close()
行 作用 对应的 TCP 行为
connect((serverName, serverPort)) 发起连接 触发三次握手(SYN → SYNACK → ACK)
send(...) 发数据 注释强调:不用再写服务器地址,因为连接已经建立
recv(1024) 等服务器回复 —
close() 关闭 发 FIN,进入 FIN_WAIT_1

客户端没有 bind():操作系统会自动分配一个临时(ephemeral)源端口。 encode/decode('utf-8'):Python 3 的 socket 只收发 bytes,字符串必须编码 —— 讲义里的旧代码如果直接 send(sentence) 会报错,这就是 lab 说「可能需要修改代码中的错误」的原因之一。


第三部分:UDP 客户端与服务器

四、UDPLabServer.py

from socket import *
serverPort = 12003
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
print ("The server is ready to receive")
while 1:
message, clientAddress = serverSocket.recvfrom(2048)
message=message.decode('utf-8')
modifiedMessage = message.upper()
serverSocket.sendto(modifiedMessage.encode('utf-8'), clientAddress)

五、UDPLabClient.py

from socket import *
serverName = '127.0.0.1'
serverPort = 12003
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = input('Input lowercase sentence:')
message=message.encode('utf-8')
clientSocket.sendto(message,(serverName, serverPort))
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print (modifiedMessage.decode('utf-8'))
clientSocket.close()

六、TCP 与 UDP 代码的逐项差别

对比点 TCP 版 UDP 版
socket 类型 SOCK_STREAM SOCK_DGRAM
服务器 listen() / accept() 有 没有
客户端 connect() 有 没有
发送 send(data) sendto(data, (ip, port))
接收 recv(n) → 只返回数据 recvfrom(n) → 返回 (数据, 发送方地址)
服务器怎么知道回给谁 连接套接字天然绑定了对方 从 recvfrom 拿到 clientAddress 再 sendto 回去
端口 12000 12003(两个程序可同时跑,不冲突)

UDP 服务器只有一个套接字,所有客户的数据报都进这一个套接字 —— 正是 Week 05 讲义 p12 的「目的 IP、端口相同但源不同的分组,会被导向同一个套接字」。

UDP 版不会因为服务器没开而在 sendto 时报错:数据报直接丢进网络就不管了,客户端会卡在 recvfrom 上一直等; TCP 版服务器没开时,connect() 立刻失败(Connection refused),因为握手的 SYN 会被对方回 RST。


第四部分:Lab 题目与官方答案

Exercise 1:同一台机器上跑 TCP 客户端和服务器

Q:先运行客户端还是服务器? 官方答案:服务器。

原因:TCP 客户端一启动就 connect(),如果此时没有进程在 12000 端口 LISTEN,握手失败。 服务器必须先 bind + listen,处于等待状态。

Q:这种场景下 serverName 填什么? 官方答案:127.0.0.1。

127.0.0.1 是环回地址(loopback),数据不出网卡,在本机协议栈里绕一圈回来。写 'localhost' 也行(会被 DNS/hosts 解析成 127.0.0.1)。

步骤 4:客户端输入小写句子,服务器回大写句子(例如 aaaaa → AAAAA)。

Exercise 2:远程服务器

Q:要连到对的机器,需要改什么? 官方答案:先知道服务器的 IP 地址,然后把它写进客户端代码(serverName)。

补充要点(做实验时会遇到): - 服务器代码 bind(('', port)) 已经监听所有网卡,不用改; - 防火墙可能挡住 12000 端口; - 端口号两边要一致。

Wireshark 抓包:客户端发 aaaaa、服务器回 AAAAA,应该能看到:

顺序 段 要点
1 客户端 → 服务器 [SYN] 随机初始序号(Wireshark 默认显示相对序号 Seq=0)
2 服务器 → 客户端 [SYN, ACK] Ack = 客户端 ISN + 1
3 客户端 → 服务器 [ACK] 握手完成
4 客户端 → 服务器 [PSH, ACK],载荷 aaaaa Seq=1,Len=5
5 服务器 → 客户端 [ACK](可能与下一个合并) Ack=6
6 服务器 → 客户端 [PSH, ACK],载荷 AAAAA Len=5
7+ [FIN, ACK] / [ACK] 若干 关闭连接

用 Week 06 的知识读包: - 第 5 行 Ack=6:客户端的数据占字节 1–5,下一个期望字节是 6 —— ACK = 对方 Seq + 数据长度。 - 三次握手的 SYN 消耗 1 个序号,所以数据从相对序号 1 开始。 - 服务器代码里先 close()(回复后立即关闭连接套接字),所以往往是服务器先发 FIN。谁先调用 close 谁就是「主动关闭方」,会进入 TIME_WAIT。

步骤 3:和同学交换角色再跑一次。

Exercise 3:UDP 版

用 UDP 代码重做 Exercise 1。(无官方答案,下面是该观察到的现象)

Wireshark 里只会看到两个 UDP 数据报:一个请求、一个回复。没有 SYN/ACK/FIN,没有序号,UDP 首部只有 8 字节(源端口、目的端口、长度、校验和)。 这就是「UDP 无连接、首部小」最直观的证据。


第五部分:期中 Quiz 反馈(W7-QuizFeedback.pdf)

期中 quiz 范围按 Ed 上的更正是 Week 1–4(见 Week 05 笔记顶部说明)。讲评只挑了几道题,下面逐题重算。 这些题的知识点分别在 Week 01、Week 02 和 Tutorial 01 博弈论。

题 1:Queueing —— 收费站车队(First car)

题设:20 辆车的车队(类比 20 比特分组);收费站每 1 秒服务 1 辆车;两收费站相距 200 km,车速 10 km/s;车队整体到齐后才开始服务(存储转发)。 问:第一辆车离开收费站后,多久进入下一个收费站的服务?

答案:39 秒。

等后面辆车被服务完最后一辆车开过去

思路:第一辆车 20 s 就到了,但它得等整个车队到齐。最后一辆车比第一辆晚 19 s 离开,再开 20 s 到达 —— 这就是存储转发:路由器要收完整个分组才能转发。

题 2:Performance —— 占用的链路比例

题设: - 电路交换: 个用户,各需 20 Mbps,共享 100 Mbps 链路 - 分组交换: 个用户,传输时需 20 Mbps,只有 30% 的时间在传

问:一个用户在传输时,占用链路容量的比例?

答案:0.2(两种情况都是 20 / 100 = 20%)。

陷阱:30% 的活跃概率与「传输时占多少」无关,它影响的是能支持多少用户(分组交换的统计复用优势),不影响这一问。

题设:Link 1 长 3 km,传播速度 m/s。

分组长度 16000 bit、链路速率 1000 Mbps 是干扰信息 —— 传播时延只和距离、速度有关,与分组大小和速率无关。

题 4:Throughput —— 客户端链路利用率

题设:4 对服务器-客户端共享中间链路 Mbps;服务器链路 Mbps;客户端链路 Mbps。

每条路径的瓶颈: Mbps。

客户端链路利用率

题 5(Question 5):信道博弈 —— 对称收益(更正过)

题设:两个用户共享无线信道,每个时隙选择发送或静默。 - 只有一人发送:发送者 +16,另一人 0 - 两人都发(碰撞):各 −8 - 都不发:都 0

问:给定 ,用户 1 的最优策略是 ,当 …?

推导:用户 1 发送的期望收益

发不发

发送严格更优 。

正确答案: 。

讲评原话:批改时答案设错了,正确答案是 < 2/3;选 = 2/3 或 < 2/3 的同学都给了满分。 ( 时用户 1 发与不发无差异, 取任何值都是最优,所以 = 2/3 也算说得通。)

题 6(Question 9):非对称收益的均衡(题目有笔误)

题设: - 只有用户 1 发:用户 1 +24,用户 2 0 - 只有用户 2 发:用户 2 +16,用户 1 0 - 都发:用户 1 −8,用户 2 −4 - 都不发:都 0

题面问「pure-strategy Nash equilibria」,标准答案是 。

推导(混合策略均衡 = 让对方无差异):

用户 1 无差异条件(由 决定):

用户 2 无差异条件(由 决定):

讲评原话:「这道题有 typo,所有同学都给满分。」 笔误在哪: 是混合策略纳什均衡,而题面写成了 pure。 这个博弈的纯策略纳什均衡是 (发, 不发) 和 (不发, 发),即 和 。

记住套路:求混合均衡时, 由「用户 2 的收益」解出, 由「用户 1 的收益」解出 —— 是让对方无差异,最容易搞反。

讲评最后一页:推荐用 Kurose & Ross 交互练习 继续刷题。


第六部分:本周要点速记

项 答案
先启动谁 服务器
本机测试的 serverName 127.0.0.1(环回地址)
跨机器要改什么 客户端里的服务器 IP
TCP 服务器有几种套接字 欢迎套接字 + 每客户一个连接套接字
accept() 返回什么 新的连接套接字 + 客户地址
connect() 触发什么 三次握手
UDP 的发送/接收函数 sendto / recvfrom(每次都带地址)
Python 3 socket 收发类型 bytes,要 encode/decode
Wireshark 里 UDP 版能看到什么 只有两个数据报,没有握手
混合策略均衡 让对方无差异

下一篇:Week 08 Multimedia(Week 7–8 的讲课内容 + W8 TCP lab 解答)。