物联网协议Coap之C#基于Mozi的CoapServer实现解析

目录

前言

一、C#的Coap Server实现

1、CoapServer相关类

2、主要类解析

3、资源控制器定义 

4、ResourceManager管理器

二、CoapServer生命周期

1、Server创建代码

2、服务端创建

 3、绑定endpoint

4、准备接收请求

总结 


前言

        在之前的关于物联网协议的介绍中,我们详细介绍了如何基于Java进行Coap协议的开发,由于一些项目原因,在项目中采用的不是Java的技术栈,而是ASP.NET Core,因此需要基于C#进行Coap协议的开发与实现。Coap本身是与编程语言无关的,不仅可以用Java进行实现,当然也可以使用C#,还可以是Python、GO等多语言,感兴趣的朋友可以自行问度娘,其实每一种语言都有相应的Coap实现。以下是weixin_34026276博主分享的博客,大家可以自己熟悉的编程语言所进行的开源实现,Coap各编程语言实现传送门。非常感谢他的认真整理。

名称开发语言CoAP版本客户端/服务端实现的CoAP特征开源协议项目链接地址
CaliforniumJavaRFC 7252Client + ServerObserve, Blockwise Transfers, DTLSEPL+EDLEclipse Californium™
cantcoapC++/CRFC 7252Client + ServerBSDhttps://github.com/staropram/cantcoap
CoAP implementation for GoGoRFC 7252Client + ServerCore + Draft SubscribeMIThttps://github.com/dustin/go-coap
CoAP.NETC#RFC 7252, coap-13, coap-08, coap-03Client + ServerCore, Observe, Blockwise Transfers3-clause BSDhttps://github.com/smeshlink/CoAP.NET
CoAPSharpC#, .NETRFC 7252Client + ServerCore, Observe, Block, RDLGPLhttp://www.coapsharp.com
CoAPthonPythonRFC 7252Client + Server + Forward Proxy + Reverse ProxyObserve, Multicast server discovery, CoRE Link Format parsing, Block-wiseMIThttps://github.com/Tanganelli/CoAPthon
CopperJavaScript (Browser Plugin)RFC 7252ClientObserve, Blockwise Transfers3-clause BSDhttps://github.com/mkovatsc/Copperhttps://addons.mozilla.org/de/firefox/addon/copper-270430/
eCoAPCRFC 7252Client + ServerCoreMITjosé bollo / ecoap · GitLab
Erbium for ContikiCRFC 7252Client + ServerObserve, Blockwise Transfers3-clause BSDContiki: The Open Source Operating System for the Internet of Things (er-rest-example)
ETRI CoAPCRFC 7252Client + ServerCore, Observe, BlockCommercialhttp://coap.or.kr/index_en.html
iCoAPObjective-CRFC 7252ClientCore, Observe, Blockwise TransfersMIThttps://github.com/stuffrabbit/iCoAP
jCoAPJavaRFC 7252Client + ServerObserve, Blockwise TransfersApache License 2.0https://code.google.com/p/jcoap/
libcoapCRFC 7252Client + ServerObserve, Blockwise TransfersBSD/GPLlibcoap: C-Implementation of CoAP download | SourceForge.net
microcoapCRFC 7252Client + ServerMIThttps://github.com/1248/microcoap
nCoapJavaRFC 7252Client + ServerObserveBSDhttps://github.com/okleine/nCoAP
node-coapJavascriptRFC 7252Client + ServerCore, Observe, BlockMIThttps://github.com/mcollina/node-coap
Ruby coapRubyRFC 7252Client + Server (david)Core, Observe, Block, RDMIT, GPLhttps://github.com/nning/coap
https://github.com/nning/david
Sensinode C Device LibraryCRFC 7252Client + ServerCore, Observe, Block, RDCommercialDownloads - Arm Developer
Sensinode Java Device LibraryJava SERFC 7252Client + ServerCore, Observe, Block, RDCommercialDownloads - Arm Developer
Sensinode NanoService PlatformJava SERFC 7252Cloud ServerCore, Observe, Block, RDCommercialDownloads - Arm Developer
SMCPCRFC 7252Client + ServerCore, Observe, BlockMIThttps://github.com/darconeous/smcp
SwiftCoAPSwiftRFC 7252Client + ServerCore, Observe, Blockwise TransfersMIThttps://github.com/stuffrabbit/SwiftCoAP
TinyOS CoapBlipnesC/Ccoap-13Client + ServerObserve, Blockwise TransfersBSDhttp://docs.tinyos.net/tinywiki/index.php/CoAP
txThingsPython (Twisted)RFC 7252Client + ServerBlockwise Transfers, Observe (partial)MIThttps://github.com/siskin/txThings/

        由于对ASP.NET Core并不是很熟悉,在进行基础入门编程学习之后后,我们基于Mozi开源框架进行扩展扩展实现,这是gitee上mozi项目地址。原本的项目包含的内容比较多,我们可以在它的基础之上进行简化,改造成符合自己需求的项目。本文以CoapServer为主线,介绍CoapServer使用C#语言的定义以及后台资源管理类的定义和实现。

