目录
一、网络编程的基本概念
1.1 IP地址
1.1.1 IP的版本
1.1.2 IP的分类
1.1.2.1 公有地址
1.1.2.2 私有地址
1.1.3 IP地址的范围
1.1.4 回环测试
1.2 常见的网络设备
1.3 端口
1.3.1 端口分配
二、网络通信协议
2.1 常用网络协议
2.2 OSI网络协议七层模型
2.3 TCP/IP协议模型
2.3.1 TCP和UDP的区别
2.3.2 三次握手
三、Socket套接字
3.1 socket()函数
3.2 socket()对象
四、UDP编程实例
五、TCP编程实例
六、单工持续通信和双工持续通信
一、网络编程的基本概念
网络编程是指利用计算机网络进行信息交流的编程。它涉及到多个计算机之间的通讯和数据传输,包括通过网络连接进行数据交换、远程服务器访问、网络协议实现等内容。
网络编程是指在计算机网络环境下进行程序设计和开发的一种编程方式。它涉及到使用网络协议进行数据传输和通信,利用网络资源进行数据交换和共享。下面是网络编程的一些基本概念:
-
客户端(Client):客户端是指发起网络请求的一方,它向服务器发送请求并接收服务器的响应。
-
服务器(Server):服务器是指接收客户端请求并进行处理的一方,它响应客户端的请求并提供相应的服务。
-
IP地址(Internet Protocol Address):IP地址是用来唯一标识网络上的设备(如计算机、路由器)的一串数字。IP地址有两种版本:IPv4和IPv6。
-
端口(Port):端口是用来区分不同应用程序或服务的标识符。在网络编程中,每个应用程序或服务都会监听一个特定的端口号。
-
套接字(Socket):套接字是网络编程中用于进行数据传输的一种抽象。它提供了一套API(应用程序接口)用来实现网络通信。
-
并发和多线程:网络编程中常常需要处理多个客户端同时连接服务器的情况,这就需要使用并发和多线程的技术来处理多个连接和数据传输。
1.1 IP地址
IP地址是Internet Protocol Address的缩写,它是用来唯一标识网络上的设备(如计算机、路由器)的一串数字。IP地址用于在网络上进行数据传输和通信。
IP地址的作用是唯一标识计算机或设备在网络中的位置,使得数据可以正确地传输到目标机器。在进行网络编程时,需要使用IP地址来指定数据传输的目标或源。
IP地址通常会与域名(Domain Name)相关联,通过域名解析将域名转换成相应的IP地址。例如,访问一个网站时可以通过域名(如www.example.com)来访问,但实际上计算机在传输数据时会使用与该域名关联的IP地址进行通信。
1.1.1 IP的版本
IP地址分为IPv4和IPv6两种版本。
-
IPv4地址:IPv4使用32位二进制数表示,通常被分成四个用点分隔的十进制数。例如,192.168.0.1是一个常见的IPv4地址格式。其中,每个十进制数范围是0到255。IPv4地址的数量有限,约为42亿个。由于互联网的快速发展,IPv4地址已经不足以满足需求。
-
IPv6地址:IPv6使用128位二进制数表示,通常被分成八个用冒号分隔的四位十六进制数。例如,2001:0db8:85a3:0000:0000:8a2e:0370:7334是一个IPv6地址格式。IPv6地址的数量非常庞大,约为3.4 x 10^38个,足以满足未来的需求。
1.1.2 IP的分类
IP的分类方式有很多种,其中一种常见的分类方式是根据是否属于公有地址或私有地址来划分。
1.1.2.1 公有地址
公有地址是指由ICANN(互联网名称与数字地址分配机构)管理和分配的全球唯一的IP地址。这些地址可用于公共互联网上的任何设备,使其能够直接访问互联网。
1.1.2.2 私有地址
私有地址是指专门用于私人网络中的IP地址范围。这些地址不是全局唯一的,可以在私有网络中重复使用。私有地址主要用于内部局域网(LAN)或家庭网络中,以提供局域网内部的通信和连接。
私有地址在网络之间无法直接访问,需要通过网络地址转换(NAT)来与公有网络进行通信。私有地址的使用可以帮助提高网络安全性,同时也有助于节约全球IP地址的使用。
1.1.3 IP地址的范围
根据IP地址的范围,IP地址被分为不同的分类,包括以下几种:
-
A类地址:范围从1.0.0.0到126.0.0.0,其中1.0.0.0保留给回环测试(loopback test);以0开头的IP地址被保留用于特殊用途。
-
B类地址:范围从128.0.0.0到191.0.0.0,其中128.0.0.0保留给回环测试;以10开头的IP地址被保留用于私有网络。
-
C类地址:范围从192.0.0.0到223.0.0.0,其中192.0.0.0保留给回环测试;以110开始的IP地址被保留用于私有网络。
-
D类地址:范围从224.0.0.0到239.0.0.0,被用于多点广播。
-
E类地址:范围从240.0.0.0到255.0.0.0,被保留用于将来的用途。
私有地址范围如下:
- A类私有地址范围:10.0.0.0 ~ 10.255.255.255
- B类私有地址范围:172.16.0.0 ~ 172.31.255.255
- C类私有地址范围:192.168.0.0 ~ 192.168.255.255
1.1.4 回环测试
回环测试(Loopback Testing)是一种网络测试方法,用于检查本地网络连接是否正常运行。它通过将数据从发送端发送到回环接口(loopback interface),然后从回环接口接收数据来进行测试。
回环接口是一个虚拟的网络接口,它模拟了一个真实的网络接口,但实际上数据并不离开计算机。当数据被发送到回环接口时,操作系统会将数据立即返回,模拟一个正常的网络传输过程。这样可以验证网络协议的正确性、测试网络软件的功能以及检测计算机的硬件故障。
回环测试通常用于以下情况:
- 验证网络协议的实现是否正确。
- 测试网络设备(如路由器、交换机)的功能。
- 检测网络软件的性能和稳定性。
- 排除硬件故障导致的网络问题。
在进行回环测试时,可以使用特定的IP地址来指定回环接口。在IPv4中,回环地址是127.0.0.1,它是一个保留的特殊IP地址,用于本地回环测试。在IPv6中,回环地址是::1。
回环测试是网络管理和故障排除的重要工具,它可以帮助确认网络连接是否正常,排除因网络配置、软件问题或硬件故障引起的网络故障。
1.2 常见的网络设备
常见的网络设备包括:路由器、交换机、网络交换机、网络存储设备、网络加速器、负载均衡器、VPN设备等。
路由器:用于将数据包从一个网络发送到另一个网络。它能够根据网络地址和路由表决定最佳的数据传输路径。路由器在互联网中起到了至关重要的作用,连接着各个网络,并确保数据可以在不同网络之间传递。
交换机:用于连接多台计算机、服务器、打印机等设备,构建一个局域网(LAN)。它能够通过物理地址(MAC地址)识别和转发数据包,将数据包从一个端口发送到另一个端口,实现设备之间的通信。交换机在局域网中起到了关键的作用,提供高速的数据传输和多设备间的数据交换。
网络交换机:用于构建局域网(LAN)。它通过将数据包从一个端口转发到另一个端口,实现不同设备之间的通信。网络交换机根据数据包中的目标MAC地址来决定将数据包发送到哪个端口。当一个设备发送一个数据包到网络交换机时,网络交换机会查看数据包的目标MAC地址,并根据这个地址决定将数据包转发到适当的端口。网络交换机是构建局域网的关键设备,它提供了高带宽、低延迟的数据传输,同时可以通过划分虚拟局域网(VLAN)来隔离不同的网络流量。
网络存储设备:用于存储和共享文件的设备。它通过网络连接,可以让多台计算机或设备共享和访问存储在其中的文件和数据。网络存储设备可以使用各种存储技术,例如硬盘驱动器(HDD)、固态硬盘(SSD)、光盘驱动器等。网络存储设备的优点,包括提供便捷的文件共享和访问、扩展存储空间以满足不断增长的数据需求、备份和恢复文件的能力等。
网络加速器:用于帮助提高网络连接速度和稳定性的设备。它可以通过优化网络流量、减少延迟和提高带宽利用率来加速网络连接。网络加速器可以通过多种方式实现,包括优化网络协议、压缩数据、缓存常用数据等。一些网络加速器还提供虚拟专用网络(VPN)功能,以保护用户的隐私和安全。网络加速器适用于各种网络环境,包括家庭网络、公司网络、移动网络等。
负载均衡器:用于分配网络请求和负载的一种设备或软件。它的主要功能是将网络流量平均分配给多个服务器,以提高系统的性能和可靠性。负载均衡器可以通过多种算法来实现请求分配,还可以通过缓存静态内容、压缩数据、提供SSL终止等功能来进一步改善性能和安全性。通过使用负载均衡器,可以有效地提高系统的可伸缩性、容错性和响应速度,同时还可以减轻单个服务器的负载压力,提高系统的可用性和可靠性。
1.3 端口
一个端口(Port)是在计算机网络中用于标识不同应用程序或进程的数字。它作为通信的一个入口,使得计算机可以与其他计算机进行数据的传输和交流。
在网络通信中,每一个数据包都需要通过某个端口发送和接收。常见的端口号范围是从0到65535,其中0到1023的端口被称为Well-known端口,用于一些被广泛使用的应用程序。例如,HTTP通信使用的端口号是80,HTTPS通信使用的端口号是443。
端口号在TCP/IP协议中扮演重要的角色,它们是用来传输数据的传输层的一部分。每一个数据包在进入网络之前都会被分配一个源端口号和目标端口号,这样接收端可以根据端口号来正确识别和处理数据包。
1.3.1 端口分配
端口分配是指将端口号分配给不同的应用程序或服务,以便它们可以通过该端口进行通信和数据传输。端口分配的目的是确保不同的应用程序可以通过不同的端口号进行并行通信,避免冲突和混淆。
在TCP/IP协议中,端口分配是由 Internet Assigned Numbers Authority (IANA) 或其委派的机构进行管理。IANA将整个端口号空间分为三个范围:
-
Well-known端口:范围是0到1023。这些端口号被分配给被广泛使用的标准应用程序和服务。例如,HTTP使用的端口号是80,HTTPS使用的端口号是443。
-
Registered端口:范围是1024到49151。这些端口号被注册给一些特定的应用程序或服务,但并不像Well-known端口那样广泛使用。
-
Dynamic或Private端口:范围是49152到65535。这些端口号被用于临时的或私有的通信,通常由操作系统动态分配给客户端应用程序。
对于一些特殊的应用程序或服务,它们可能需要预留或分配特定的端口号。例如,FTP使用的端口号是20和21,SMTP使用的端口号是25。
二、网络通信协议
网络通信协议是指计算机网络中用于数据交换和通信的规则和约定。它定义了数据如何在网络中传输、编码和解码、错误检测和纠正、数据分组和重组、路由选择等方面的工作原理和规范。
2.1 常用网络协议
2.2 OSI网络协议七层模型
OSI网络协议七层模型是开放系统互联通信(Open Systems Interconnection,简称OSI)标准化的一种网络协议体系结构。它将网络通信过程分解为七个层次,每个层次负责特定的功能,从物理层到应用层,依次为:
-
物理层(Physical Layer):负责传输原始比特流,定义了电气和物理特性,如传输媒介、数据的传输速率和编码规范。
-
数据链路层(Data Link Layer):负责将原始比特流划分为数据帧,进行透明传输和错误检测,确保可靠的点对点传输。
-
网络层(Network Layer):负责数据包的路由和转发,将数据包从源主机传输到目标主机,提供网络互联和逻辑地址寻址。
-
传输层(Transport Layer):负责数据传输的可靠性和流量控制,提供端到端的数据可靠传输和错误恢复,常见的协议有TCP和UDP。
-
会话层(Session Layer):负责建立、管理和终止会话,提供通信双方之间的对话控制和同步。
-
表示层(Presentation Layer):负责数据的格式化、加密和压缩,确保数据的可理解性和安全性。
-
应用层(Application Layer):负责应用程序之间的通信,提供用户接口和服务,如电子邮件、文件传输和远程登录等。
这种七层模型的设计使得不同层次的协议可以相互独立地开发和更新,同时也提供了一种标准化的参考架构,用于指导各种网络协议的设计和实现。
2.3 TCP/IP协议模型
TCP/IP协议模型是现代互联网通信的基础协议体系,由两个主要协议簇组成:传输控制协议(TCP)和互联网协议(IP)。TCP/IP协议模型是一种四层模型,包括以下层次:
-
物理+数据链路层:负责将数据链路层的数据帧转换为比特流,以便在物理网络上传输。它定义了网络硬件和设备的接口标准,如网卡和以太网。
-
网际层:负责数据包的路由和转发,通过IP协议进行逻辑地址寻址。它定义了数据包的结构和如何在网络中进行传输,同时也包括一些额外的协议,如Internet Control Message Protocol(ICMP)和Internet Group Management Protocol(IGMP)。
-
传输层:负责提供端到端的可靠数据传输和错误恢复,其中最常用的协议是(TCP)和用户数据报协议(UDP)。TCP提供面向连接的可靠传输,而UDP提供无连接的不可靠传输。
-
应用层:负责应用程序之间的通信,提供各种服务和协议,如域名系统(DNS)、文件传输协议(FTP)、超文本传输协议(HTTP)和简单邮件传输协议(SMTP)等。
2.3.1 TCP和UDP的区别
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常用的传输层协议,它们在互联网通信中有一些重要的区别。
-
连接方式:TCP是面向连接的协议,通过三次握手建立连接,确保可靠的数据传输。UDP是无连接的协议,数据包可以直接发送,不需要建立和维护连接。
-
可靠性:TCP提供可靠的数据传输,通过序列号、确认应答和重传机制来保证数据的完整性和正确性。UDP则不提供可靠性保证,发送的数据包不保证可靠地到达目标。
-
速度和效率:TCP提供流量控制、拥塞控制和错误恢复等机制,确保数据在网络中按序到达,但这也使得TCP的传输速度相对较慢。UDP不提供这些机制,传输速度相对较快,适用于实时性要求较高的应用场景。
-
数据包大小:TCP没有固定的限制,可以处理任意大小的数据。UDP的数据包大小有限制,IPv4最大为64KB,IPv6最大为4GB。
-
适用场景:TCP适用于对可靠性和顺序性要求较高的应用,如文件传输、网页浏览、电子邮件等。UDP适用于对实时性要求较高,但可靠性要求较低的应用,如音视频流媒体、在线游戏、语音通话等。
2.3.2 三次握手
三次握手(Three-way Handshake)是TCP建立连接时使用的一种握手过程,用于确保通信双方的状态同步和可靠性。
具体的三次握手过程如下:
-
第一次握手(SYN):建立连接的客户端向服务端发送一个SYN(同步)报文,该报文中包含一个随机的初始序列号(ISN)。
-
第二次握手(SYN+ACK):服务端收到客户端的SYN报文后,会向客户端发送一个SYN+ACK报文作为应答。该报文中包含了服务端的初始序列号(ISN+1),并在确认序号中确认客户端的序列号(ISN+1)。
-
第三次握手(ACK):客户端收到服务端的SYN+ACK报文后,会向服务端发送一个ACK(确认)报文,该报文中确认服务端的序列号(ISN+1),并在确认序号中确认服务端的序列号(ISN+1)。
连接建立成功,通信双方可以开始进行数据传输。
三次握手的目的是为了防止已失效的连接请求报文段突然又传送到了服务端,从而造成资源浪费和网络拥塞。通过三次握手,服务端可以确认客户端的请求并建立连接,双方的序列号和确认序号也得以同步,保证了通信的可靠性和正确性。
三、Socket套接字
Socket套接字是一种用于网络通信的编程接口,它提供了一种标准的通信方式,可以在不同的计算机间进行数据的传输和交互。
Socket套接字可以进行不同层次的网络通信,包括传输控制协议(TCP)、用户数据报协议(UDP)等。它允许程序在互联网上的不同计算机之间进行双向的数据传输。
Socket套接字的使用一般需要指定IP地址和端口号,以便与其他计算机建立连接并进行数据交换。通过Socket套接字的接口,程序可以发送和接收数据,建立连接、断开连接,以及监听和接受连接请求等操作。
3.1 socket()函数
socket()函数是通过socket模块提供的方法,用于创建一个新的Socket对象。
函数原型如下:
socket.socket([family[, type[, proto]]])
参数说明:
- family:指定Socket的地址族,常用的有socket.AF_INET(IPv4)和socket.AF_INET6(IPv6)等。
- type:指定Socket的类型,常用的有socket.SOCK_STREAM(面向连接的TCP套接字)和socket.SOCK_DGRAM(无连接的UDP套接字)等。
- proto:指定使用的传输协议,通常不需要指定,默认为0。
返回值:
- 返回一个Socket对象,用于后续的套接字操作。
使用socket()函数可以创建一个新的Socket对象,该对象可以用于后续的套接字操作,如绑定地址、监听连接、发送和接收数据等。
示例代码:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Socket created successfully")
# 输出结果 Socket created successfully
# 后续可以进行其他套接字操作
以上代码使用socket()函数创建了一个面向连接的TCP套接字,输出结果中显示"Socket created successfully"表示套接字创建成功。之后可以进行其他操作,如绑定地址、监听连接等。
3.2 socket()对象
socket库提供了一系列的socket对象方法用于网络编程。以下是一些常用的socket对象方法:
-
bind(address):绑定socket到指定的地址。address参数是一个元组,包含IP地址和端口号。例如:s.bind(("localhost", 8080))
-
listen(backlog):将socket设置为监听模式,等待客户端连接请求。backlog参数指定等待连接的最大数量。例如:s.listen(5)
-
accept():接受客户端的连接请求并返回一个新的socket对象和客户端地址。通常在使用多线程或多进程时,使用accept方法来接受客户端连接,并创建一个新的socket对象来处理与该客户端的通信。例如:client_socket, client_address = s.accept()
-
connect(address):与远程主机建立连接。address参数是一个元组,包含远程主机的IP地址和端口号。例如:s.connect(("localhost", 8080))
-
send(data):将数据发送到已连接的远程主机。data参数是要发送的数据,通常是一个字节字符串。例如:s.send(b"Hello, World!")
-
recv(buffer_size):从远程主机接收数据。buffer_size参数指定每次接收的最大字节数。recv方法将返回一个字节字符串,包含从对端接收到的数据。例如:data = s.recv(1024)
-
close():关闭socket连接。当通信完成时,应该使用close方法来关闭socket。例如:s.close()
(下面示例会使用)
四、UDP编程实例
这是一个简单的UDP编程实例,通过UDP客户端和UDP服务器端实现了简单的消息通信。可以根据实际需求进行扩展和修改。
UDP客户端(client.py):
import socket
server_address = ('localhost', 8080)
# 创建UDP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 发送数据
message = 'Hello, server!'
client_socket.sendto(message.encode(), server_address)
# 接收服务器端的响应
data, server = client_socket.recvfrom(1024)
print('Received response from', server, ':', data.decode())
# 关闭socket连接
client_socket.close()
UDP服务器端(server.py):
import socket
server_address = ('localhost', 8080)
# 创建UDP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口
server_socket.bind(server_address)
print('Server is listening on', server_address)
while True:
# 接收客户端的请求和数据
data, client = server_socket.recvfrom(1024)
print('Received data from', client, ':', data.decode())
# 发送响应给客户端
response = 'Hello, client!'
server_socket.sendto(response.encode(), client)
首先,在终端中运行服务器端的代码:
$ python server.py
Server is listening on ('localhost', 8080)
然后,在另一个终端中运行客户端的代码:
$ python client.py
Received response from ('localhost', 8080) : Hello, client!
客户端会向服务器发送一条消息,然后服务器收到消息后会发送一条响应给客户端。在客户端终端中会打印出服务器的响应。
五、TCP编程实例
以下是一个简单的TCP编程实例,其中包括一个服务器和一个客户端,它们之间可以进行双向通信:
服务器端的代码:
import socket
# 创建一个 TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定服务器 IP 地址和端口号
server_address = ('localhost', 12345)
server_socket.bind(server_address)
# 监听连接
server_socket.listen(1)
print('Server is listening for connections...')
while True:
# 接受客户端连接请求
client_socket, client_address = server_socket.accept()
print('Client connected:', client_address)
# 接收来自客户端的数据
data = client_socket.recv(1024)
print('Received from client:', data.decode())
# 向客户端发送数据
message = 'Server: Message received'
client_socket.sendall(message.encode())
# 关闭连接
client_socket.close()
客户端的代码:
import socket
# 创建一个 TCP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 服务器 IP 地址和端口号
server_address = ('localhost', 12345)
# 连接服务器
client_socket.connect(server_address)
# 发送数据到服务器
message = input('Enter message to send: ')
client_socket.sendall(message.encode())
# 接收来自服务器的数据
data = client_socket.recv(1024)
print('Received from server:', data.decode())
# 关闭连接
client_socket.close()
在这个示例中,服务器使用 socket.bind() 函数将服务器 IP 地址和端口号绑定到一个 TCP socket 上,然后使用 socket.listen() 函数开始监听连接请求。服务器在一个无限循环中不断接受客户端的连接请求,并在连接建立后接收客户端发送的数据,并向客户端发送回复。
客户端使用 socket.connect() 函数连接到服务器的 IP 地址和端口号,并使用 socket.sendall() 函数发送数据到服务器。然后,客户端使用 socket.recv() 函数接收服务器发送的数据,并打印出来。最后,客户端关闭连接。
(3.2 socket对象)
六、单工持续通信和双工持续通信
对于单工持续通信,可以使用Python的socket库进行实现。其中,一个程序作为服务器端,另一个程序作为客户端。服务器端监听某个端口,接收来自客户端的数据,而客户端则通过socket连接到服务器端并发送数据。这种通信方式类似于请求-响应模式,服务器端只能接收客户端的请求,而不能主动发送数据给客户端。代码示例:
服务器端:
import socket
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_socket.bind(('localhost', 8000))
# 监听连接
server_socket.listen(1)
# 接收客户端连接
client_socket, addr = server_socket.accept()
# 接收数据
data = client_socket.recv(1024)
print(f"Received data: {data.decode()}")
# 关闭连接
client_socket.close()
server_socket.close()
客户端:
import socket
# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
client_socket.connect(('localhost', 8000))
# 发送数据
client_socket.send(b"Hello server!")
# 关闭连接
client_socket.close()
对于双工持续通信,可以使用Python的socket库结合多线程或异步编程来实现。在这种情况下,服务器端和客户端可以同时进行数据的发送和接收。服务器端可以监听多个客户端连接,并通过多线程或异步处理来同时接收和发送数据。代码示例:
服务器端:
import socket
import threading
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_socket.bind(('localhost', 8000))
# 监听连接
server_socket.listen(5)
# 处理客户端连接的函数
def handle_client(client_socket):
while True:
# 接收数据
data = client_socket.recv(1024)
print(f"Received data from client: {data.decode()}")
# 发送数据
client_socket.send(b"Hello client!")
# 如果客户端发送"exit"则退出循环
if data.decode() == "exit":
break
# 关闭连接
client_socket.close()
# 接收客户端连接并创建线程处理
while True:
client_socket, addr = server_socket.accept()
print(f"New client connected: {addr}")
# 创建线程处理客户端连接
client_thread = threading.Thread(target=handle_client, args=(client_socket,))
client_thread.start()
客户端:
import socket
# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
client_socket.connect(('localhost', 8000))
while True:
# 发送数据
client_socket.send(b"Hello server!")
# 接收数据
data = client_socket.recv(1024)
print(f"Received data from server: {data.decode()}")
# 如果服务器发送"exit"则退出循环
if data.decode() == "exit":
break
# 关闭连接
client_socket.close()