网络相关概念
网络
将具有独立功能的多台计算机通过通信线路和通信设备连接起来,在网络管理软件及网络通信协议下,实现资源共享和信息传递的虚拟平台
网络编程
能够编写基于网络通信的软件或程序,通常来说就是网络编程
使用编程语言实现多台计算机的网络通信,需要具备网络编程三个要素:
- IP地址,这时网络环境下每一台计算机的唯一标识,通过IP地址来找到指定的计算机
- 端口,用于表示进程的逻辑地址,通过端口来找到指定的进程
- 协议,定义通信规则,符合协议则可以通信,否则无法通信
TCP协议
TCP 简称传输控制协议,是一种面相连接的、可靠的、基于字节流的传输层通信协议
TCP协议创建连接:三次握手
三次握手 是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立
- 第一次握手:客户端向服务端发送请求,等待服务端确认
- 第二次握手:服务端收到请求后知道客户端请求建立连接,回复给客户端以确认连接请求
- 第三次握手:客户端收到确认后,再次发送请求确认服务端,服务端收到正确请求后,如果正确则连接建立成功,完成三次握手,随后客户端与服务端之间成功建立连接
TCP协议断开连接:四次挥手
四次挥手,即TCP断开连接的时候需要4次确认,TCP是双向的,A连接B,B连接A都要断开
- 第一次挥手,当主机A完成数据传输后,提出停止TCP连接请求
- 第二次挥手,主机B收到请求后对其做出响应,确认这一方向上的TCP连接即将关闭
- 第三次回收,主机B再提出反方向的连接关闭请求
- 第四次挥手,主机A对主机B的请求进行响应,确认TCP连接关闭
TCP协议的特点
面相连接
- 通信双方必须先建立好连接才能进行数据传输,数据传输完成后断开
可靠传输
- 都建立了连接,传输数据可靠
- 必须先建立连接,相对效率较低
- 大多数服务器程序都是使用TCP协议开发的,比如文件下载,网页浏览等
UDP协议
- 面向无连接
- 采用数据报包(Package)形式传输数据,每个包大小不能超过64KB
- 不安全(不可靠)协议
- 效率相对较高
- 不区分客户端和服务器端,叫 发送端 和接收端
套接字(Socket)
socket 是进程之间通信的一个工具,因为进程之间想要进行网络通信需要基于Socket来完成。
python中的socket模块下提供了socket类,开发人员在代码中创建socket对象,就可以实现不同主机之间的进程间通信
函数名 | 含义 |
---|---|
socket(AddressFamily,Type) | 用于创建一个socket对象,其中,参数AddressFamily可以选择AF_INET用于Internet进程间通信,实际工作中常用它;参数Type表示套接字类型,可以是SOCKET_STREAM流式套接字,主要用于TCP协议 |
例:创建一个socket对象
python
# 导入模块
import socket
# 创建套接字对象
# Address Family,地址族,即:表示用何种IP规则来解析,如 IPV4,IPV6... AF_INET代表IPV4
# Type 表示传输方式,SOCK_STREAM 表示用字节流(二进制形式)传输数据
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 输出内容
print(tcp_socket)
# 释放资源
tcp_socket.close()
TCP程序开发流程
TCP网络应用程序开发分为:
- TCO客户端程序开发,客户端程序是指运行在用户设备上的程序
- TCP服务端程序开发,服务端程序是指运行在服务器设备上的程序,专门为客户端提供数据服务
相关函数
常用语服务器端的函数
函数名 | 含义 |
---|---|
socket(AddressFamily,Type) | 用于创建一个socket对象,其中,参数AddressFamily可以选择AF_INET用于Internet进程间通信,实际工作中常用它;参数Type表示套接字类型,可以是SOCKET_STREAM流式套接字,主要用于TCP协议 |
listen(backlog) | 开始TCP监听。参数backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量,该值至少为1,大部分应用程序设为5就可以了,最大值可设置为128 |
accept() | 被动接受TCP客户端连接,等待连接的到来,返回一个connection对象,以元组形式显示,且元组的第一个参数为已建立连接的socket对象,第二个参数为地址address |
同时应用于服务器端和客户端的函数
函数名 | 含义 |
---|---|
recv(bufsize) | 用于接受TCP数据,数据bytes二进制形式返回,bufsize指定要接收的最大数据量,可设置为1024 |
send(data) | 发送TCP数据,将data中的数据发送到连接的套接字,data为bytes二进制数据 |
close() | 关闭套接字 |
setsockopt(level,optname,value) | 可设置端口号复用,让程序退出端口号并立即释放,level可指定SOL_SOCKET,opename可指定为SO_REUSEADDR,value可指定为True |
常用语客户端的函数
函数名 | 含义 |
---|---|
connect(address) | 主动初始化TCP服务器连接,一般地,参数address的格式为元组(host,port),若连接出错,则返回socket.err错误 |
字符串str与二进制bytes类型转换
在网络中,数据是二进制数据类型bytes的形式进行传递的,所以在我们向网络传输数据的时候需要把数据转化为二进制,从网络中接收到的数据默认也是二进制类型的数据,想要正常使用这些数据也需要把这些数据从二进制类型转化为字符串str类型
当字符串str数据转换为二进制bytes类型时
函数名 | 含义 |
---|---|
encode(encoding) | 用于把字符串编码转换为二进制数据,其中,encoding表示编码格式,通常设置为utf-8 |
python
strs = '需要传输的数据'
# 编码为二进制
bs = strs.encode(encoding='utf-8')
print(bs)
# 解码为字符串
result = bs.decode(encoding='utf-8')
print(result)
当二进制bytes数据转换为字符串str类型时
函数名 | 含义 |
---|---|
decode(encoding) | 用于把二进制数据转换为字符串,其中,encoding表示编码格式,通常设置为utf-8 |
python
bs = b"需要传输的数据"
result = bs.decode(encoding='utf-8')
print(result)
收发一句话
- 创建一个socket对象服务器端,并向客户端发送消息内容 Webcome to study socket!
python
import socket
if __name__ == "__main__":
# 创建服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定ip和端口号,如果ip地址设置为"",则表示绑定本机ip地址
tcp_server_socket.bind(("", 8080))
# 设置监听128,代表服务端等待排队连接的最大数量
tcp_server_socket.listen(128)
print("服务端已启动,等待客户端连接...")
# 等待接受客户端的连接请求accept阻塞等待,返回一个用以和客户端通信的socket,和客户端的地址
client_socket,client_addr = tcp_server_socket.accept()
print(f"客户端地址为:{client_addr}")
# 接收消息
recv_data = client_socket.recv(1024) # 1024代表每次接收的最大字节数
print(f"接收到数据为:{recv_data.decode('utf-8')}")
# 发送消息
client_socket.send("Webcome to study socket!".encode("utf-8"))
# 关闭资源
client_socket.close()
tcp_server_socket.close()
- 创建一个socket对象客户端,并用来接收服务器端发送过来的内容
python
import socket
if __name__ == "__main__":
# 创建客户端套接字对象
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 和服务器端建立连接
tcp_client_socket.connect(("127.0.0.1", 8080))
# 发送消息
tcp_client_socket.send("Hello Server!".encode("utf-8"))
# 接收消息
recv_data = tcp_client_socket.recv(1024)
print(f"接收到数据为:{recv_data.decode('utf-8')}")
# 关闭资源
tcp_client_socket.close()
当客户端和服务端建立连接后,服务端程序退出后端口号不会立即释放,需要等待大概1~2分钟,解决方案如下:
- 更换服务端端口号
- 设置端口号复用(推荐使用),即让服务端程序退出后端口号立即释放
设置端口号复用的代码
python
# 参数1: 表示当前套接字
# 参数2: 设置端口号复用选项
# 参数3: 设置端口号复用选择对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
TCP网络程序的注意点
- TCP服务端程序必须绑定端口号,否则客户端找不到这个TCP服务端程序,为了更稳定,建议把IP也绑定
accept()
前的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息- 当TCP客户端程序和TCP服务端程序连接成功后,TCP服务端程序会产生一个新的套接字,用于收发客户端消息
- 若关闭
accept()
返回的被动连接套接字,则表示和这个客户端已经通信完毕 - 对于服务器端socket,关闭时要慎重
- 当客户端的套接字调用close后,服务器端的recv会被阻塞,返回的长度为0.用于判断客户端是否已经下线
服务器端接收多客户端信息
python
import socket
if __name__ == "__main__":
# 创建服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定ip和端口号,如果ip地址设置为"",则表示绑定本机ip地址
tcp_server_socket.bind(("", 8080))
# 设置监听128,代表服务端等待排队连接的最大数量
tcp_server_socket.listen(128)
print("服务端已启动,等待客户端连接...")
while True:
# 等待接受客户端的连接请求accept阻塞等待,返回一个用以和客户端通信的socket,和客户端的地址
client_socket,client_addr = tcp_server_socket.accept()
print(f"客户端地址为:{client_addr}")
# 接收消息
recv_data = client_socket.recv(1024) # 1024代表每次接收的最大字节数
print(f"接收到数据为:{recv_data.decode('utf-8')}")
# 发送消息
client_socket.send("Webcome to study socket!".encode("utf-8"))
# 关闭资源
client_socket.close()
tcp_server_socket.close()