一.网络通信要素
1.网络介绍
- 将具有独立功能的多台计算机通过通信线路和通信设备连接起来,在网络管理软件及网络通信协议下,实现资源共享和信息传递的虚拟平台。
- 能够编写基于网络通信的软件或程序,通常来说就是网络编程。
2.IP地址的介绍
IP地址是分配给网络设备上网使用的数字标签,它能够标识网络中唯一的一台设备,好比现实中每个人都有一个手机号。
2.1IP地址表现形式
- IP地址分为两类,IPv4和IPv6
- IPv4当前使用的ip地址
- IPv6时未来使用的IP地址
2.2 IP地址的作用
通过 IP地址找到网络中唯一一台设备,然后可以跟这个设备进行数据通信
3.ifconfig和ping命令
- 查看网卡信息
linux:
- 192.168.64.129 是在网络中的地址,使用ifconfig主要是查看网卡对应网络中的IP地址
- 127.0.0.1表示本机地址,如果和自己的电脑通信就可以使用该地址
- 127.0.0.1该地址对应的域名是localhost,域名是IP地址的别名,通过域名能解析出一个对应的IP地址
- ens33和 lo是网卡名
windows: ipconfig
- 检查网络是否正常
- ping www.baidu.com 检查是否能上公网
- ping 当前局域网的IP地址,检查是否在同一个局域网内
- ping 127.0.0.1 检查本地网卡是否正常
4.端口和端口号的介绍
- 只有IP地址无法确定把数据给那个进程
- 其实,每运行一个程序都会有一个端口,想要给对应程序发送数据,找到对应端口即可
- 端口是传输数据的通道,好比教室的门,是数据传输必经之路。
- 操作系统为了统一管理这么多端口,就对端口进行了编号,这就是端口号,端口号其实就是一个数字,好比我们现实生活中的门牌号。
- 每个端口对应一个端口号,想要找到端口 通过端口号即可
5.端口号的分类
- 知名端口号
知名端口号是指众所周知的端口号,范围从0到1023,这些端口号一般固定分配给一些服务,比如21端口分配给FTP(文件传输协议)服务,25端口分配给SMTP(简单邮件传输协议)服务,80端口分配给HTTP服务。 - 动态端口号
一般程序员开发应用程序使用端口号称为动态端口号。
动态端口号的范围是从1024到65535,如果程序员开发的程序没有设置端口号,操作系统会在动态端口号这个范围内随机生成一个给开发的应用程序使用。- 当运行一个程序默认有一个端口号,当程序退出时,所占用的端口号就会被释放
6.socket介绍
socket(简称 套接字) 是程序之间通信一个工具,好比现实中的电话,当知道了对方的电话号码后需要使用电话才能进行通讯,程序之间想要进行网络通信需要基于这个socket,socket就是程序间进行网络通讯的工具。
7.TCP介绍
之前我们学习了IP地址和端口号,通过IP地址能够找到对应的设备,然后再通过端口号找到对应的端口,再通过端口把数据传输给应用程序,这里要注意,数据不能随便发送,在发送之前要选择网络传输方式(传输协议),保证程序之间按照指定的传输规则进行数据的通信。
TCP概念
TCP的英文全拼(Transmission Control Protocol)简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP通信步骤:创建连接->传输数据->关闭连接
TCP的特点
- 面向连接
通信双方必须先建立好连接才能进行数据的传输,并且双方都会为此连接分配必要资源用来记录连接的状态和信息。当数据传输完成后,双方必须断开此连接,以释放系统资源。 - 可靠传输:
- TCP采用发送应答机制:
通过TCP这种方式发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传送成功 - 超时重传
发送端发送一个报文之后就会启动定时器,如果指定时间内没有得到应答就会重新发送这个报文段 - 错误校验
TCP用一个校验和函数来校验数据是否有错误,在发送和接收时都要计算校验和 - 流量控制和阻塞管理
流量控制用来避免发送端发送过快而使得接收方来不及接收
二.TCP的网络应用程序开发
- TCP客户端程序开发
- 客户端程序是指运行在用户设备上的程序
- TCP服务端程序开发
- 服务端程序是指运行在服务器设备上的程序,专门为客户端提供数据服务。
1.TCP客户端程序开发流程
- 创建客户端套接字对象
- 和服务端套接字建立连接
- 发送数据
- 接受数据
- 关闭客户端套接字
- 主动发起建立连接请求的是客户端程序
- 等待接受连接请求的是服务端程序
2.TCP客户端程序开发
2.1 socket类的介绍
# 导入socket模块
import socket
# 创建客户端socket对象使用socket类
socket.socket(AddressFamily, Type)
- AddressFamily IP地址类型,分为IPv4和IPv6
- Type 传输协议类型
2.2 开发客户端使用到的函数
方法名 | 说明 |
---|---|
connect | 和服务端套接字建立连接 |
send | 发送数据 |
recv | 接收数据 |
close | 关闭连接 |
2.3创建TCP客户端
# -*-coding:utf8-*-
import socket
if __name__ == '__main__':
# 1.创建客户端套接字对象
# socket.AF_INET :表示选择IPv4
# socket.SOCK_STREAM: 表示流式传输协议,也就是tcp的通信方式
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.和服务端套接字建立连接
# 参数为IP地址和端口号
tcp_client_socket.connect(("192.168.64.1", 8080))
# 3.发送数据
# 编码为二进制数据发送
# 注意:如果是windows的网络调试助手使用gbk编码
tcp_client_socket.send("hello world!".encode(encoding='utf-8'))
# 4.接受数据
# 1024 大小
tcp_client_socket.recv(1024)
# 5.关闭客户端套接字
tcp_client_socket.close()
网络调试助手接受到的信息
网络调试助手发送消息,客户端程序接收到的数据
3.TCP服务端程序开发流程
具有了客户端和服务端,一个网络应用才可以真正的使用
- 创建服务端套接字对象
- 绑定IP地址和端口号
- 设置监听
- 等待接收客户端的连接请求
- 接收数据
- 发送数据
- 关闭套接字
4.TCP服务端程序开发
4.1 TCP服务端相关函数
方法名 | 说明 |
---|---|
bind | 绑定IP地址和端口号 |
listen | 设置监听 |
accept | 等待接受客户端的连接请求 |
send | 发送数据 |
recv | 接收数据 |
4.2 创建TCP服务端
#!/usr/bin/nev python
# -*-coding:utf8-*-
import socket
if __name__ == '__main__':
# 1.创建服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 2.绑定IP地址和端口号
tcp_server_socket.bind(('192.168.64.1', 8080))
# 3.设置监听 128:服务端等待排队链接的对搭数量
tcp_server_socket.listen(128)
# 4.等待接收客户端的连接请求 返回值:服务套接字,ip和端口号
connect_socket, ip_port = tcp_server_socket.accept()
print(ip_port)
# 5.接收数据 1024:接收数据大小
recv_data = connect_socket.recv(1024)
print(recv_data.decode(encoding='gbk'))
# 6.发送数据
connect_socket.send('hello world'.encode(encoding='gbk'))
# 7.闭套接字
connect_socket.close()
tcp_server_socket.close()
- 先运行服务端,在运行客户端解阻塞:
5.TCP网络应用程序注意点的介绍
- 当TCP客户端程序想要和TCP服务端程序进行通信的时候必须要先建立连接
- TCP客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。
- TCP服务端程序必须绑定端口号,否则客户端找不到这个TCP服务端程序。
- listen后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。
- 当TCP客户端程序和TCP服务端程序连接成功后, TCP服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字。
- 关闭accept返回的套接字意味着和这个客户端已经通信完毕。
- 当客户端的套接字调用close后,服务器端的recv会解阻塞,返回的数据长度为0,服务端可以通过返回数据的长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的recv也会解阻塞,返回的数据长度也为0。
6.socket之send和recv原理剖析
- TCP socket的发送和接收缓冲区
当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。 - send原理剖析
要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡 - recv原理剖析
应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据。
总结:
不管是recv还是send都不是直接接收到对方的数据和发送数据到对方,发送数据会写入到发送缓冲区,接收数据是从接收缓冲区来读取,发送数据和接收数据最终是由操作系统控制网卡来完成。
案例-多任务版TCP服务端程序开发
目前我们开发的TCP服务端程序不能同时服务于多个客户端
使用多任务可以实现一个服务端同时服务多个客户端,本案例中我们使用线程
- 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
添加以下代码
# 循环等待接收客户端的连接请求
while True:
service_client_socket, ip_port = tcp_server_socket.accept() # 等待接收客户端的连接请求
# 当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
sub_thread.setDaemon(True) # 设置守护主线程
sub_thread.start() # 启动子线程