OSI七层协议

传输层

1.PORT协议:前面讲过
2.TCP协议与UDP协议:规定了数据传输所遵循的规则(数据传输能够遵循的协议有很多,TCP和UDP是较为常见的两个)

TCP协议

基于TCP传输数据是非常安全的,是因为数据不容易丢失,并非是因为有双向通道。不容易丢失的原因在于二次确认机制,每次发送数据都需要返回确认消息,否则在一定时间会反复发送

三次握手–建立双向通道

过程
1.客户端向服务端发送请求,询问是否可以建立一个数据通道
2.服务端就向客户端发送确认,允许客户端建立一个通向服务端的数据通道
ps:此时数据通道是单向的,只允许客户端向服务端发送信息
3.服务端向客户端发送请求,询问是否可以建立一个数据通道
4.客户端就向服务端发送确认,允许服务端建立一个通向服务端的数据通道
ps:此时数据通道是双向的,允许客户端、服务端互相发送信息上述有四步,可以将2、3不合并成一步,因为服务端向客户端发送确认的同时,也可以发送请求。将三步称之为三次握手
四次挥手–断开双向通道

过程
1.客户端向服务端发送请求,询问是否可以关闭客户端到服务端的数据通道
2.服务端就向客户端发送确认,允许关闭客户端到服务端的数据通道
ps:此时数据通道由双向变为单向,只有服务端到客户端的通道了
ps:这是服务端会检查还有没有数据没有发送完毕,没有发送完毕就继续发送,发送完毕就发送请求关闭通道
3.服务端向客户端发送请求,询问是否可以关闭服务端到客户端的数据通道
4.客户端就向服务端发送确认,允许关闭服务端到客户端的数据通道
ps:此时数据通道就完全关闭了
上述四个步骤称之为四次挥手,关闭双向通道,2、3步不能合并,因为需要有检查时间,看看数据是否发送完毕没有

UDP协议

基于UDP协议发送数据,没有任何的通道也没有任何的限制,UDP发送数据没有TCP安全,因为没有二次确认机制

总结

TCP类似于打电话,有来有往;UDP类似于发送短信,只要发送了,剩下的什么都不管

应用层

主要取决于程序员自己采用什么策略和协议,常见的协议有HTTP、HTTPS、FTP…

socket套接字

基于文件类型的套接字家族的名字是:AF_UNIX;基于网络类型的套接字家族的名字是:AF_INET

代码–基础

运行程序的时候,先确保服务端运行,之后才是客户端

服务端

1.创建一个socket对象
import socket
server = socket.socket()ps:socket是一个类,socket()此时就相当于实例化对象  def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1',8080)) ps:1.bind() >>> def bind(self, address: Union[_Address, bytes]) -> None: ...>>>里面可以填元组,字符串,一般填写元组,字符串会出错在后续2. 127.0.0.1 ip地址,这里是本地回环地址,只允许自己的机器访问8080 端口号
3.半连接池

主要是为了做缓冲,避免太多的无效等待

server.listen(5)
4.开业 等待接客
sock,address = server.accept()
print(sock,address)      ps: 1. 类里面的函数accept()返回值是 return sock, addr2.sock是双向通道,address是客户端地址
5.数据交互
sock.send(b"hello client ,i'm server")
data = sock.recv(1024)
print(data)ps: 1. sock.send()是操客户端发送数据2. sock.recv()是接收客户端发送的数据 1024代表接收1024bytes
6.断开连接
sock.close()  #  断连接
server.close()  # 关机

客户端

1.产生一个socket对象
import 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 server ,i'm client")ps: client.recv(1024) 是接收服务端发送的数据 1024代表接收1024bytesclient.send()朝服务端发送数据
4.关闭
client.close()

代码–问题及优化

1.客户端与服务端不能同时执行同一个send或recv

意思就是如果客户端先接收(recv()),那么服务端就先发(send());如果客户端先发(send()),那么服务端就先收(recv())

2.消息自定义

用input获取用户数据(编码解码即可)