一、C#的Coap Server实现

        本节将重点介绍CoapServer在C#中的设计与实现,由于Coap协议在Java的篇章中有所涉及,相信大家对Coap已经不再陌生,因此这里不再对Coap进行赘述。下面依然采用熟悉的OOA即面向对象分析,采用面向对象的方式进行源代码分析。

1、CoapServer相关类

在CoapServer中,在这个工程中,主要涉及的类如下:

序号类名说明
1CoAPServerCoAP的服务端
2CoAPResource类似于Java的中controller
3ResourceManager资源管理器,可以理解成IOC容器

2、主要类解析

        CoapServer是服务端程序中最重要的类,其中主要定义了后端的服务,以及绑定了Coap协议,用于接收前端来自Client的请求。

        从类的继承体系来说,CoapServer是CoAPPeer的子类,有必要对CoAPPeer进行一个全面的说明。 

using System;
using System.Collections.Generic;
namespace Mozi.IoT
{
    /// <summary>
    /// CoAP对等端
    /// </summary>
    public class CoAPPeer
    {
        /// <summary>
        /// 最大数据包尺寸 包含所有头信息和有效荷载 Byte
        /// </summary>
        private int _maxTransferPackSize = 512;
        private int _blockSize = 128;
        private ulong _packetSendCount, _totalSendBytes, _packetReceived = 0, _totalReceivedBytes;
        protected UDPSocketIOCP _socket;
        protected int BindPort = CoAPProtocol.Port;
        /// <summary>
        /// 最小分块大小,单位Byte
        /// </summary>
        public const int MinBlockSize = 16;
        /// <summary>
        /// 最大分块大小,单位Byte
        /// </summary>
        public const int MaxBlockSize = 2048;
        /// <summary>
        /// 当前端默认采用块大小,默认值为128bytes,单位Byte
        /// </summary>
        /// <remarks>在通讯两方没有进行协商的情况下,默认采用此值作为分块大小。取值区间为{<see cref="MinBlockSize"/>~<see cref="MaxBlockSize"/>}</remarks>
        public int BlockSize { get { return _blockSize; } set { _blockSize = value; } }
        /// <summary>s
        /// 受支持的请求方法
        /// </summary>
        protected List<CoAPCode> SupportedRequest = new List<CoAPCode> { CoAPRequestMethod.Get, CoAPRequestMethod.Post, CoAPRequestMethod.Put, CoAPRequestMethod.Delete };
        /// <summary>
        /// 数据包接收事件,字节流数据包
        /// </summary>
        public PackageReceive DatagramReceived;
        /// <summary>
        /// 服务端口
        /// </summary>
        public int Port { get { return BindPort; } protected set { BindPort = value; } }
        /// <summary>
        /// 启动时间
        /// </summary>
        public DateTime StartTime { get; private set; }
        /// <summary>
        /// 服务器运行状态
        /// </summary>
        public bool Running
        {
            get; set;
        }
        /// <summary>
        /// 最大数据包尺寸 包含所有头信息和有效荷载
        /// </summary>
        internal int MaxTransferPackSize { get => _maxTransferPackSize; set => _maxTransferPackSize = value; }
        /// <summary>
        /// 累计接收到的包的数量
        /// </summary>
        public ulong PacketReceivedCount { get => _packetReceived; }
        /// <summary>
        /// 累计接收的字节数
        /// </summary>
        public ulong TotalReceivedBytes { get => _totalReceivedBytes; }
        /// <summary>
        /// 累计发出的包的数量
        /// </summary>
        public ulong PacketSendCount => _packetSendCount;
        /// <summary>                                                               
        /// 累计发出的字节数                                                                
        /// </summary>
        public ulong TotalSendBytes => _totalSendBytes; 

