网络:网络就是实现资源共享和信息传递的虚拟平台,我们可以编写基于网络通信的程序。比如socket编程,web开发
Socket编程
Socket是程序之间通信的一个工具,好比显示生活中的电话,你知道了对方的电话号码之后,需要使用电话进行通讯。同理你知道了对方的ip 地址和端口号之后,你需要使用socket进行通信。
在通信之前,我们需要选择网络通讯协议(网络传输方式)。保证程序之间按照指定的规则进行数据通信。
TCP
TCP 简称 传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
1、面向连接:通信双方必须先建立好连接才能进行数据传输,并且双方都会为此连接分配必要资源用来记录连接的状态和信息。当数据传输完成之后,双方必须断开此连接,以释放系统资源。
2、可靠传输:
TCP采用发送应答机制:
通过TCP这种方式发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段发送成功
超时重传
发送端发送一个报文之后就会启动定时器,如果指定的时间内没有得到应答就会重新发送这个报文
错误校验
TCP用一个 校验和 函数来校验数据是否有错误,在发送和接收时都要计算 校验和
流量控制和阻塞管理
流量控制用来避免发送端发送过快而使得接收方来不及接收
通信步骤:
1、创建连接
2、传输数据
3、关闭连接
python编码转换
在网络中传输的数据都是二进制的数据,所以我们在发送数据的时候需要将数据转换为二进制,到达目的地之后再将二进制数据转换回来。
转码: encode
解码: decode
TCP客户端程序开发
开发流程:
开发步骤:
1、导入socket模块
import socket
2、创建客户端socket对象
socket.socket(AddressFamily , Type)
AddressFamily:IP地址类型,分为Ipv4和Ipv6
type:传输协议类型
3、建立连接
connect
4、数据传输
send:发送数据
recv:接收数据
close:关闭连接
代码演示:
import socket
if __name__ == '__main__':
# 1、创建客户端套接字对象
# 静态常量 AF_INET代表Ipv4 SOCK_STREAM代表TCP协议
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2、和服务器建立套接字连接。参数是元组,指定ip和端口
tcp_client_socket.connect(("127.0.0.1", 8888))
# 3、发送数据,需要进行编码
tcp_client_socket.send("hello".encode(encoding="utf-8"))
# 4、接收数据,每次接收数据的大小,单位是字节,recv会阻塞等待数据到来,如果你不需要接收数据,那就跳过这步
recv_data = tcp_client_socket.recv(1024)
print(recv_data.decode()) # 默认utf-8解码,可省略不写
# 5、关闭客户端套接字
tcp_client_socket.close()
TCP服务端程序开发
1、创建服务端套接字对象
2、绑定IP地址和端口号,通过bind()函数完成
3、设置监听 , listen()函数
4、等待接收客户端连接请求
5、接收数据
6、发送数据
7、关闭套接字
开发步骤:
1、导入socket模块
import socket
2、创建服务端socket对象
socket.socket(AddressFamily , Type)
AddressFamily:IP地址类型,分为Ipv4和Ipv6
type:传输协议类型
3、绑定ip地址和端口
bind
4、设置监听
listen
5、等待接收客户端的连接请求
accept
发送数据
6、数据传输
send:发送数据
recv:接收数据
close:关闭连接
代码演示:
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.31.59", 8888))
tcp_server_socket.bind(("", 8888)) # 不设置的话,会自动填充本机ip地址
# 3、设置监听
tcp_server_socket.listen(128) # 参数128:代表服务端等待排队连接的最大数量,超出的限制的连接就丢弃
# 4、等待客户端请求
# 这步会阻塞等待,如果接收到请求,返回一个用以和客户端通讯的socket,和客户端地址
# 上面创建的socket只是监听,不会发送数据
connect_socket, ip_port = tcp_server_socket.accept()
print("客户端地址:", ip_port)
# 5、接收数据
recv_data = connect_socket.recv(1024)
print("接收到的数据:", recv_data.decode())
# 6、发送数据
connect_socket.send("客户端数据已收到".encode())
# 7、关闭套接字
connect_socket.close()
tcp_server_socket.close()
socket的发送和接收缓冲区
当创建一个TCP socket对象的时候,会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区就是内存中的一片空间。
当你要发送消息时,先将消息存放在缓冲区,当消息达到一定的数量之后再一次性进行发送。
send原理解析:
问题:send是不是直接把数据发送给服务端?
不是,要想发送数据,必须得通过网卡发送数据,应用程序无法直接通过网卡发送数据,它需要调用操作系统接口,也就是说,应用程序把发送得数据先写入到发送缓冲区(内存一片空间),再由操作系统控制网卡把发送缓冲区得数据发送给服务端网卡。
recv原理解析:
问题:recv是不是直接从客户端接收数据?
不是,应用程序无法直接通过网卡接收数据,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收得数据写入接收缓冲区(内存一片空间),应用程序再从接收缓冲区获取客户端发送得数据。
Demo多任务版TCP服务端程序
import socket
import threading
def tcp_server_thread(connect_socket, ip_port):
print("客户端地址:", ip_port)
# 5、接收数据
recv_data = connect_socket.recv(1024)
print("接收到的数据:", recv_data.decode())
# 6、发送数据
connect_socket.send("客户端数据已收到".encode())
# 7、关闭套接字
connect_socket.close()
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.31.59", 8888))
tcp_server_socket.bind(("", 8888)) # 不设置的话,会自动填充本机ip地址
# 3、设置监听
tcp_server_socket.listen(128) # 参数128:代表服务端等待排队连接的最大数量,超出的限制的连接就丢弃
while True:
# 4、等待客户端请求
# 这步会阻塞等待,如果接收到请求,返回一个用以和客户端通讯的socket,和客户端地址
# 上面创建的socket只是监听,不会发送数据
connect_socket, ip_port = tcp_server_socket.accept()
server_thread = threading.Thread(target=tcp_server_thread, args=(connect_socket, ip_port))
server_thread.start()
tcp_server_socket.close()