服务端
msg= input("请输入你想给客户端发送的内容>>>:").strip()
sock.send(msg.encode('utf8'))
data = sock.recv(1024)
print(data.decode('utf8'))客户端
data = client.recv(1024)
print(data.decode('utf8'))
msg= input("请输入你想给服务端发送的内容>>>:").strip()
client.send(msg.encode('utf8'))

3.循环通信

给数据交互环节添加循环即可

服务端
while True:msg= input("请输入你想给客户端发送的内容>>>:").strip()sock.send(msg.encode('utf8'))data = sock.recv(1024)print(data.decode('utf8'))客户端
while True:data = client.recv(1024)print(data.decode('utf8'))msg= input("请输入你想给服务端发送的内容>>>:").strip()client.send(msg.encode('utf8'))

4.服务端能够持续提供服务

也就是不会因为客户端断开连接而报错;我们就可以使用异常捕获:一旦客户断开连接,服务端就结束通信循环,跳到连接等待接客处

服务端
while True:sock,address = server.accept()print(sock,address)# 数据交互while True:try:msg= input("请输入你想给客户端发送的内容>>>:").strip()sock.send(msg.encode('utf8'))data = sock.recv(1024)print(data.decode('utf8'))except ConnectionResetError :sock.close()break

5.消息不能为空

解决方式是判断待发送的消息是否为空,如果是则重新输入(主要针对客户端)

客户端
while True:data = client.recv(1024)print(data.decode('utf8'))msg= input("请输入你想给服务端发送的内容>>>:").strip()if len(msg) == 0:msg = '手抖了一下'client.send(msg.encode('utf8'))

6.服务端频繁重启可能会报端口号被占用的错误(主要针对mac电脑)

在服务端写一下代码

 from socket import SOL_SOCKET,SO_REUSEADDRserver.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind()前加

7.客户端异常退出就会发送空消息(针对mac linux)

针对接收的消息加判断处理即可

黏包问题

服务端接收数据,客户端发送数据,黏包问题更明显

服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
sock, address = server.accept()
print(sock.recv(1024))
print(sock.recv(1024))
print(sock.recv(1024))客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))
client.send(b'jason')
client.send(b'kevin')
client.send(b'jerry')

产生原因

1.TCP特性
流式协议:内容与内容之间没有明确的分界标志,所有的数据类似于水流连接在一起的ps:数据量小 并且时间间隔很多,那么就会自动组织到一起
2.recv() 括号里规定了读取的字节数。由于我们不知道即将要接收的数据量多大,就容易产生黏包问题

struct模块

struct模块无论数据长度多少,都可以帮你打包成固定的长度;然后基于固定的长度,还可以反向解析出真实的长度

import struct
info1 = '你好 world'
print(len(info1))  # 8
res1 = struct.pack('i',len(info1))
print(res1)  #b'\x08\x00\x00\x00'
print(len(res1))  # 4
res2 = struct.unpack('i',res1)
print(res2)  # (8,)
print(res2[0])  # 8import struct
info1 = '你好啊 你是我的全世界'
print(len(info1))  # 11
res1 = struct.pack('i',len(info1))
print(res1)  #b'\x0b\x00\x00\x00'
print(len(res1))  # 4
res2 = struct.unpack('i',res1)
print(res2)  # (11,)
print(res2[0])  # 11

struct模块很对数据量特别大的数字没有办法打包

file_size = 11203495674265677
res = struct.pack('i',file_size)
print(len(res))  # struct.error: argument out of range

可以以字典的形式,解决数据量大的问题

file_dic ={'size':1223456575567567685,'name':'学习视频.mp4'
}
print(len(file_dic))  # 2
res = struct.pack('i',len(file_dic))
print(len(res))  # 4
res1 = struct.unpack('i',res)
print(res1[0])  # 2

struct知识点总结

1.struct.pack('i',len(info1)) 是将数据原来的长度打包,打印返回值是二进制,用len()方法打印出来是4, i是模式
2.struct.unpack('i',res1) 将打包之后固定长度为4的数据拆包,打印返回值是一个元组,用索引取值取值出来的就是原来数据的长度