        public CoAPPeer()
        {
            _socket = new UDPSocketIOCP();
            _socket.AfterReceiveEnd += Socket_AfterReceiveEnd;
        }
        /// <summary>
        /// 以指定端口启动<see cref="F:Port"/>,如果不配置端口则使用默认端口
        /// </summary>
        public void Start()
        {
            Start(BindPort);
        }
        /// <summary>
        /// 启动本端服务 默认5683端口
        /// </summary>
        /// <param name="port"></param>
        public void Start(int port)
        {
            BindPort = port;
            _socket.Start(BindPort);
            StartTime = DateTime.Now;
            Running = true;
        }
        /// <summary>
        /// 端口下线
        /// </summary>
        public void Shutdown()
        {
            _socket.Shutdown();
            StartTime = DateTime.MinValue;
            Running = false;
        }
        /// <summary>
        /// 数据接收完成回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <remarks>继承类如果覆盖该事件,则可以接管数据处理</remarks>
        protected virtual void Socket_AfterReceiveEnd(object sender, DataTransferArgs args)
        {
            _packetReceived++;
            _totalReceivedBytes += args.Data != null ? (uint)args.Data.Length : 0;
            if (DatagramReceived != null)
            {
                DatagramReceived(args.IP, args.Port, args.Data);
            }
        }
    }
}

         从以上的代码中可以发现,上述类也是定义了CoAP协议工作所必须要的一些属性,比如端口、协议、数据包内容等等。CoapServer的属性代码如下:

 private Cache.MessageCacheManager _cm;

/// <summary>
/// 接收到请求
/// </summary>
public MessageTransmit RequestReceived;
/// <summary>
/// 发起响应请求
/// </summary>
public MessageTransmit Responsed;
private bool _proxyPassed = false;
private uint maxBodySize=20*1024*1024;
/// <summary>
/// 服务端能处理的最大POST资源大小 单位byte
/// </summary>
public uint MaxBodySize { get => maxBodySize; set => maxBodySize = value; }
/// <summary>
/// 服务器根目录
/// </summary>
public string Root = AppDomain.CurrentDomain.BaseDirectory;

3、资源控制器定义 

每一个后台都会对应一个资源控制器,这里也不例外,我们来看下C#的实现。在CoapResource中同样的定义了get、post、put、delete四种请求方法。如下图所示:

