Tcp协议与Udp协议
规定了数据传输所遵循的规则
数据传输能够遵循的协议有很多 Tcp与Udp是比较为常见的两个
一、Tcp协议
三次握手
建立双向通道

四次挥手
断开双向通道

基于Tcp传输数据非常的安全 因为有双向通道是否正确?
基于Tcp传输,数据不容易丢失 不容易丢失的原因在于二次确认机制
每次发送数据都需要返回确认信息 否则会在一定的时间内反复发送
二、Udp协议
基于Udp协议发送数据 没有任何的通道也没有任何的限制
Udp发送数据没有Tcp安全(没有两次确认机制)
socket套接字
一、基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
二、基于网络类型的套接字家族
套接字家族的名字:AF_INET
三、基于 Tcp 的scoket服务端与客户端
运行程序的时候 确保是服务端先运行 之后才是客户端
简易版
服务端
import socket
# 1.创建一个socket对象
server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080)) # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address) # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~') # 朝客户端发送数据
data = sock.recv(1024) # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close() # 断链接
server.close() # 关机
客服端
import socket
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024) # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server') # 朝服务端发送数据
# 4.关闭
client.close()
代码优化版本
1.send与recv
客户端与服务端不能同时执行同一个
有一个收 另外一个就是发
有一个发 另外一个就是收
不能同时收或者发!!!
2.消息自定义
input获取用户数据即可(主要编码解码)
3.循环通信
给数据交互环节添加循环即可
4.服务端能够持续提供服务
不会因为客户端断开连接而报错
解决措施:使用异常捕获 一旦客户端断开连接 服务端结束通信循环 调到连接处等待
5.消息不能为空
判断是否为空 如果是则重新输入(主要针对客户端)
6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)
这个错在苹果系统报的频繁 windows频率比较少
from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加上此代码
7.客户端异常退出会发送空消息(针对mac linux)
针对接收的消息加判断处理即可
服务端
import socket
from socket import SOL_SOCKET, SO_REUSEADDR
# 1.创建一个socket对象
server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加
server.bind(('127.0.0.1', 8080)) # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
while True:
sock, address = server.accept()
print(sock, address) # sock是双向通道 address是客户端地址
# 5.数据交互
while True:
try:
msg = input('请输入发送给客户端的消息>>>:').strip()
if len(msg) == 0: continue
sock.send(msg.encode('utf8')) # 朝客户端发送数据
data = sock.recv(1024) # 接收客户端发送的数据 1024bytes
if len(data) == 0: #
break
print(data.decode('utf8'))
except ConnectionResetError:
sock.close()
break
客服端
import socket
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
while True:
data = client.recv(1024) # 接收服务端发送的数据
print(data.decode('utf8'))
msg = input('请输入发送给客户端的消息>>>:').strip()
if len(msg) == 0:
msg = '手抖了一下 暂无消息'
client.send(msg.encode('utf8')) # 朝服务端发送数据
半连接池
server.listen(5)
设置最大等待人数
主要是为了做缓冲 避免太多的无效等待
黏包问题
服务端代码
sock.recv(1024)
sock.recv(1024)
sock.recv(1024)
客户端代码
client.send(b'hello')
client.send(b'joker')
client.send(b'jason')
"""
执行的结果
b'hellojokerjason'
b''
b''
"""
一、Tcp的特性
流式协议:所有的数据类似于水流 连接在一起
数据量很小 并且时间间隔很多 那么就会自动组织到一起
二、recv
我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包现象
解决黏包问题
struct模块
import struct
info = '今天星期五了哎!'
print(len(info)) # 8 数据原来的长度
res = struct.pack('i', len(info)) # 将原来的数据长度打包
print(len(res)) # 4 打包之后的长度是4
ret = struct.unpack('i', res) # 将打包之后固定长度为4的数据拆包
print(ret[0]) # 8 又得到了原来数据的长度
info1 = '过了今天就明天就是一个愉快的周末了,但还是得敲可恶选课系统'
print(len(info1)) # 29
res = struct.pack('i', len(info1)) # 将原来的数据长度打包
print(len(res)) # 4 打包之后的长度是4
ret = struct.unpack('i', res)
print(ret[0]) # 29
pack可以将无论数字长度是多少的 都可以打包成固定的长度
unpack可以将基于该固定的长度 反向解析出真实的长度
思路:
服务端
1.先将真实数据的长度制作成固定长度
2.先发送固定长度的报头
3.再发送真实数据
客服端
1.先接收固定长度的报头
2.再根据报头解压出真实长度
3.根据真实长度接收即可
代码实操
服务端
while True:
sock,addr = server.accept()
while True:
# 1.先构造数据文件的字典
file_dict = {
'file_name':'精彩写真',
'file_size':os.path.getsize(r'精彩写真'),
'file_content':'美女图片',
'file_root':'joker'
}
# 2.将文件字典打包成固定长度数据
dict_json = json.dumps(file_dict)
file_bytes_dict = len(dict_json.encode('utf8'))
dict_len = struct.pack('i',file_bytes_dict)
# 3.发送固定长度的字典报头
sock.send(dict_len)
# 4.发送真实字典数据 将字典序列化成json格式字符串
sock.send(dict_json.encode('utf8'))
# 5.发送真实数据
with open(r'精彩写真.txt','rb')as f:
for line in f:
sock.send(line)
break
客户端
import socket
import struct
import json
client = socket.socket()
client.connect(('127.0.0.1',8088))
while True:
# 1.先接收长度为4的报头数据
header_len = client.recv(4)
# 2.根据报头解包出字典的长度
dict_len = struct.unpack('i',header_len)[0]
# 3.直接接收字典数据
dict_data = client.recv(dict_len) # bytes类型
# 4.解码并反序列化出字典
real_dict = json.loads(dict_data)
# 5.从数据字典中获取真实数据的各项信息
total_size = real_dict.get('file_size')
# client.recv(total_size) # 简单粗暴的方法 有多少一次性就读读多少
file_size = 0
with open(r'%s'%real_dict.get('file_name'),'wb')as f:
while file_size < total_size:
data = client.recv(1024)
f.write(data)
file_size += len(data)
posted on
浙公网安备 33010602011771号