思路

1.学习了struct模块,对解决黏包问题有了一定的思路

1.先将真实数据的长度制作成固定的长度>>>struct.pack()
2.先发送固定长度的报头 >>>sock.send()
3.再发送真实的数据1.先接收固定长度的报头 >>> sock.recv(4)
2.再根据报头解压出真实的长度 >>>struct.unpack()
3.根据真实长度接收即可

2.问题:struct模块很对数据量特别大的数字没有办法打包

file_size = 11203495674265677
res = struct.pack('i',file_size)
print(len(res))  # struct.error: argument out of range

3.解决:可以以字典的形式,解决数据量大的问题

file_dic ={'size':1223456575567567685,'name':'学习视频.mp4'
}
print(len(file_dic))  # 2
res = struct.pack('i',len(file_dic))
print(len(res))  # 4
res1 = struct.unpack('i',res)
print(res1[0])  # 2

4.终极解决黏包方法

服务端
1.先构造一个数据的详细字典
2.对字典数据进行打包处理,得到一个固定长度的数据>>>struct.pack() 4
3.将上述打包之后的数据发送给客户端 >>>sock.send()
4.将字典数据发送给客户端
5.将真实数据发送给客户端客户端
1.先接收固定长度的数据 >>> sock.recv(4)
2.根据固定长度解析出即将要接收的字典真实长度 >>>struct.unpack()
3.接收字典数据
4.根据字典数据,获取出真实数据的长度
5.接收真实数据长度

解决

服务端

import json
import os
import socket
import structserver = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)while True:sock,address = server.accept()while True:# 1.先构造数据文件的字典file_dict = {'name':'luo编程视频合集.txt','size':os.path.getsize(r'../xxx视频合集.txt'),}# 将字典打包成固定长度的数据dict_json = json.dumps(file_dict)  # 将字典转换成json字符串file_bytes_dict = dict_json.encode('utf8')  # 将json字符串格式转换成二进制dict_len_bytes= struct.pack('i',len(file_bytes_dict))# 发送固定长度的字典报头sock.send(dict_len_bytes)# 发送真实字典数据sock.send(dict_json.encode('utf8'))# 发送真实数据with open(r'../xxx视频合集.txt','rb') as f:for line in f:sock.send(line)break

客户端

import json
import socket
import structclient = socket.socket()
client.connect(('127.0.0.1',8080))
while True:# 先接收长度为4的报头数据header_len = client.recv(4)  # 接收的是dict_len_bytes# 根据报头解包出字典的长度dict_len = struct.unpack('i',header_len)[0]  # 68# 直接接收字典数据dict_data = client.recv(dict_len)   # b'{"name": "luo\\u7f16\\u7a0b\\u89c6\\u9891\\u5408\\u96c6.txt", "size": 511}''# 解码并反序列化出字典real_dict = json.loads(dict_data)  # {'name': 'luo编程视频合集.txt', 'size': 511}# 从数据字典中获取真实数据的各项信息total_size = real_dict.get('size')file_size = 0with open(r'%s' % real_dict.get('name'),'wb') as f:while file_size < total_size:data = client.recv(1024)f.write(data)file_size += len(data)print('文件接收完毕')break