/// <summary>
    /// CoAP资源
    /// </summary>
    public abstract class CoAPResource
    {
        /// <summary>
        /// 资源总大小
        /// </summary>
        public abstract uint ResourceSize { get; }
        /// <summary>
        /// 默认分块大小128,单位Bytes 
        /// </summary>
        /// <remarks>
        /// 如果资源尺寸过大,则必须合理配置此大小。
        /// 取值范围为16-2048Bytes BlockOptionValue中Size的数据容量。参考<see cref="BlockOptionValue"/>
        /// </remarks>
        public virtual uint BlockSize { get { return 128; } }
        /// <summary>
        /// GET方法
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        public virtual CoAPPackage OnGet(CoAPContext ctx)
        {
            ctx.Response = new CoAPPackage { MessageType = CoAPMessageType.Acknowledgement, MesssageId = ctx.Request.MesssageId, Token = ctx.Request.Token, Code = CoAPResponseCode.Forbidden };
            return ctx.Response;
        }
        /// <summary>
        /// Post方法
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        public virtual CoAPPackage OnPost(CoAPContext ctx)
        {
            ctx.Response = new CoAPPackage { MessageType = CoAPMessageType.Acknowledgement, MesssageId = ctx.Request.MesssageId, Token = ctx.Request.Token, Code = CoAPResponseCode.Forbidden };
            return ctx.Response;
        }
        /// <summary>
        /// PUT方法
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        public virtual CoAPPackage OnPut(CoAPContext ctx)
        {
            ctx.Response = new CoAPPackage { MessageType = CoAPMessageType.Acknowledgement, MesssageId = ctx.Request.MesssageId, Token = ctx.Request.Token, Code = CoAPResponseCode.Forbidden };
            return ctx.Response;
        }
        /// <summary>
        /// Delete方法
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        public virtual CoAPPackage OnDelete(CoAPContext ctx)
        {
            ctx.Response = new CoAPPackage { MessageType = CoAPMessageType.Acknowledgement, MesssageId = ctx.Request.MesssageId, Token = ctx.Request.Token, Code = CoAPResponseCode.Forbidden };
            return ctx.Response;
        }
        /// <summary>
        /// 分块查找
        /// </summary>
        /// <param name="indBlock"></param>
        /// <param name="blockSize"></param>
        /// <returns></returns>
        protected virtual byte[] Seek(int indBlock, int blockSize)
        {
            return new byte[] { };
        }
        /// <summary>
        /// Block2分块协商
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        internal virtual void HandleBlock2Query(CoAPContext ctx)
        {
            CoAPOption opt = ctx.Request.Options.Find(x => x.Option == CoAPOptionDefine.Block2);
            if (opt != null)
            {
                OptionValue opt2 = new BlockOptionValue() { Pack = opt.Value.Pack };
                //if(opt2)
            }
        }
        /// <summary>
        /// 请求服务端资源大小,响应条件为 Get Size2=0
        /// </summary>
        /// <param name="ctx">响应上下文对象</param>
        /// <returns></returns>
        internal virtual bool HandleSize2Query(CoAPContext ctx)
        {
            CoAPOption opt = ctx.Request.Options.Find(x => x.Option == CoAPOptionDefine.Size2);
            if (opt != null && int.Parse(opt.Value.ToString()) ==0 && ctx.Request.Code == CoAPRequestMethod.Get)
            {

                ctx.Response = new CoAPPackage { MessageType = CoAPMessageType.Acknowledgement, MesssageId = ctx.Request.MesssageId, Token = ctx.Request.Token, Code = CoAPResponseCode.Content };

                CoAPOption optResp = new CoAPOption() { Option = CoAPOptionDefine.Size2, Value = new UnsignedIntegerOptionValue() { Value = ResourceSize } };
                
                ctx.Response.SetOption(optResp);
                return true;
            }
            else
            {
                return false;
            }
        }
    }

4、ResourceManager管理器

        每个业务接收类都对应一个Resource,而这些Resource必须要使用一个统一的容器管理起来,可以把它理解成Java对应的IOC容器,程序运行时会自动把相关资源管理起来。资源描述如下

 public class ResourceDescriptionAttribute : Attribute
    {
        /// <summary>
        /// 命名空间
        /// </summary>
        public string Namespace { get; set; }
        /// <summary>
        /// 资源名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 文字描述
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// 资源类型
        /// </summary>
        public string ResourceType { get; set; }
    }

        资源管理器的核心管理方式也是采用反射的机制,如下:

二、CoapServer生命周期

        在上面的代码中,对CoapServer的编码实现进行了介绍,下面将采用熟悉的调试方法来进行调用跟踪,在关键代码中进行深度讲解。

1、Server创建代码

        创建Server的代码如下:

static void Main(string[] args)
        {
            Console.WriteLine("你好!COAP服务端已开启,等待客户端连接......");
            //服务端
            CoAPServer cs = new CoAPServer();
            cs.RequestReceived += new MessageTransmit((host, port, pack) =>
            {
                Console.WriteLine($"From:[{host}:{port}]");
                Console.WriteLine(pack.ToString());
                Console.Title = string.Format("elapsed:{2},count:{0},pps:{3},bytes:{1}", cs.PacketReceivedCount, cs.TotalReceivedBytes,FormatSeconds(sp.ElapsedMilliseconds),pps);
            });
            cs.Start();
            Console.ReadLine();
        }

2、服务端创建

        第一步、调用构造方法

        第二步、指定端口启动

 第三步、设置socket,绑定协议

 3、绑定endpoint

