计算机网络基础
中等9网络通信原来这么简单——用Python模拟收发消息
计算机网络就像一套“数字快递系统”。每台计算机(设备)都有一个唯一的“地址”——IP地址,就像家的门牌号。数据被切成一个个“包裹”(数据包),在路上经过路由器(像快递站)的转发,最终送到目的地。今天我们就用一个简单的Python程序,模拟两台电脑(客户端和服务器)之间的通信,把看不见的网络过程变成能运行的代码。
一、网络中的两个角色:谁请求,谁响应?
在网络上,两个设备通信时通常扮演不同的角色:
- 客户端:主动发起请求的一方。比如你打开浏览器输入网址,你的浏览器就是客户端,它向存放网页的服务器说“请把首页内容给我”。
- 服务器:被动等待请求,然后提供数据的一方。例如存放网页的电脑(服务器)收到请求后,把网页文件传给你。
生活中的例子:你(客户端)去便利店买东西,告诉店员要什么;店员(服务器)从货架上拿给你。没有你主动问,店员不会凭空把东西塞给你。
二、IP地址和端口:门牌号 + 房间号
每个连接互联网的设备都有一个IP地址,相当于“门牌号”,用来找到这台电脑。但一台电脑上可能同时运行很多网络程序(比如浏览器、微信、游戏),怎么知道数据该交给哪个程序呢?这就需要端口——相当于同一个门牌号下的不同房间号。
- 常用端口:Web服务器(HTTP)用80,HTTPS用443,数据库用3306。
- 特殊地址:
127.0.0.1表示“本机”,只在你自己电脑上测试时使用,数据不会真的发送到外网。 - 我们自己选用的端口是
12345,只要不和常用端口冲突就行。
三、数据包:把大文件切成小块
当你发送一条消息“你好,网络世界!”,电脑不会直接扔一串字符到网线上。它会把这串字符切分成多个小数据包(比如每包512字节),每个包都写上源地址、目的地址、序号。路由器根据目的地址把包一站一站转发,就像快递包裹经过多个中转站。最后接收方把所有包按序号拼回完整消息。
四、用Python写一个“传信”程序
Python自带的socket库可以让我们创建网络套接字(Socket),实现两台电脑间的通信。我们写一个最简单的例子:一台电脑当服务器(先启动),另一台当客户端(后启动),双方交换一句祝福。
注意:以下代码只能在 同一台电脑上测试(因为用了127.0.0.1),如果你想在两台不同电脑上试,把IP地址换成对方的实际IP(但需要关闭防火墙或设置允许)。
代码中每行都有中文注释,请仔细阅读。
1. 服务器端代码(先运行)
# 服务器端(先启动)
import socket # 导入网络通信库
# 创建一个TCP套接字(AF_INET表示IPv4,SOCK_STREAM表示TCP)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定到本机127.0.0.1的12345端口
server.bind(('127.0.0.1', 12345))
# 开始监听,最多允许1个客户端排队等待连接
server.listen(1)
print("服务器已启动,等待客户端连接...")
# 接受客户端连接,返回一个新的套接字conn和客户端的地址addr
conn, addr = server.accept()
print(f"客户端 {addr} 已连接")
# 接收客户端发来的数据,最多接收1024字节,并解码为字符串
data = conn.recv(1024).decode()
print(f"收到客户端消息:{data}")
# 回复客户端:将字符串编码为字节再发送
conn.send("服务器收到了你的祝福!".encode())
# 关闭连接
conn.close()
server.close()
2. 客户端代码(后运行)
# 客户端(后启动)
import socket
# 创建TCP套接字
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接到服务器(IP和端口要和服务器一致)
client.connect(('127.0.0.1', 12345))
# 发送一条消息,先编码为字节
client.send("你好,网络世界!".encode())
# 接收服务器的回复,解码
reply = client.recv(1024).decode()
print(f"服务器回复:{reply}")
# 关闭连接
client.close()
五、运行步骤
- 打开两个终端窗口(或者用两个Python IDE标签页)。
- 先运行服务器程序,你会看到“服务器已启动,等待客户端连接...”。
- 再运行客户端程序,客户端会自动连接,发送消息,然后服务器打印收到的消息,回复,客户端打印回复。
- 两条消息成功传输后,程序结束。
六、新手容易犯的错误
- 程序运行顺序搞反:必须先启动服务器,再启动客户端。如果客户端先运行,它会因为找不到服务器而报错
Connection refused。 - 端口被占用:如果你之前测试过同样的端口但没正常关闭程序,或者有其他软件占用了
12345端口,服务器会报错Address already in use。可以换一个端口号(比如12346),或者等一会儿再试。 - IP地址写错:如果改成其他IP(比如192.168.1.x),但另一台电脑没连到同一个局域网,或者防火墙没放行,连接会失败。
- 忘记编码/解码:
send()和recv()传输的是字节数据,所以发送前要.encode(),收到后要.decode(),否则会出现TypeError或乱码。 - 只开了一个终端:服务器和客户端必须同时运行,如果在一个终端里先跑服务器,按Ctrl+C中断后再跑客户端,服务器已经关闭,客户端也会报错。
七、完整可运行示例(含注释)
为了方便复制,我把上面两段代码合并到一块,但实际运行时请分开在两个文件或两个终端里跑。
文件1: server.py
# 服务器端
import socket
# 创建TCP套接字
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定到本机端口12345
server.bind(('127.0.0.1', 12345))
# 开始监听,允许1个客户端排队
server.listen(1)
print("服务器已启动,等待客户端连接...")
# 接受连接
conn, addr = server.accept()
print(f"客户端 {addr} 已连接")
# 接收数据并解码
data = conn.recv(1024).decode()
print(f"收到客户端消息:{data}")
# 发送回复
conn.send("服务器收到了你的祝福!".encode())
# 关闭
conn.close()
server.close()
文件2: client.py
# 客户端
import socket
# 创建TCP套接字
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接到服务器
client.connect(('127.0.0.1', 12345))
# 发送消息
client.send("你好,网络世界!".encode())
# 接收回复
reply = client.recv(1024).decode()
print(f"服务器回复:{reply}")
# 关闭
client.close()
运行后你会看到类似输出:
服务器已启动,等待客户端连接...
客户端 ('127.0.0.1', 54321) 已连接
收到客户端消息:你好,网络世界!
服务器回复:服务器收到了你的祝福!
八、相关指引
- 如果想进一步学习网络编程,可以了解 TCP/UDP的区别(UDP是无连接的,像发短信不保证对方一定收到)。
- 实际应用中,我们很少直接用
socket写HTTP请求,而是使用更高级的库如requests(你只需要requests.get("https://www.example.com")就能拿到网页内容)。 - 网络的基础协议:IP(互联网协议)负责寻址,TCP(传输控制协议)负责可靠传输。
- 如果你想把程序变成真正的聊天软件,可以加上多线程,让服务器同时服务多个客户端,或者让客户端也能不停发送消息。
现在你已经亲手写了一个网络通信程序,恭喜你迈出了理解计算机网络的第一步!
例题精讲
在OSI七层模型中,哪一层负责将数据包从源端路由到目的端?
TCP和UDP都是传输层协议,其中哪个协议是面向连接的?
IP地址192.168.1.1属于私有IP地址范围。
HTTP协议默认使用TCP端口443进行通信。
使用Python socket库获取域名对应的IP地址,请补全代码。
import socket
domain = "___"
ip = socket.gethostbyname(domain)
print(f"IP address of {domain} is {ip}")