TCP与UDP协议、socket套接字编程、通信相关操作(cs架构软件)、TCP黏包问题及解决思路相关推荐

  1. udp协议没有粘包问题、基于socketserver实现并发的socket(基于tcp、udp协议)、基于udp协议的套接字、操作系统原理以及进程知识

    基于udp协议的套接字通信服务端 from socket import *server=socket(AF_INET,SOCK_DGRAM) #数据报协议->udp server.bind((' ...

  2. 基于UDP协议的socket套接字编程 基于socketserver实现并发的socket编程

    基于UDP协议 的socket套接字编程 1.UDP套接字简单示例 1.1服务端 import socketserver = socket.socket(socket.AF_INET,socket.S ...

  3. 基于UDP协议的套接字+socketserver模块

    基于UDP协议的套接字 user datagram protocal 数据报协议 无双向通道.自带报头.类似发短信.不会粘包 不可靠:数据发送时,丢就丢了 UDP socket: 服务端: impor ...

  4. 20181225 基于TCP/IP和基于UDP/IP的套接字编程

    一.TCP/IP的套接字编程 服务器端代码: import socket​server = socket.socket() # 默认是基于TCP# 基于TCP的对象serve=socket.socke ...

  5. day26-2 基于TCP协议的套接字编程

    目录 基于TCP协议的套接字编程 套接字 套接字工作流程 基于TCP协议的套接字编程(简单) 服务端 客户端 基于TCP协议的套接字编程(循环) 服务端 客户端1 客户端2 基于TCP协议的套接字编程 ...

  6. 自学Python 58 Socket套接字编程 (一)

    Python Socket套接字编程 (一) 文章目录 Python Socket套接字编程 (一) 一.库 Socket 内置函数和属性 二.Python中的socket通信逻辑   Socket又 ...

  7. TCP与UDP协议,socket套接字编程,通信相关操作

    文章目录 TCP与UDP协议 TCP协议 ==三次握手== ==四次挥手== UDP协议 TCP与UDP的区别 应用层 socket套接字 代码优化 循环通信 半连接池 粘包问题 TCP与UDP协议 ...

  8. 网络七层协议 五层模型 TCP连接 HTTP连接 socket套接字

    socket(套接字)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程 ...

  9. TCP Socket套接字编程 附源码

    TCP 最主要的特点 TCP 是面向连接的运输层协议.应用程序在使用 TCP 协议之前,必须先建立 TCP 连接.在传送数据完毕后,必须释放已经建立的 TCP 连接 每一条 TCP 连接只能有两个端点 ...

最新文章

  1. Verilog语言设计增加延时的正确方法
  2. matlab 画图比例缩小图片大小,Matlab 画图字体,字号的设定,图片大小和比例
  3. 乔布斯1秒变“白痴”,马化腾5秒,张小龙10秒,你呢?
  4. 【软件工程】知识点梳理(全)
  5. 命令行刷新Magento索引管理
  6. [kubernetes] Schedule --- Node调度与隔离
  7. ajax status php,解决laravel 出现ajax请求419(unknown status)的问题
  8. CentOS64位下python2.6升级到2.7的详细教程
  9. mysql 横纵表转化_数据库MySQL横纵表相互转化操作实现方法
  10. [转载]JXTA技术与原型实现简介
  11. 降本增效促提升---豪越创新企业后勤管理模式
  12. 三菱FX3U 485ADP与东元TECO变频器N310通讯实战程序
  13. 80题题目+AC代码汇总 ~ 南阳 NYOJ
  14. Java Object类讲解 上帝类及其一些方法的分析
  15. centos linux开始防火墙
  16. android 多个按键精灵,给大家分享一个,按键精灵安卓版,找多图, 以及找多图返回多个坐标的,相信大家绝对用得到 _ 按键精灵手机版 - 按键精灵论坛...
  17. html五角星代码,五角星评分系统.html
  18. 沟通的技巧--爱因斯坦如何向老太太解释相对论
  19. 推荐一个单干网赚好站!BUXJOB - 健康程序员,至尚生活!
  20. Android R 11 后台定位权限没有 始终允许选项的解决方法

热门文章

  1. 20道常见的Java面试题,你一定有遇到过
  2. ubuntu14.04下安装postfix+cyrus-sasl经验
  3. javascript中的some方法
  4. [someip专题]vsomeip代码解析2
  5. xtrabackup备份报错:Error : failed to fetch query result SELECT server _ uuid , local , replication
  6. 【信息学】【2018.02】噪声环境下基于时频域信号模型的语音去混响
  7. 测试Leader接手新业务
  8. 玻璃钢 硅胶 石膏模具制作 大理石 花岗岩 树脂 仿玉背景墙 人造砂岩技术视频教程
  9. Android毕业设计必备工具(时序图,类图一键生成)
  10. axure如何实现跳转_Axure中继器实现APP中设置页面的各种列表