4、准备接收请求

 

总结 

        以上就是本文的主要内容,本文以CoapServer为主线,介绍CoapServer使用C#语言的定义以及后台资源管理类的定义和实现。行文仓促,定有不当之处,欢迎各位朋友专家批评指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/332696.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

汽车连接器接线端子和多芯线束连接界面

冷压接的开式压接和闭式压接以及热压接的超声波焊接对汽车连接器接线端子和多芯线束连接界面 连接器接线端子和多芯线束的连接是电子线束行业&#xff0c;特别是汽车行业常用的导线连接方式。汽车整车线束又由许多分支线束组成&#xff0c;而分支线束必须通过连接器实现连接&am…

HBase学习六:LSM树算法

1、简介 HBase是基于LSM树架构实现的,天生适合写多读少的应用场景。 LSM树本质上和B+树一样,是一种磁盘数据的索引结构。但和B+树不同的是,LSM树的索引对写入请求更友好。因为无论是何种写入请求,LSM树都会将写入操作处理为一次顺序写,而HDFS擅长的正是顺序写(且HDFS不…

IMDB电影评论的情感分析——paddle

项目地址&#xff1a;IMDB电影评论的情感分析 - 飞桨AI Studio星河社区 (baidu.com) 1. 实验介绍 1.1 实验目的 理解并掌握循环神经网络的基础知识点&#xff0c;包括模型的时序结构、模型的前向传播、反向传播等掌握长短时记忆网络LSTM和门控循环单元网络GRU的设计原理熟悉如…

移动云助力智慧交通数智化升级

智慧交通是在整个交通运输领域充分利用物联网、空间感知、云计算、移动互联网等新一代信息技术&#xff0c;综合运用交通科学、系统方法、人工智能、知识挖掘等理论与工具&#xff0c;以全面感知、深度融合、主动服务、科学决策为目标&#xff0c;推动交通运输更安全、更高效、…

O(1)插入、删除和随机元素[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 实现RandomizedSet类&#xff1a; 【1】RandomizedSet()初始化RandomizedSet对象。 【2】bool insert(int val)当元素val不存在时&#xff0c;向集合中插入该项&#xff0c;并返回true&#xff1b;否则&#xff0c;返回false。 【3】…

java基本算法

1.链表 链表用来存储数据&#xff0c;由一系列的结点组成。这些结点的物理地址不一定是连续的&#xff0c;即可能连续&#xff0c;也可能不连续&#xff0c;但链表里的结点是有序的。一个结点由数据的值和下一个数据的地址组成。一个链表内的数据类型可以是多种多样的。数组也是…

Debian系统写Mysql时中文出现乱码无法定入的问题解决方案

原因是操作系统可能精简安装&#xff0c;没有GBK字符集&#xff0c;只有UTF8在转换或使用的时候有问题。 使用locale -a查看系统支持的字符集。正常的比较全的字符集的操作系统如下&#xff1a; 有问题的操作系统字符集如下&#xff1a; 解决方案&#xff1a; 步骤1&#…

protobuf学习日记 | 认识protobuf中的类型

目录 前言 一、标量数据类型 二、protobuf中的 “数组” 三、特殊类型 1、枚举类型 &#xff08;1&#xff09;类型讲解 &#xff08;2&#xff09;升级通讯录 2、Any类型 &#xff08;1&#xff09;类型讲解 &#xff08;2&#xff09;升级通讯录 3、oneof类型 …

【动态规划】【二分查找】【C++算法】730. 统计不同回文子序列

作者推荐 【动态规划】【数学】【C算法】18赛车 涉及知识点 动态规划 二分查找 LeetCode730. 统计不同回文子序列 给你一个字符串 s &#xff0c;返回 s 中不同的非空回文子序列个数 。由于答案可能很大&#xff0c;请返回对 109 7 取余 的结果。 字符串的子序列可以经由…

ubuntu源码安装MySQL

mysql下载路径 创建新数组 mysql sudo groupadd mysql# 创建用户 mysql ,指定属组为 mysql&#xff0c;禁止其登录 # --no-create-home选项&#xff0c;创建用户时不会自动创建主目录 sudo adduser --system --no-create-home --ingroup mysql --shell /sbin/nologin mysql创…

第二证券:大逆转!A股强势反弹,多家机构看好后市

周四&#xff0c;A股强势反弹&#xff0c;沪指2800点合浦还珠&#xff0c;两市成交量达8767亿元&#xff0c;较周三大幅增加2000多亿元。沪深300ETF大幅放量&#xff0c;华泰柏瑞沪深300ETF、嘉实沪深300ETF、易方达沪深300ETF和华夏沪深300ETF等4只沪深300ETF算计成交额超311亿…

常用中间件漏洞

IIS6 IIS7 安装 控制面板-----打开关闭windows功能 添加角色-----添加IIS 启动之后访问localhost 复现 服务器换成IIS7 访问报错 大概就是缺少CGI模块 问题解决 添加php-cgi的路径 添加脚本映射 修改php.ini文件 将 cgi.fix_pathinfo1 然后设置一个图片 访问 在后缀加上/.…

游戏开发要注意这几个问题

游戏开发是一个充满创意和挑战的过程。对于初学者和经验丰富的开发者来说&#xff0c;每个项目都是一个新的学习机会。然而&#xff0c;成功的游戏开发不仅仅是关于编码和设计&#xff1b;它还涉及到细致的规划、测试和市场洞察。以下是在开发游戏时需要特别注意的几个关键方面…

阿里云容器服务助力万兴科技 AIGC 应用加速

作者&#xff1a;子白&#xff08;顾静&#xff09; 2023 年堪称是 AIGC 元年&#xff0c;文生图领域诞生了 Stable Diffusion 项目&#xff0c;文生文领域诞生了 GPT 家族。一时间风起云涌&#xff0c;国内外许多企业投身 AIGC 创新浪潮&#xff0c;各大云厂商紧随其后纷纷推…

ELK 分离式日志

目录 一.ELK组件 ElasticSearch&#xff1a; Kiabana&#xff1a; Logstash&#xff1a; 可以添加的其它组件&#xff1a; ELK 的工作原理&#xff1a; 二.部署ELK 节点都设置Java环境: 每台都可以部署 Elasticsearch 软件&#xff1a; 修改elasticsearch主配置文件&…

Vue以弹窗形式实现导入功能

目录 前言正文 前言 由于个人工作原因&#xff0c;偏全栈&#xff0c;对于前端的总结还有些初出茅庐&#xff0c;后续会进行规整化的总结 对应的前端框架由&#xff1a;【vue】avue-crud表单属性配置&#xff08;表格以及列&#xff09; 最终实现的表单样式如下&#xff1a;…

VSCode 插件推荐

前言 关于开发用的插件就不做赘述了&#xff0c;网上面有很多文章都做了推荐&#xff0c;本文推荐几个好看的插件。 文件图标主题 Vscode icons Material Icon Theme 字体主题 推荐 One Dark Pro 其他 推荐一个生成好看代码的网址 https://carbon.now.sh/

策略模式在工作中的运用

前言 在不同的场景下&#xff0c;执行不同的业务逻辑&#xff0c;在日常工作中是很寻常的事情。比如&#xff0c;订阅系统。在收到阿里云的回调事件、与收到AWS的回调事件&#xff0c;无论是收到的参数&#xff0c;还是执行的逻辑都可能是不同的。为了避免&#xff0c;每次新增…

如何选购一款质量好超声波清洗机呢?质量好超声波清洗机排行榜

想要选择到一款好用的超声波清洗机还是要多做功课&#xff01;现在市面上超声波清洗机品牌可见是非常多的&#xff0c;质量也是参差不齐&#xff0c;大家在选购的时候需要多看参数再下手也不迟的&#xff01;现在大多数的上班族&#xff0c;面临的都是早九晚六的工作&#xff0…

LeetCode 算法 3.无重复字符的最长子串(python版)

1.需求 #给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 #输入: s “pwwkew” #输出: 3 #解释: 因为无重复字符的最长子串是 “wke”&#xff0c;所以其长度为 3。 #请注意&#xff0c;你的答案必须是 子串 的长度&#xff0c;“pwke” 是一个…