1、不同电脑上的进程之间如何通信
网络层的'ip地址'可以唯一标识网络中的主机,而传输层的'协议(TCP/IP协议族)+端口'可以唯一标识主机中的应用进程(进程)
进程:运行的程序以及运行时用到的资源这个整体称为进程
进程间通信:运行的程序之间的数据共享
2、socket简介
socket(套接字)是进程间通信的一种方式,它与其它进程间通信的一个主要不同是:它能实现不同主机间的进程间通信。
3、创建socket
在 Python 中 使用socket 模块的函数 socket 就可以完成:
import socket
socket.socket(AddressFamily, Type)
说明:
函数 socket.socket 创建一个 socket,该函数带有两个参数:
(1)Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
(2)Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个tcp socket(tcp套接字)
import socket
# 创建tcp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ...这里是使用套接字的功能(省略)...
# 不用的时候,关闭套接字
s.close()
创建一个udp socket(udp套接字)
import socket
# 创建udp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# ...这里是使用套接字的功能(省略)...
# 不用的时候,关闭套接字
s.close()
说明:套接字使用流程与文件的使用流程类似:
(1)创建套接字
(2)使用套接字收/发数据
(3)关闭套接字
4、网络-udp
4.1 upd网络程序-发送、接受数据
创建一个基于udp的网络程序流程很简单,具体步骤如下:
(1)创建客户端套接字
(2)发送/接受数据
(3)关闭套接字
from socket import *
# 1. 创建udp套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 2. 准备接收方的地址
# '192.168.1.103'表示目的ip地址
# 8080表示目的端口
dest_addr = ('192.168.1.103', 8080) # 注意 是元组,ip是字符串,端口是数字
# 3. 从键盘获取数据
send_data = input("请输入要发送的数据:")
# 4. 发送数据到指定的电脑上的指定程序中
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
# 5. 等待接收对方发送的数据
recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数
# 6. 显示对方发送的数据
# 接收到的数据recv_data是一个元组
# 第1个元素是对方发送的数据
# 第2个元素是对方的ip和端口
print(recv_data[0].decode('gbk'))
print(recv_data[1])
# 7. 关闭套接字
udp_socket.close()
4.2 python3编码转换
字符串通过编码(encode)成为字节码,字节码通过解码(decode)成为字符串
其中decode()与encode()方法可以接受参数,其声明分别为:
bytes.decode(encoding="utf-8", errors="strict")
str.encode(encoding="utf-8", errors="strict")
其中的encoding是指在解码编码过程中使用的编码(此处指“编码方案”是名词),errors是指错误的处理方案。
4.3 udp绑定信息
一个udp网络程序,可以不绑定,此时操作系统会随机进行分配一个端口,如果重新运行此程序端口可能会发生变化
一个udp网络程序,也可以绑定信息(ip地址,端口号),如果绑定成功,那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的
from socket import *
# 1. 创建套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 2. 绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配
local_addr = ('', 7788) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip
udp_socket.bind(local_addr)
# 3. 等待接收对方发送的数据
recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数
# 4. 显示接收到的数据
print(recv_data[0].decode('gbk'))
# 5. 关闭套接字
udp_socket.close()
4.4 案例:udp聊天器
https://www.cnblogs.com/nuochengze/p/12629226.html
5、网络-tcp
5.1 TCP简介
TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
5.2 TCP的特点
5.2.1 面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这一个连接进行。
完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此tcp不适用于广播的应用程序,基于广播的应用程序可以使用UDP协议。
5.2.2 可靠传输
(1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
(2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
(3)错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
(4)流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
5.3 TCP与UDP的不同点
面向连接(确认有创建三方交握,连接已创建才作传输。)
有序数据传输
重发丢失的数据包
舍弃重复的数据包
无差错的数据传输
阻塞/流量控制
5.4 TCP通信模型
5.5 tcp客户端构建流程
tcp客户端构建流程:
(1)socket创建一个套接字
(2)链接服务器端
(3)发送和接受数据
(4)关闭套接字
1 import socket 2 3 4 def main(): 5 # 创建tcp的套接字 6 tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 7 # 链接服务器 8 server_ip = input("请输入要链接的服务器的ip地址:") 9 server_port = int(input("请输入要链接的服务器的端口:")) 10 server_addr = (server_ip,server_port) 11 tcp_client_socket.connect(server_addr) 12 # 发送数据/接收数据 13 send_data = input("请输入要发送的信息:") 14 tcp_client_socket.send(send_data.encode('utf-8')) 15 recv_data = tcp_client_socket.recv(1024) 16 print('接受到的数据为:{}'.format(recv_data.decode('utf-8'))) 17 # 关闭套接字 18 tcp_client_socket.close() 19 20 if __name__=='__main__': 21 main()
5.6 tcp服务器端构建流程
tcp服务器端构建流程:
(1)socket创建一个套接字
(2)bind绑定ip和port
(3)listen使套接字变为可以被动接听
(4)accept等待客户端的链接
(5)recv/send接收发送数据
1 import socket 2 3 def main(): 4 # 创建套接字 5 tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 6 # 绑定本地信息bind 7 localaddr = ('',8081) 8 tcp_server_socket.bind(localaddr) 9 # 让默认的套接字由主动变成被动listen 10 tcp_server_socket.listen(128) 11 print('——等待客人上门——') 12 # 等待客户端的链接 accept 13 new_client_socket,client_addr = tcp_server_socket.accept() 14 15 print('——链接上了,客户端地址:{}——'.format(client_addr)) 16 print('——等待客人提需求——') 17 18 # 接受客户端发送过来的数据 19 recv_data = new_client_socket.recv(1024) 20 print('客人的要求是:{}'.format(recv_data.decode('utf-8'))) 21 22 # 回送一部分数据给客户端 23 new_client_socket.send('再见,欢迎再次光临'.encode('utf-8')) 24 25 print('———客人离开了———') 26 27 # 关闭套接字 28 new_client_socket.close() 29 tcp_server_socket.close() 30 31 32 if __name__=='__main__': 33 main()
5.6 tcp注意点
- tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器
- tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机
- tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
- 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信
- 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
- listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的
- 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
- 关闭accept返回的套接字意味着这个客户端已经服务完毕
- 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
5.7 案例:文件下载器
https://www.cnblogs.com/nuochengze/p/12638066.html
5.8 长连接和短连接
tcp通信的过程:
5.8.1 tcp短连接
模拟一种TCP短连接的情况:
- client 向 server 发起连接请求
- server 接到请求,双方建立连接
- client 向 server 发送消息
- server 回应 client
- 一次读写完成,此时双方任何一个都可以发起 close 操作
在步骤5中,一般都是 client 先发起 close 操作。当然也不排除有特殊的情况。
短连接一般只会在 client/server 间传递一次读写操作!
5.8.2 tcp长连接
模拟一种长连接的情况:
- client 向 server 发起连接
- server 接到请求,双方建立连接
- client 向 server 发送消息
- server 回应 client
- 一次读写完成,连接不关闭
- 后续读写操作...
- 长时间操作之后client发起关闭请求