python基础-基于tcp/ip、udp的Scoket使用
操作系统封装了应用层下的OSI协议并抽象出了Sokcet,让应用层的通讯不用关心底层的实现。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是操作系统封装完下层协议后封装出来的一组接口。
基于tcp/ip的Scoket使用
通过基于tcp/ip的socket可以进行internet上可靠的通讯
服务端代码:
import socket
# 1.调用socket方法创建一个socket对象
# socket.AF_INET 代表进行internet间的通讯
# socket.SOCK_STREAM 代表使用tcp/ip的流式协议
soc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2.设置监听的端口
soc.bind(('127.0.0.1',8081)) # 0-65535, 1024以前的都被系统保留使用
# 3.设置半连接池的大小
soc.listen(5)
# 4.阻塞在此,直到接受客户端的消息
# 接收到消息时,会获得消息的内容和发送方的链接对象
conn,client_addr=phone.accept()
# 5.设置最大接受数据的量
data=conn.recv(1024) # 最大接收的数据量为1024Bytes,收到的是bytes类型
# 字符编码需要根据客户端的编码进行解码
print("客户端发来的消息:",data.decode('utf-8'))
# 6.给客户端发送消息
conn.send(data.upper())
# 7.关闭对应客户端的链接
conn.close()
# 8.关闭socket
soc.close()
客户端代码:
在客户端
import socket
# 1.创建socket对象
soc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2.绑定服务端的地址
soc.connect(('127.0.0.1',8081))
# 3.发送消息
soc.send('hello 哈哈哈'.encode('utf-8'))
# 4.接受返回的消息
data=phone.recv(1024)
print(data.decode('utf-8'))
# 5.关闭连接(必选的回收资源的操作)
soc.close()
黏包问题
产生黏包问题的原因:
发送端发送的数据时间间隔很小,由于系统会使用nagle算法对tcp进行优化,会导致,多条数据一块发送给服务端
接受端没有及时接受缓冲区的包,导致读取时取出来的为上次没接受完的数据
解决方式:
增加数据的头信息:数据信息的长度,数据信息
# 服务端解决黏包问题
import struct
# 1、生成头信息
header_dic={
"filename":"a.txt",
# 真实数据的长度
"total_size":total_size,
"md5":"xxxxx"
}
json_str = json.dumps(header_dic)
json_str_bytes = json_str.encode('utf-8')
# 2、获取头的长度,并发送
# 使用struct可以使用固定的4个字节存储头的长度
x=struct.pack('I',len(json_str_bytes))
conn.send(x)
# 3、发头信息
conn.send(json_str_bytes)
# 4、再发真实的数据
conn.send(stdout_res)
在客户端的处理步骤:1. 接收4个字节 2.根据四个自己的信息,读出头部信息 3.根据头部信息,读出相应长度的数据。
tip:
如果客户端没有正常关闭scoket(基于流传输)会发生什么?
则连接会出现错误,则在unix系统中会不停的收到空数据,在windows中会报错,如果持续监听,需要进行相关处理。
关闭socket后端口不会马上释放,因为操作系统不会立即回收端口。
基于UDP的Socket使用
udp 协议不会保证数据的可靠传输,但拥有更快的传输速度。udp的数据在接收时,如果一次没有接收完,那么剩余的数据会被丢弃
服务端代码
import socket
# 1. 创建socket对象
# 使用数据报协议=》udp
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2. 绑定服务端口
server.bind(('127.0.0.1',8081))
# 3. 接受数据
data,client_addr=server.recvfrom(1024)
# 4. 给客户端发送消息
server.sendto(data.upper(),client_addr)
# 5. 关闭连接
server.close()
客户端
import socket
# 1. 创建socket对象
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2. 发送数据
client.sendto('hello'.encode('utf-8'),('127.0.0.1',8081))
# 3. 接受数据
res=client.recvfrom(1024)
# 4. 关闭连接
client.close()
udp协议不存在黏包问题,如果服务端只接受了数据的一部分,那么剩余部分将被抛弃
tip:
如果客户端没有正常关闭scoket(基于包传输)会发生什么?
不会影响服务端的接收数据,udp协议中,客户端和服务端是没有关联的
流传输协议、包传输协议:
流传输协议:
在流式协议中,如果A要给B发送数据,那么A可以分多次发,然后B一次接受或者多次接受,A中如果使用socket发送的为空数据,那么操作系统将不会发送该数据。
包传输协议:
在包传输协议中,如果为空数据,那么系统也会将空数据进行包装发送给接收端。
other
nagle算法:Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。这种机制减少了网络中传输的拥塞。
域名解析包括递归和迭代
scoket 封装了 传输层、网络层 数据链路层,
socket 由操作系统完成,
物理层 执行的是发送和接受
那么应用层程序结束后,链接资源是什么时候回收的呢?