From c16c9be4c6c743fb455395bff838649477ecb942 Mon Sep 17 00:00:00 2001 From: liyi Date: Mon, 6 Jan 2025 09:52:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0p2p=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main/lockMian/lockMain/lockMain_page.dart | 43 ++- .../startChart/command/message_command.dart | 28 +- .../constant/payload_type_constant.dart | 9 + .../handle/impl/udp_rbcuInfo_handler.dart | 69 ++++ lib/talk/startChart/handle/other/do_sign.dart | 124 +++++++ .../handle/scp_message_handler_factory.dart | 2 + lib/talk/startChart/p2p/p2p_manage.dart | 21 ++ lib/talk/startChart/proto/rbcu.pb.dart | 278 +++++++++++++++ lib/talk/startChart/proto/rbcu.pbenum.dart | 11 + lib/talk/startChart/proto/rbcu.pbjson.dart | 64 ++++ lib/talk/startChart/proto/rbcu.pbserver.dart | 14 + lib/talk/startChart/proto/rbcu.proto | 39 +++ lib/talk/startChart/start_chart_manage.dart | 326 ++++-------------- .../views/talkView/talk_view_page.dart | 19 +- pubspec.yaml | 1 + 15 files changed, 768 insertions(+), 280 deletions(-) create mode 100644 lib/talk/startChart/handle/impl/udp_rbcuInfo_handler.dart create mode 100644 lib/talk/startChart/handle/other/do_sign.dart create mode 100644 lib/talk/startChart/p2p/p2p_manage.dart create mode 100644 lib/talk/startChart/proto/rbcu.pb.dart create mode 100644 lib/talk/startChart/proto/rbcu.pbenum.dart create mode 100644 lib/talk/startChart/proto/rbcu.pbjson.dart create mode 100644 lib/talk/startChart/proto/rbcu.pbserver.dart create mode 100644 lib/talk/startChart/proto/rbcu.proto diff --git a/lib/main/lockMian/lockMain/lockMain_page.dart b/lib/main/lockMian/lockMain/lockMain_page.dart index ebcdd7ff..2390d0c3 100755 --- a/lib/main/lockMian/lockMain/lockMain_page.dart +++ b/lib/main/lockMian/lockMain/lockMain_page.dart @@ -230,24 +230,31 @@ class _StarLockMainPageState extends State height: 160.h, ), // _buildImage(), - // SubmitBtn( - // btnName: '发送回声测试'.tr, - // onClick: () async { - // String assetPath = 'assets/test.jpg'; // 替换为你的图片路径 - // List imageBytes = await loadAssetImageAsBytes(assetPath); - // // String testStr = - // // '---天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章爱育黎首臣伏戎羌遐迩体率宾归王鳴鳳翔兮神靈應和炫耀光芒照灼穹蒼迤邐岷峨嶢峻崢巍懿承緒劬勞育秩延佚賡孳蕃鑑識祗崇乾坤纘宵旰卿芮詒朔皎琲犂檄圮黯馭燎竈硎洮蠟胝攢囤嶇葺錡堯羲冕箕翬騏闡燮鸞蓊枘橐榘稟泗沂涖浹涔浣淇漱瀋潢瀾沚澮澆潦瀦瀌瀹灧炅焯燠燋爨熠焜煆烜燴炻熛燧牝牯犛犴牖牒疰疴痀癜痱癃皞皎盂盰盱盻眚眵瞋瞑瞷矙矧矬矰矞砟砉砥砣硇磑磲硤硭礌礞礡祀祚祜祧祼祺禋禡秭秣稃稞稹稷穈穄窀窆窠窸笄筇筌筅筮箦篑篯簟籀籝糅糗紵紽紾綃綈綬綣緜縞縰縻縴繢繇繙罟罾羝羶羸羼翊翕翥翡翳翽耖耜耠耱耦耧耩耨耬耵耶胂胼胬脘腽膦臢臬臾舂舄舡舸艋艏艟艤艚艨艪艭艴芏芊苣苴苕茌茱荄荃莛莪莶菰萁菸菽萸葶蒯蓍蓐蓬蓼蔌蔪蕈蕖蕙蕺蕻薷藦藨藭蘅蘧蘼虍虔虬虮虰虺虻蚨蚋蚱蛏蛘蜊蜍蜉蜣蜥蜩蜴蜱蜮蜾蝣蝤蝥螓螯螨蟒蟑蟛蠊蠋蠛蠡蠹衄衒衢衾袢袷裎裥裨裾褊褙褚褡褰褶襁襦襻觇觋觖觫觿訇訑訾詑詈詟詹誊誨誥誦誨諉諛謏謦譊譖譟譬譯譴讴讵讷诐诪诮诰诳诶谂谄谌谏谑谟谡谥谧谮谯谳谵豇豉豕豚豳豸貂貊貔賑賚賡贐赍赑赗赪赭赳趑趔趱趿跂跏跎跖跗跣跹跽踆踔踝踟踬踮踯蹀蹅蹇蹉蹐蹙蹦蹩躅躐躔躜躞軎軑軔軛軫軬軺輀輅輇輈輐輗輢輦輭輶轋轘轜辀辂辒辚辩迓迕迤迨迮逄逋逑逖逯遄遘遑遴遽邂邈邋邙邡邴邳邶郅郇郛郡郾鄌鄑鄘鄜鄞鄢鄣鄯鄹酃酆酈酖酗酘酢酤酴酹酽酾酾醅醊醑醚醢醪醭醮醯醵醴醶釃釅釐釜釡釴釸釾鉅鈦鈍鈹鈾鈿鉦鉬鉮鉻鉿銎銋銖銩銫銮銲鋈鋋鋌鋮鋯鋹鋻錎錡錣錤鍉鍐鍬鍱鍾鎏鎑鎒鎰鎵鎸鎿鏊鏖鏞鏟鏸鏹鏺鏽鐃鐋鐔鐛鐠鐣鐦鐭鐮鐯鐳鐴鐵鐼鐿鑌鑒鑔鑕鑞鑢鑬鑰鑱鑲鑴鑽鑾鑿钁钆钇钋钍钏钔钗钜钯钴钷钹钺钼钽钿铄铈铊铍铐铖铗铘铙铚铠铨铪铬铮铰铹铼铿锃锆锊锍锎锏锒锓锔锖锗锘锞锟锢锩锫锬锭锯锴锶锷锸锺锼锾镂镄镅镆镉镎镏镒镔镖镙镛镞镡镢镤镩镪镫镬镭镮镯镰镱镲镳镴镶镸长門閃閔閘閡闅闈闑闒闓闔闛闞闠闤闥闦闬闿阂阃阄阆阇阘阛阝队阡阯阱阪阽陀陂陉陔陘陞陟陧陬陲陴陶隃隋隍隒隓隗隦隰隱隳隵隶隽隿雎雋雐雚雝雟雤雩雯雱雿霈霅霌霎霏霗霙霛霝霡霣霤霧靂靅靆靉靎靏靐靕靗靛靡靺靻靽鞀鞉鞎鞑鞔鞖鞚鞝鞡鞤鞧鞨鞫鞬鞮鞯鞳鞴韁韃韈韉韋韐韒韓韗韙韝韟韢韡韣韦韧韨韩韪韫韬韭韮韯韲音頍頏頔頖頞頟頤頦頬頰頲頴頵頹頽頿顃顅顈顊顋顒顕顗顛顝顟顤顪顫顬顮顰顱顲顳顴页顶顷顸颃预颋颌颎颏颐频颓颔颕颙颖颛颟颡颢颣颤颥颦颧風颮颯颰颱颳颶颷颸颹颻颼颾飁飆飇飉飋飏飐飔飗飘飙飚飛飜飝飛飜飝飛飜飝飛飜飝雊霺霿靁靟鞲韞韭頫顴颿飌飡馺駃騑騜髐鬃鰩鳯鷊黴在古老的东方有一座美丽的城市名为锦绣锦绣城四季如春风景秀丽是人们心中的理想之地城中有一条清澈见底的小河河水潺潺流过滋养着两岸的花草树木河边有一座古老的石桥桥上雕刻着精美的图案见证了无数过往行人锦绣城的居民勤劳善良他们日出而作日落而息过着简单而幸福的生活城中有一位智者名叫慧心慧心先生博学多才深受人们的尊敬他常常在河边的亭子里给孩子们讲述历史故事传授知识和智慧孩子们围坐在他身边听得津津有味仿佛置身于一个个奇妙的世界在锦绣城的北边有一片茂密的森林森林里生活着各种各样的动物有活泼可爱的猴子它们在树上跳跃嬉戏有威武雄壮的老虎它们在林间巡视守护着森林的安宁还有许多不知名的鸟儿它们在枝头欢快地歌唱为森林增添了无限生机一天慧心先生带着几个学生走进森林进行一次生动的自然课他们观察着各种植物的生长了解它们的特性聆听鸟儿的鸣叫感受大自然的和谐学生们兴奋不已他们发现大自然是一个神奇的宝库蕴藏着无尽的奥秘在森林的深处有一片神秘的湖泊湖水碧绿如玉清澈见底传说中湖中住着一位美丽的水仙子她用神奇的力量保护着湖泊和周围的生灵慧心先生告诉学生们要爱护大自然与万物和谐共处才能得到大自然的馈赠锦绣城的居民们深知这一点他们珍惜每一滴水每一寸土地努力保护着这片美丽的家园他们相信只要人与自然和谐相处锦绣城将永远充满生机与活力锦绣城外群山环绕山间云雾缭绕宛如仙境山中有一处幽静的山谷谷中开满了五颜六色的花朵香气扑鼻引得蝴蝶翩翩起舞山谷中还有一条蜿蜒的小径两旁长满了奇花异草仿佛是一条通往神秘世界的通道慧心先生的学生们在一次探险中偶然发现了这个山谷他们惊叹于大自然的鬼斧神工纷纷拿出画笔将这美丽的景色描绘下来他们还发现了一种奇特的植物它的叶子呈现出透明的蓝色在阳光下闪闪发光仿佛蕴含着神秘的力量慧心先生告诉他们这种植物名为“梦幻草”只生长在特定的环境中非常稀有在锦绣城的东边有一片广阔的草原草原上绿草如茵牛羊成群草原上还有一座古老的风车风车的叶片随风旋转发出“吱呀吱呀”的声响仿佛在诉说着岁月的故事草原上的人们以放牧为生他们骑着骏马驰骋在广阔的天地间享受着自由自在的生活一天慧心先生带领学生们来到草原给他们上了一堂生动的地理课他指着远处的山脉告诉学生们山脉的形成过程以及它们对气候和生态环境的影响学生们听得入迷仿佛置身于大自然的怀抱中感受到了大自然的壮丽与神奇在锦绣城的南边有一片茂密的竹林竹林里竹子挺拔绿意盎然竹林中有一条清澈的小溪溪水潺潺流淌溪边长满了青苔竹林里还有一座精致的小亭子亭子的柱子上刻着优美的诗句让人感受到一种宁静与雅致慧心先生常常在竹林中的小亭子里静坐思考人生的哲理他告诉学生们竹子虽然柔弱但却有着坚韧不拔的精神无论遇到多大的困难都能顽强地生长学生们深受启发明白了在人生的道路上要像竹子一样勇敢地面对挑战坚持不懈地追求自己的理想锦绣城的居民们还擅长制作各种手工艺品他们的作品精美绝伦远近闻名有巧夺天工的陶瓷色彩斑斓形态各异有细腻精致的刺绣图案生动栩栩如生还有雕刻精美的木雕栩栩如生令人叹为观止这些手工艺品不仅展现了锦绣城居民的智慧和技艺也成为了他们与外界交流的纽带在锦绣城的中心有一座宏伟的宫殿宫殿的建筑风格独特气势恢宏宫殿的屋顶覆盖着金色的琉璃瓦在阳光下熠熠生辉宫殿的墙壁上雕刻着精美的图案栩栩如生仿佛在诉说着一个个古老的故事宫殿里住着一位英明的君主他以仁爱之心治理国家深受百姓的爱戴慧心先生常常受邀到宫殿中为君主出谋划策帮助他解决国家大事君主也非常尊重慧心先生的智慧常常向他请教治国之道在他们的共同努力下锦绣城国泰民安繁荣昌盛锦绣城的居民们还非常重视教育他们相信知识能够改变命运让孩子们拥有更加美好的未来城中有一座古老的书院书院的建筑古朴典雅环境优美书院里有许多博学的老师他们用心地教导学生传授知识和智慧学生们在这里努力学习汲取知识的养分,为将来的发展打下坚实的基础慧心先生也常常到书院授课他用自己的知识和智慧启迪学生们的思维引导他们去探索未知的世界学生们对慧心先生敬仰有加他们知道只有不断学习才能像慧心先生一样拥有渊博的知识和智慧为社会做出贡献锦绣城的故事还有很多这里的人们用自己的勤劳和智慧创造了一个充满生机与活力的美好家园他们相信只要人与自然和谐相处与他人携手共进锦绣城的明天一定会更加美好=='; - // - // // List imageBytes = utf8.encode(testStr).toList(); - // // List imageBytes = utf8.encode(testStr).toList(); - // // state.talkData.value = imageBytes; - // // setState(() {}); - // String toPeerId = - // '2vzXdjdzipJBpWpJxhiRzCFXrDKk54t3YJ7EjYPSRuij'; - // StartChartManage() - // .sendEchoMessage(payload: imageBytes, toPeerId: toPeerId); - // }, - // ), + SubmitBtn( + btnName: '发送地址', + onClick: () async { + StartChartManage().startSendingRbcuInfoMessages(); + // String assetPath = 'assets/test.jpg'; // 替换为你的图片路径 + // List imageBytes = await loadAssetImageAsBytes(assetPath); + // // String testStr = + // // '---天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章爱育黎首臣伏戎羌遐迩体率宾归王鳴鳳翔兮神靈應和炫耀光芒照灼穹蒼迤邐岷峨嶢峻崢巍懿承緒劬勞育秩延佚賡孳蕃鑑識祗崇乾坤纘宵旰卿芮詒朔皎琲犂檄圮黯馭燎竈硎洮蠟胝攢囤嶇葺錡堯羲冕箕翬騏闡燮鸞蓊枘橐榘稟泗沂涖浹涔浣淇漱瀋潢瀾沚澮澆潦瀦瀌瀹灧炅焯燠燋爨熠焜煆烜燴炻熛燧牝牯犛犴牖牒疰疴痀癜痱癃皞皎盂盰盱盻眚眵瞋瞑瞷矙矧矬矰矞砟砉砥砣硇磑磲硤硭礌礞礡祀祚祜祧祼祺禋禡秭秣稃稞稹稷穈穄窀窆窠窸笄筇筌筅筮箦篑篯簟籀籝糅糗紵紽紾綃綈綬綣緜縞縰縻縴繢繇繙罟罾羝羶羸羼翊翕翥翡翳翽耖耜耠耱耦耧耩耨耬耵耶胂胼胬脘腽膦臢臬臾舂舄舡舸艋艏艟艤艚艨艪艭艴芏芊苣苴苕茌茱荄荃莛莪莶菰萁菸菽萸葶蒯蓍蓐蓬蓼蔌蔪蕈蕖蕙蕺蕻薷藦藨藭蘅蘧蘼虍虔虬虮虰虺虻蚨蚋蚱蛏蛘蜊蜍蜉蜣蜥蜩蜴蜱蜮蜾蝣蝤蝥螓螯螨蟒蟑蟛蠊蠋蠛蠡蠹衄衒衢衾袢袷裎裥裨裾褊褙褚褡褰褶襁襦襻觇觋觖觫觿訇訑訾詑詈詟詹誊誨誥誦誨諉諛謏謦譊譖譟譬譯譴讴讵讷诐诪诮诰诳诶谂谄谌谏谑谟谡谥谧谮谯谳谵豇豉豕豚豳豸貂貊貔賑賚賡贐赍赑赗赪赭赳趑趔趱趿跂跏跎跖跗跣跹跽踆踔踝踟踬踮踯蹀蹅蹇蹉蹐蹙蹦蹩躅躐躔躜躞軎軑軔軛軫軬軺輀輅輇輈輐輗輢輦輭輶轋轘轜辀辂辒辚辩迓迕迤迨迮逄逋逑逖逯遄遘遑遴遽邂邈邋邙邡邴邳邶郅郇郛郡郾鄌鄑鄘鄜鄞鄢鄣鄯鄹酃酆酈酖酗酘酢酤酴酹酽酾酾醅醊醑醚醢醪醭醮醯醵醴醶釃釅釐釜釡釴釸釾鉅鈦鈍鈹鈾鈿鉦鉬鉮鉻鉿銎銋銖銩銫銮銲鋈鋋鋌鋮鋯鋹鋻錎錡錣錤鍉鍐鍬鍱鍾鎏鎑鎒鎰鎵鎸鎿鏊鏖鏞鏟鏸鏹鏺鏽鐃鐋鐔鐛鐠鐣鐦鐭鐮鐯鐳鐴鐵鐼鐿鑌鑒鑔鑕鑞鑢鑬鑰鑱鑲鑴鑽鑾鑿钁钆钇钋钍钏钔钗钜钯钴钷钹钺钼钽钿铄铈铊铍铐铖铗铘铙铚铠铨铪铬铮铰铹铼铿锃锆锊锍锎锏锒锓锔锖锗锘锞锟锢锩锫锬锭锯锴锶锷锸锺锼锾镂镄镅镆镉镎镏镒镔镖镙镛镞镡镢镤镩镪镫镬镭镮镯镰镱镲镳镴镶镸长門閃閔閘閡闅闈闑闒闓闔闛闞闠闤闥闦闬闿阂阃阄阆阇阘阛阝队阡阯阱阪阽陀陂陉陔陘陞陟陧陬陲陴陶隃隋隍隒隓隗隦隰隱隳隵隶隽隿雎雋雐雚雝雟雤雩雯雱雿霈霅霌霎霏霗霙霛霝霡霣霤霧靂靅靆靉靎靏靐靕靗靛靡靺靻靽鞀鞉鞎鞑鞔鞖鞚鞝鞡鞤鞧鞨鞫鞬鞮鞯鞳鞴韁韃韈韉韋韐韒韓韗韙韝韟韢韡韣韦韧韨韩韪韫韬韭韮韯韲音頍頏頔頖頞頟頤頦頬頰頲頴頵頹頽頿顃顅顈顊顋顒顕顗顛顝顟顤顪顫顬顮顰顱顲顳顴页顶顷顸颃预颋颌颎颏颐频颓颔颕颙颖颛颟颡颢颣颤颥颦颧風颮颯颰颱颳颶颷颸颹颻颼颾飁飆飇飉飋飏飐飔飗飘飙飚飛飜飝飛飜飝飛飜飝飛飜飝雊霺霿靁靟鞲韞韭頫顴颿飌飡馺駃騑騜髐鬃鰩鳯鷊黴在古老的东方有一座美丽的城市名为锦绣锦绣城四季如春风景秀丽是人们心中的理想之地城中有一条清澈见底的小河河水潺潺流过滋养着两岸的花草树木河边有一座古老的石桥桥上雕刻着精美的图案见证了无数过往行人锦绣城的居民勤劳善良他们日出而作日落而息过着简单而幸福的生活城中有一位智者名叫慧心慧心先生博学多才深受人们的尊敬他常常在河边的亭子里给孩子们讲述历史故事传授知识和智慧孩子们围坐在他身边听得津津有味仿佛置身于一个个奇妙的世界在锦绣城的北边有一片茂密的森林森林里生活着各种各样的动物有活泼可爱的猴子它们在树上跳跃嬉戏有威武雄壮的老虎它们在林间巡视守护着森林的安宁还有许多不知名的鸟儿它们在枝头欢快地歌唱为森林增添了无限生机一天慧心先生带着几个学生走进森林进行一次生动的自然课他们观察着各种植物的生长了解它们的特性聆听鸟儿的鸣叫感受大自然的和谐学生们兴奋不已他们发现大自然是一个神奇的宝库蕴藏着无尽的奥秘在森林的深处有一片神秘的湖泊湖水碧绿如玉清澈见底传说中湖中住着一位美丽的水仙子她用神奇的力量保护着湖泊和周围的生灵慧心先生告诉学生们要爱护大自然与万物和谐共处才能得到大自然的馈赠锦绣城的居民们深知这一点他们珍惜每一滴水每一寸土地努力保护着这片美丽的家园他们相信只要人与自然和谐相处锦绣城将永远充满生机与活力锦绣城外群山环绕山间云雾缭绕宛如仙境山中有一处幽静的山谷谷中开满了五颜六色的花朵香气扑鼻引得蝴蝶翩翩起舞山谷中还有一条蜿蜒的小径两旁长满了奇花异草仿佛是一条通往神秘世界的通道慧心先生的学生们在一次探险中偶然发现了这个山谷他们惊叹于大自然的鬼斧神工纷纷拿出画笔将这美丽的景色描绘下来他们还发现了一种奇特的植物它的叶子呈现出透明的蓝色在阳光下闪闪发光仿佛蕴含着神秘的力量慧心先生告诉他们这种植物名为“梦幻草”只生长在特定的环境中非常稀有在锦绣城的东边有一片广阔的草原草原上绿草如茵牛羊成群草原上还有一座古老的风车风车的叶片随风旋转发出“吱呀吱呀”的声响仿佛在诉说着岁月的故事草原上的人们以放牧为生他们骑着骏马驰骋在广阔的天地间享受着自由自在的生活一天慧心先生带领学生们来到草原给他们上了一堂生动的地理课他指着远处的山脉告诉学生们山脉的形成过程以及它们对气候和生态环境的影响学生们听得入迷仿佛置身于大自然的怀抱中感受到了大自然的壮丽与神奇在锦绣城的南边有一片茂密的竹林竹林里竹子挺拔绿意盎然竹林中有一条清澈的小溪溪水潺潺流淌溪边长满了青苔竹林里还有一座精致的小亭子亭子的柱子上刻着优美的诗句让人感受到一种宁静与雅致慧心先生常常在竹林中的小亭子里静坐思考人生的哲理他告诉学生们竹子虽然柔弱但却有着坚韧不拔的精神无论遇到多大的困难都能顽强地生长学生们深受启发明白了在人生的道路上要像竹子一样勇敢地面对挑战坚持不懈地追求自己的理想锦绣城的居民们还擅长制作各种手工艺品他们的作品精美绝伦远近闻名有巧夺天工的陶瓷色彩斑斓形态各异有细腻精致的刺绣图案生动栩栩如生还有雕刻精美的木雕栩栩如生令人叹为观止这些手工艺品不仅展现了锦绣城居民的智慧和技艺也成为了他们与外界交流的纽带在锦绣城的中心有一座宏伟的宫殿宫殿的建筑风格独特气势恢宏宫殿的屋顶覆盖着金色的琉璃瓦在阳光下熠熠生辉宫殿的墙壁上雕刻着精美的图案栩栩如生仿佛在诉说着一个个古老的故事宫殿里住着一位英明的君主他以仁爱之心治理国家深受百姓的爱戴慧心先生常常受邀到宫殿中为君主出谋划策帮助他解决国家大事君主也非常尊重慧心先生的智慧常常向他请教治国之道在他们的共同努力下锦绣城国泰民安繁荣昌盛锦绣城的居民们还非常重视教育他们相信知识能够改变命运让孩子们拥有更加美好的未来城中有一座古老的书院书院的建筑古朴典雅环境优美书院里有许多博学的老师他们用心地教导学生传授知识和智慧学生们在这里努力学习汲取知识的养分,为将来的发展打下坚实的基础慧心先生也常常到书院授课他用自己的知识和智慧启迪学生们的思维引导他们去探索未知的世界学生们对慧心先生敬仰有加他们知道只有不断学习才能像慧心先生一样拥有渊博的知识和智慧为社会做出贡献锦绣城的故事还有很多这里的人们用自己的勤劳和智慧创造了一个充满生机与活力的美好家园他们相信只要人与自然和谐相处与他人携手共进锦绣城的明天一定会更加美好=='; + // + // // List imageBytes = utf8.encode(testStr).toList(); + // // List imageBytes = utf8.encode(testStr).toList(); + // // state.talkData.value = imageBytes; + // // setState(() {}); + // String toPeerId = + // '2vzXdjdzipJBpWpJxhiRzCFXrDKk54t3YJ7EjYPSRuij'; + // StartChartManage() + // .sendEchoMessage(payload: imageBytes, toPeerId: toPeerId); + }, + ), + SubmitBtn( + btnName: '停止', + onClick: () async { + StartChartManage().stopSendingRbcuInfoMessages(); + }, + ), if (F.isLite) Container() else diff --git a/lib/talk/startChart/command/message_command.dart b/lib/talk/startChart/command/message_command.dart index 82b8ade6..784e3f67 100644 --- a/lib/talk/startChart/command/message_command.dart +++ b/lib/talk/startChart/command/message_command.dart @@ -8,8 +8,8 @@ import 'package:star_lock/talk/startChart/constant/udp_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_accept.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_hangup.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_ping.pb.dart'; @@ -375,6 +375,32 @@ class MessageCommand { return _hexToBytes(serializedBytesString); } + // RbcuInfo 地址交换消息 + static List genericRbcuInfoMessage({ + required String FromPeerId, + required String ToPeerId, + required RbcuInfo rbcuInfo, + int? MessageId, + String? errorMessageText, + }) { + final payload = rbcuInfo.writeToBuffer(); + ScpMessage message = ScpMessage( + ProtocolFlag: ProtocolFlagConstant.scp01, + MessageType: MessageTypeConstant.Resp, + MessageId: MessageId, + SpTotal: 1, + SpIndex: 1, + FromPeerId: FromPeerId, + ToPeerId: ToPeerId, + Payload: payload, + PayloadCRC: calculationCrc(payload), + PayloadLength: payload.length, + PayloadType: PayloadTypeConstant.RbcuInfo, + ); + String serializedBytesString = message.serialize(); + return _hexToBytes(serializedBytesString); + } + // 辅助方法:将16进制字符串转换为字节列表 static List _hexToBytes(String hex) { final bytes = []; diff --git a/lib/talk/startChart/constant/payload_type_constant.dart b/lib/talk/startChart/constant/payload_type_constant.dart index 8987e967..b6bf874e 100644 --- a/lib/talk/startChart/constant/payload_type_constant.dart +++ b/lib/talk/startChart/constant/payload_type_constant.dart @@ -55,4 +55,13 @@ class PayloadTypeConstant { // 通话中挂断 static const int talkHangup = 1668; + + // Rbcu地址交换 + static const int RbcuInfo = 150; + + // Rbcu探测 + static const int RbcuProbe = 152; + + // Rbcu确认 + static const int RbcuConfirm = 154; } diff --git a/lib/talk/startChart/handle/impl/udp_rbcuInfo_handler.dart b/lib/talk/startChart/handle/impl/udp_rbcuInfo_handler.dart new file mode 100644 index 00000000..4d681588 --- /dev/null +++ b/lib/talk/startChart/handle/impl/udp_rbcuInfo_handler.dart @@ -0,0 +1,69 @@ +import 'package:star_lock/login/selectCountryRegion/common/index.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; +import 'package:star_lock/talk/startChart/entity/scp_message.dart'; +import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; +import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; +import 'package:star_lock/talk/startChart/p2p/p2p_manage.dart'; +import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; + +class UdpRbcuInfoHandler extends ScpMessageBaseHandle + implements ScpMessageHandler { + @override + deserializePayload( + {required int payloadType, + required int messageType, + required List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final RbcuInfo rbcuInfo = RbcuInfo(); + rbcuInfo.mergeFromBuffer(byte); + return rbcuInfo; + } else { + String payload = utf8.decode(byte); + return payload; + } + } + + @override + void handleInvalidReq(ScpMessage scpMessage) { + // TODO: implement handleInvalidReq + } + + @override + void handleRealTimeData(ScpMessage scpMessage) { + // TODO: implement handleRealTimeData + } + + @override + void handleReq(ScpMessage scpMessage) { + final RbcuInfo rbcuInfo = scpMessage.Payload(); + if (rbcuInfo.isResp) { + // 如果是回复的消息 + _handleResultRbcuInfo(rbcuInfo); + } + } + + @override + void handleResp(ScpMessage scpMessage) { + final GenericResp genericResp = scpMessage.Payload(); + if (checkGenericRespSuccess(genericResp)) { + // 收到回复之后停止重发 + startChartManage.stopSendingRbcuInfoMessages(); + } + } + + /// 处理回复的rbcuInfo消息 + void _handleResultRbcuInfo(RbcuInfo rbcuInfo) { + P2pManage().communicationObjectRbcuInfo = rbcuInfo; + } +} diff --git a/lib/talk/startChart/handle/other/do_sign.dart b/lib/talk/startChart/handle/other/do_sign.dart new file mode 100644 index 00000000..c02c9aa6 --- /dev/null +++ b/lib/talk/startChart/handle/other/do_sign.dart @@ -0,0 +1,124 @@ +import 'dart:async'; + +import 'dart:typed_data'; +import 'package:convert/convert.dart'; +import 'package:fast_rsa/fast_rsa.dart' as fastRsa; +import 'package:flutter/services.dart'; +import 'package:pointycastle/export.dart' as pc; +import 'dart:convert'; +import 'package:asn1lib/asn1lib.dart' as asn1lib; +import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart'; // Prefix for asn1lib + +class DoSign { + // 生成签名sing + Future generateSign({ + required int currentTimestamp, + required String privateKeyHex, + }) async { + String resultSign = ''; + try { + // 1. 将 32 位时间戳以小端字节序编码为二进制数据 + Uint8List signData = encodeTimestampToLittleEndianBytes(currentTimestamp); + + // 2.将十六进制字符串转换为字节数组 + List privateKeyBytes = hexToBytes(privateKeyHex); + + // 3.将私钥转换为 PEM 格式 + final pemPrivateKey = + convertToPemPrivateKey(privateKeyBytes, isPKCS8: true); + // 4.签名 + var result = await fastRsa.RSA + .signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey); + resultSign = hex.encode(result); + } catch (e) { + throw StartChartMessageException('❌--->上报信息生成签名时出现错误: $e'); + } + return resultSign ?? ''; + } + + // 将 32 位时间戳以小端字节序编码为二进制数据 + Uint8List encodeTimestampToLittleEndianBytes(int timestamp) { + // 创建一个 4 字节的 ByteData 对象 + ByteData byteData = ByteData(4); + + // 将 32 位时间戳写入 ByteData,使用小端字节序 + byteData.setUint32(0, timestamp, Endian.little); + + // 将 ByteData 转换为 Uint8List + Uint8List bytes = byteData.buffer.asUint8List(); + + return bytes; + } + + /// 转换私钥格式 + String convertToPemPrivateKey(List privateKeyBytes, + {bool isPKCS8 = true}) { + // 将字节数组转换为Base64编码的字符串 + String base64PrivateKey = base64Encode(privateKeyBytes); + + // 添加PEM格式的头尾标签 + String pemHeader; + String pemFooter; + + if (isPKCS8) { + pemHeader = "-----BEGIN PRIVATE KEY-----"; + pemFooter = "-----END PRIVATE KEY-----"; + } else { + pemHeader = "-----BEGIN RSA PRIVATE KEY-----"; + pemFooter = "-----END RSA PRIVATE KEY-----"; + } + + // 将Base64字符串分行为每行64个字符 + const lineLength = 64; + List lines = []; // 用于存储每一行 + + for (int i = 0; i < base64PrivateKey.length; i += lineLength) { + int end = (i + lineLength < base64PrivateKey.length) + ? i + lineLength + : base64PrivateKey.length; + lines.add(base64PrivateKey.substring(i, end)); + } + + // 组合成完整的PEM格式字符串 + return "$pemHeader\n${lines.join('\n')}\n$pemFooter"; + } + + /// 自定义 PEM 格式的 RSA 私钥解析器 + pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) { + // 将十六进制字符串转换为字节数组 + final uint8list = Uint8List.fromList(hexToBytes(privateKeyHex)); + try { + // 使用 asn1lib 的 ASN1Parser 解析 + final asn1Parser = asn1lib.ASN1Parser(uint8list); + final topLevelSeq = asn1Parser.nextObject() as asn1lib.ASN1Sequence; + + final modulus = bytesToBigInt( + (topLevelSeq.elements[1] as asn1lib.ASN1Integer).valueBytes()); + final privateExponent = bytesToBigInt( + (topLevelSeq.elements[3] as asn1lib.ASN1Integer).valueBytes()); + final p = bytesToBigInt( + (topLevelSeq.elements[4] as asn1lib.ASN1Integer).valueBytes()); + final q = bytesToBigInt( + (topLevelSeq.elements[5] as asn1lib.ASN1Integer).valueBytes()); + + return pc.RSAPrivateKey(modulus, privateExponent, p, q); + } catch (e) { + // 如果发生解码错误,打印错误信息 + print("Error decoding private key: $e"); + rethrow; + } + } + + // 将十六进制字符串转换为字节数组 + List hexToBytes(String hex) { + return List.generate(hex.length ~/ 2, + (i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16)); + } + + BigInt bytesToBigInt(Uint8List bytes) { + return BigInt.parse( + bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(), + radix: 16, + ); + } +} diff --git a/lib/talk/startChart/handle/scp_message_handler_factory.dart b/lib/talk/startChart/handle/scp_message_handler_factory.dart index f9b35582..8818da28 100644 --- a/lib/talk/startChart/handle/scp_message_handler_factory.dart +++ b/lib/talk/startChart/handle/scp_message_handler_factory.dart @@ -54,6 +54,8 @@ class ScpMessageHandlerFactory { return UdpTalkDataHandler(); case PayloadTypeConstant.talkHangup: return UdpTalkHangUpHandler(); + case PayloadTypeConstant.RbcuInfo: + return UdpTalkHangUpHandler(); default: return UnKnowPayloadTypeHandler(); } diff --git a/lib/talk/startChart/p2p/p2p_manage.dart b/lib/talk/startChart/p2p/p2p_manage.dart new file mode 100644 index 00000000..20f4c551 --- /dev/null +++ b/lib/talk/startChart/p2p/p2p_manage.dart @@ -0,0 +1,21 @@ +import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart'; + +class P2pManage { + RbcuInfo? communicationObjectRbcuInfo; + + + void init(){ + + } + + + // 解析 address 属性,提取对方的 IP 和端口 + List> parseRemoteAddresses() { + final addresses = communicationObjectRbcuInfo?.address ?? []; + return addresses.map((addr) { + final parts = addr.split(':'); + return {'ip': parts[0], 'port': parts[1]}; + }).toList(); + } + +} diff --git a/lib/talk/startChart/proto/rbcu.pb.dart b/lib/talk/startChart/proto/rbcu.pb.dart new file mode 100644 index 00000000..9d3f6c51 --- /dev/null +++ b/lib/talk/startChart/proto/rbcu.pb.dart @@ -0,0 +1,278 @@ +// +// Generated code. Do not modify. +// source: rbcu.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +/// RbcuInfo 地址交换 +class RbcuInfo extends $pb.GeneratedMessage { + factory RbcuInfo({ + $core.String? sessionId, + $core.String? name, + $core.Iterable<$core.String>? address, + $fixnum.Int64? time, + $core.bool? isResp, + }) { + final $result = create(); + if (sessionId != null) { + $result.sessionId = sessionId; + } + if (name != null) { + $result.name = name; + } + if (address != null) { + $result.address.addAll(address); + } + if (time != null) { + $result.time = time; + } + if (isResp != null) { + $result.isResp = isResp; + } + return $result; + } + RbcuInfo._() : super(); + factory RbcuInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RbcuInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RbcuInfo', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'SessionId', protoName: 'SessionId') + ..aOS(2, _omitFieldNames ? '' : 'Name', protoName: 'Name') + ..pPS(4, _omitFieldNames ? '' : 'Address', protoName: 'Address') + ..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'Time', $pb.PbFieldType.OU6, protoName: 'Time', defaultOrMaker: $fixnum.Int64.ZERO) + ..aOB(6, _omitFieldNames ? '' : 'isResp', protoName: 'isResp') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RbcuInfo clone() => RbcuInfo()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RbcuInfo copyWith(void Function(RbcuInfo) updates) => super.copyWith((message) => updates(message as RbcuInfo)) as RbcuInfo; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RbcuInfo create() => RbcuInfo._(); + RbcuInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RbcuInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RbcuInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get sessionId => $_getSZ(0); + @$pb.TagNumber(1) + set sessionId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasSessionId() => $_has(0); + @$pb.TagNumber(1) + void clearSessionId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get name => $_getSZ(1); + @$pb.TagNumber(2) + set name($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasName() => $_has(1); + @$pb.TagNumber(2) + void clearName() => clearField(2); + + @$pb.TagNumber(4) + $core.List<$core.String> get address => $_getList(2); + + @$pb.TagNumber(5) + $fixnum.Int64 get time => $_getI64(3); + @$pb.TagNumber(5) + set time($fixnum.Int64 v) { $_setInt64(3, v); } + @$pb.TagNumber(5) + $core.bool hasTime() => $_has(3); + @$pb.TagNumber(5) + void clearTime() => clearField(5); + + @$pb.TagNumber(6) + $core.bool get isResp => $_getBF(4); + @$pb.TagNumber(6) + set isResp($core.bool v) { $_setBool(4, v); } + @$pb.TagNumber(6) + $core.bool hasIsResp() => $_has(4); + @$pb.TagNumber(6) + void clearIsResp() => clearField(6); +} + +/// RbcuProbe 万箭齐发 +class RbcuProbe extends $pb.GeneratedMessage { + factory RbcuProbe({ + $core.String? sessionId, + $core.String? data, + $core.String? targetAddress, + }) { + final $result = create(); + if (sessionId != null) { + $result.sessionId = sessionId; + } + if (data != null) { + $result.data = data; + } + if (targetAddress != null) { + $result.targetAddress = targetAddress; + } + return $result; + } + RbcuProbe._() : super(); + factory RbcuProbe.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RbcuProbe.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RbcuProbe', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'SessionId', protoName: 'SessionId') + ..aOS(2, _omitFieldNames ? '' : 'Data', protoName: 'Data') + ..aOS(3, _omitFieldNames ? '' : 'TargetAddress', protoName: 'TargetAddress') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RbcuProbe clone() => RbcuProbe()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RbcuProbe copyWith(void Function(RbcuProbe) updates) => super.copyWith((message) => updates(message as RbcuProbe)) as RbcuProbe; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RbcuProbe create() => RbcuProbe._(); + RbcuProbe createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RbcuProbe getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RbcuProbe? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get sessionId => $_getSZ(0); + @$pb.TagNumber(1) + set sessionId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasSessionId() => $_has(0); + @$pb.TagNumber(1) + void clearSessionId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get data => $_getSZ(1); + @$pb.TagNumber(2) + set data($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasData() => $_has(1); + @$pb.TagNumber(2) + void clearData() => clearField(2); + + @$pb.TagNumber(3) + $core.String get targetAddress => $_getSZ(2); + @$pb.TagNumber(3) + set targetAddress($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasTargetAddress() => $_has(2); + @$pb.TagNumber(3) + void clearTargetAddress() => clearField(3); +} + +/// RbcuConfirm 连通确认 +class RbcuConfirm extends $pb.GeneratedMessage { + factory RbcuConfirm({ + $core.String? sessionId, + $core.String? probeAddress, + $core.String? receiveAddress, + }) { + final $result = create(); + if (sessionId != null) { + $result.sessionId = sessionId; + } + if (probeAddress != null) { + $result.probeAddress = probeAddress; + } + if (receiveAddress != null) { + $result.receiveAddress = receiveAddress; + } + return $result; + } + RbcuConfirm._() : super(); + factory RbcuConfirm.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RbcuConfirm.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RbcuConfirm', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'SessionId', protoName: 'SessionId') + ..aOS(2, _omitFieldNames ? '' : 'ProbeAddress', protoName: 'ProbeAddress') + ..aOS(3, _omitFieldNames ? '' : 'ReceiveAddress', protoName: 'ReceiveAddress') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RbcuConfirm clone() => RbcuConfirm()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RbcuConfirm copyWith(void Function(RbcuConfirm) updates) => super.copyWith((message) => updates(message as RbcuConfirm)) as RbcuConfirm; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RbcuConfirm create() => RbcuConfirm._(); + RbcuConfirm createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RbcuConfirm getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RbcuConfirm? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get sessionId => $_getSZ(0); + @$pb.TagNumber(1) + set sessionId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasSessionId() => $_has(0); + @$pb.TagNumber(1) + void clearSessionId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get probeAddress => $_getSZ(1); + @$pb.TagNumber(2) + set probeAddress($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasProbeAddress() => $_has(1); + @$pb.TagNumber(2) + void clearProbeAddress() => clearField(2); + + @$pb.TagNumber(3) + $core.String get receiveAddress => $_getSZ(2); + @$pb.TagNumber(3) + set receiveAddress($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasReceiveAddress() => $_has(2); + @$pb.TagNumber(3) + void clearReceiveAddress() => clearField(3); +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/talk/startChart/proto/rbcu.pbenum.dart b/lib/talk/startChart/proto/rbcu.pbenum.dart new file mode 100644 index 00000000..720f44c1 --- /dev/null +++ b/lib/talk/startChart/proto/rbcu.pbenum.dart @@ -0,0 +1,11 @@ +// +// Generated code. Do not modify. +// source: rbcu.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + diff --git a/lib/talk/startChart/proto/rbcu.pbjson.dart b/lib/talk/startChart/proto/rbcu.pbjson.dart new file mode 100644 index 00000000..48a9d2e4 --- /dev/null +++ b/lib/talk/startChart/proto/rbcu.pbjson.dart @@ -0,0 +1,64 @@ +// +// Generated code. Do not modify. +// source: rbcu.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use rbcuInfoDescriptor instead') +const RbcuInfo$json = { + '1': 'RbcuInfo', + '2': [ + {'1': 'SessionId', '3': 1, '4': 1, '5': 9, '10': 'SessionId'}, + {'1': 'Name', '3': 2, '4': 1, '5': 9, '10': 'Name'}, + {'1': 'Address', '3': 4, '4': 3, '5': 9, '10': 'Address'}, + {'1': 'Time', '3': 5, '4': 1, '5': 4, '10': 'Time'}, + {'1': 'isResp', '3': 6, '4': 1, '5': 8, '10': 'isResp'}, + ], +}; + +/// Descriptor for `RbcuInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List rbcuInfoDescriptor = $convert.base64Decode( + 'CghSYmN1SW5mbxIcCglTZXNzaW9uSWQYASABKAlSCVNlc3Npb25JZBISCgROYW1lGAIgASgJUg' + 'ROYW1lEhgKB0FkZHJlc3MYBCADKAlSB0FkZHJlc3MSEgoEVGltZRgFIAEoBFIEVGltZRIWCgZp' + 'c1Jlc3AYBiABKAhSBmlzUmVzcA=='); + +@$core.Deprecated('Use rbcuProbeDescriptor instead') +const RbcuProbe$json = { + '1': 'RbcuProbe', + '2': [ + {'1': 'SessionId', '3': 1, '4': 1, '5': 9, '10': 'SessionId'}, + {'1': 'Data', '3': 2, '4': 1, '5': 9, '10': 'Data'}, + {'1': 'TargetAddress', '3': 3, '4': 1, '5': 9, '10': 'TargetAddress'}, + ], +}; + +/// Descriptor for `RbcuProbe`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List rbcuProbeDescriptor = $convert.base64Decode( + 'CglSYmN1UHJvYmUSHAoJU2Vzc2lvbklkGAEgASgJUglTZXNzaW9uSWQSEgoERGF0YRgCIAEoCV' + 'IERGF0YRIkCg1UYXJnZXRBZGRyZXNzGAMgASgJUg1UYXJnZXRBZGRyZXNz'); + +@$core.Deprecated('Use rbcuConfirmDescriptor instead') +const RbcuConfirm$json = { + '1': 'RbcuConfirm', + '2': [ + {'1': 'SessionId', '3': 1, '4': 1, '5': 9, '10': 'SessionId'}, + {'1': 'ProbeAddress', '3': 2, '4': 1, '5': 9, '10': 'ProbeAddress'}, + {'1': 'ReceiveAddress', '3': 3, '4': 1, '5': 9, '10': 'ReceiveAddress'}, + ], +}; + +/// Descriptor for `RbcuConfirm`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List rbcuConfirmDescriptor = $convert.base64Decode( + 'CgtSYmN1Q29uZmlybRIcCglTZXNzaW9uSWQYASABKAlSCVNlc3Npb25JZBIiCgxQcm9iZUFkZH' + 'Jlc3MYAiABKAlSDFByb2JlQWRkcmVzcxImCg5SZWNlaXZlQWRkcmVzcxgDIAEoCVIOUmVjZWl2' + 'ZUFkZHJlc3M='); + diff --git a/lib/talk/startChart/proto/rbcu.pbserver.dart b/lib/talk/startChart/proto/rbcu.pbserver.dart new file mode 100644 index 00000000..8f46ccae --- /dev/null +++ b/lib/talk/startChart/proto/rbcu.pbserver.dart @@ -0,0 +1,14 @@ +// +// Generated code. Do not modify. +// source: rbcu.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +export 'rbcu.pb.dart'; + diff --git a/lib/talk/startChart/proto/rbcu.proto b/lib/talk/startChart/proto/rbcu.proto new file mode 100644 index 00000000..28251468 --- /dev/null +++ b/lib/talk/startChart/proto/rbcu.proto @@ -0,0 +1,39 @@ +//Relay-Based Connection Upgrade (RBCU) 是通信流程中的一个步骤。它通过中继路径确保基础通信的可用性,并在此基础上,尝试升级到更高效的直连(P2P)方式。 + +//## RBCU 流程 +//前提:1. 已建立的中继连接, 2. 可以获取自己socket对应的外网地址端口(STUN或其他)。 +//设定主动方为A,被动方为B +//1. 【基于中继】A获取地址列表,形成RbcuInfo发送到B,并等待RbcuInfo。 +//2. 【基于中继】B收到RbcuInfo,将自己的地址列表形成RbcuResp作为响应。 +//4. 【基于UDP】AB向对方的地址列表发送RbcuProbe +//5. 【基于UDP】AB收到RbcuProbe则认为直连成功,并回复RbcuConfirm +// 参阅:https://docs.star-lock.cn/zh/starchart/rbcu + +syntax = "proto3"; +package main; +option go_package = "./spb/rbcu"; + +// RbcuInfo 地址交换 +message RbcuInfo { + string SessionId = 1; // 随机UUID,用于匹配接下来的打洞会话 + string Name = 2; // 用于标识自己的名字 + repeated string Address = 4; // 地址+端口 列表 + uint64 Time = 5; // 开始时间戳 + bool isResp = 6; // 是否是响应, 如果是响应,就不用回复了, 第一个发起的人这里传false,后续收到并发出的人这里传true +} + +// RbcuProbe 万箭齐发 +message RbcuProbe { + string SessionId = 1; // RbcuInfo的UUID,用于匹配 + string Data = 2; // 100字节随机数据,因为有些防火墙会拦截小包(空白也行,但是会被pb压缩,所以最好填充随机或顺序数据) + string TargetAddress = 3; // 目标地址,例如这个包我发往192.168.1.2:9000 那么这个字段就是192.168.1.2:9000 +} + +// RbcuConfirm 连通确认 +message RbcuConfirm { + string SessionId = 1; // RbcuInfo的UUID,用于匹配 + string ProbeAddress = 2; // 地址+端口 从RbcuProbe里面取的TargetAddress + string ReceiveAddress = 3; // 地址+端口 收到RbcuProbe的来源地址(udp_read()函数的返回值) +} + + diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 10022e9f..2462bb0e 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -1,12 +1,6 @@ import 'dart:async'; +import 'package:fixnum/fixnum.dart'; import 'dart:io'; -import 'dart:typed_data'; - -import 'package:convert/convert.dart'; -import 'package:fast_rsa/fast_rsa.dart' as fastRsa; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:pointycastle/export.dart' as pc; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/login/login/entity/LoginData.dart'; @@ -25,11 +19,13 @@ import 'package:star_lock/talk/startChart/entity/report_information_data.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/entity/star_chart_register_node_entity.dart'; import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart'; +import 'package:star_lock/talk/startChart/handle/other/do_sign.dart'; import 'package:star_lock/talk/startChart/handle/other/talke_data_over_time_timer_manager.dart'; import 'package:star_lock/talk/startChart/handle/other/talke_ping_over_time_timer_manager.dart'; import 'package:star_lock/talk/startChart/handle/other/talke_request_over_time_timer_manager.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart'; +import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_expect.pbserver.dart'; @@ -39,9 +35,6 @@ import 'package:star_lock/tools/deviceInfo_utils.dart'; import 'package:star_lock/tools/storage.dart'; import 'package:uuid/uuid.dart'; -import 'dart:convert'; -import 'package:asn1lib/asn1lib.dart' as asn1lib; // Prefix for asn1lib - class StartChartManage { // 私有构造函数,防止外部直接new对象 StartChartManage._internal(); @@ -64,16 +57,13 @@ class StartChartManage { final String _productName = F.navTitle; RawDatagramSocket? _udpSocket; - final Map> _completers = {}; // 发送消息的请求是否完成 - final Uuid _uuid = Uuid(); // 用于区分发送的消息的唯一id - int _messageMaxTimeout = 5; // 消息最大超时时间(s) + final Uuid _uuid = Uuid(); // 随机UUID,用于匹配接下来的打洞会话 late String remoteHost = ''; // 远程主机地址(服务器返回) late int remotePort = 0; // 远程主机端口(服务器返回) final int localPort = 62289; // 本地端口 String localPublicHost = ''; // 本地公网ip地址 int heartbeatIntervalTime = 1; // 心跳包间隔时间(s) - Timer? _heartBeatTimer; // 心跳包定时器 bool _heartBeatTimerRunning = false; // 心跳包定时任务发送状态 @@ -84,15 +74,14 @@ class StartChartManage { final String echoPeerId = '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH'; bool isOnlineStartChartServer = false; // 星图是否上线成功 - int reStartOnlineStartChartServerIntervalTime = 1; // 重新上线星图服务任务间隔(s) Timer? reStartOnlineStartChartServerTimer; // 重新上线定时器 - int talkPingIntervalTime = 1; // 发送通话保持消息间隔(s) Timer? talkPingTimer; // 发送通话保持消息定时器 - int talkExpectIntervalTime = 1; // 发送通话预期数据的消息间隔(s) Timer? talkExpectTimer; // 发送通话预期消息定时器 Timer? talkAcceptTimer; // 重发同意接听消息定时器 - int talkDataIntervalTime = 10; // 通话数据的消息间隔(ms) + int talkDataIntervalTime = 10; // 发送通话数据的消息间隔(ms) + int _defaultIntervalTime = 1; // 默认定时发送间隔(s) Timer? talkDataTimer; // 发送通话数据消息定时器 + Timer? rbcuInfoTimer; // p2p地址交换定时器 final int _maxPayloadSize = 8 * 1024; // 分包大小 @@ -102,9 +91,6 @@ class StartChartManage { audioType: [AudioTypeE.G711], ); - // 默认通话数据 - TalkData _defaultTalkData = TalkData(); - String relayPeerId = ''; // 中继peerId // 获取 StartChartTalkStatus 的唯一实例 @@ -234,6 +220,65 @@ class StartChartManage { } } + // 发送RbcuInfo 地址交换消息 + void _sendRbcuInfoMessage() async { + final uuid = _uuid.v1(); + final int timestamp = DateTime.now().millisecondsSinceEpoch; + final Int64 int64Timestamp = Int64(timestamp); // 使用构造函数 + + // 获取本机所有ip地址和中继返回的外网地址 + final List listenAddrDataList = + await _makeListenAddrDataList(); + listenAddrDataList.insert( + 0, // 插入到头部 + ListenAddrData( + type: ListenAddrTypeConstant.local, + address: localPublicHost + ':' + localPort.toString(), + ), + ); + + final address = listenAddrDataList + .where((element) => + element.type == ListenAddrTypeConstant.local) // 过滤出本地地址 + .map((e) => e.address) // 转换为 List + .where((addr) => addr != null) // 过滤掉 null 值 + .map( + (addr) => addr!.replaceAll(IpConstant.udpUrl, ''), + ) // 去除 "udp://" 前缀 + .cast< + String>(); // 转换为 Iterable// 将 Iterable 转换为 Iterable + final RbcuInfo rbcuInfo = RbcuInfo( + sessionId: uuid, + name: uuid, + address: address, + time: int64Timestamp, + isResp: false, + ); + final message = MessageCommand.genericRbcuInfoMessage( + ToPeerId: ToPeerId, + FromPeerId: FromPeerId, + MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true), + rbcuInfo: rbcuInfo, + ); + _sendMessage(message: message); + } + + // 启动定时任务 + void startSendingRbcuInfoMessages() { + // 每隔 1 秒执行一次 _sendRbcuInfoMessage + rbcuInfoTimer ??= + Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) { + // 发送RbcuInfo 地址交换消息 + _sendRbcuInfoMessage(); + }); + } + + // 停止定时任务 + void stopSendingRbcuInfoMessages() { + rbcuInfoTimer?.cancel(); // 取消定时器 + rbcuInfoTimer = null; + } + // 发送上线消息 Future _sendOnlineMessage() async { if (isOnlineStartChartServer) { @@ -296,14 +341,6 @@ class StartChartManage { } // 分包发送完了递增一下id MessageCommand.getNextMessageId(ToPeerId); - // - // final message = MessageCommand.talkDataMessage( - // FromPeerId: FromPeerId, - // ToPeerId: ToPeerId, - // talkData: talkData, - // MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true), - // ); - // await _sendMessage(message: message); } // 发送心跳包消息 @@ -495,7 +532,7 @@ class StartChartManage { } reStartOnlineStartChartServerTimer ??= Timer.periodic( Duration( - seconds: reStartOnlineStartChartServerIntervalTime, + seconds: _defaultIntervalTime, ), (Timer timer) async { // 重新发送上线消息 @@ -575,7 +612,7 @@ class StartChartManage { // 获取私钥 final privateKey = await getPrivateKey(); // 生成签名 - final sign = await _generateSign( + final sign = await DoSign().generateSign( currentTimestamp: relayInfoEntity!.time ?? 0, privateKeyHex: privateKey, ); @@ -697,135 +734,14 @@ class StartChartManage { return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(''); } - // 生成签名sing - Future _generateSign({ - required int currentTimestamp, - required String privateKeyHex, - }) async { - String resultSign = ''; - try { - // 1. 将 32 位时间戳以小端字节序编码为二进制数据 - Uint8List signData = encodeTimestampToLittleEndianBytes(currentTimestamp); - - // 2.将十六进制字符串转换为字节数组 - List privateKeyBytes = hexToBytes(privateKeyHex); - - // 3.将私钥转换为 PEM 格式 - final pemPrivateKey = - convertToPemPrivateKey(privateKeyBytes, isPKCS8: true); - // 4.签名 - var result = await fastRsa.RSA - .signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey); - resultSign = hex.encode(result); - } catch (e) { - throw StartChartMessageException('❌--->上报信息生成签名时出现错误: $e'); - e.printError(); - } - return resultSign ?? ''; - } - - // 将 32 位时间戳以小端字节序编码为二进制数据 - Uint8List encodeTimestampToLittleEndianBytes(int timestamp) { - // 创建一个 4 字节的 ByteData 对象 - ByteData byteData = ByteData(4); - - // 将 32 位时间戳写入 ByteData,使用小端字节序 - byteData.setUint32(0, timestamp, Endian.little); - - // 将 ByteData 转换为 Uint8List - Uint8List bytes = byteData.buffer.asUint8List(); - - return bytes; - } - - /// 转换私钥格式 - String convertToPemPrivateKey(List privateKeyBytes, - {bool isPKCS8 = true}) { - // 将字节数组转换为Base64编码的字符串 - String base64PrivateKey = base64Encode(privateKeyBytes); - - // 添加PEM格式的头尾标签 - String pemHeader; - String pemFooter; - - if (isPKCS8) { - pemHeader = "-----BEGIN PRIVATE KEY-----"; - pemFooter = "-----END PRIVATE KEY-----"; - } else { - pemHeader = "-----BEGIN RSA PRIVATE KEY-----"; - pemFooter = "-----END RSA PRIVATE KEY-----"; - } - - // 将Base64字符串分行为每行64个字符 - const lineLength = 64; - List lines = []; // 用于存储每一行 - - for (int i = 0; i < base64PrivateKey.length; i += lineLength) { - int end = (i + lineLength < base64PrivateKey.length) - ? i + lineLength - : base64PrivateKey.length; - lines.add(base64PrivateKey.substring(i, end)); - } - - // 组合成完整的PEM格式字符串 - return "$pemHeader\n${lines.join('\n')}\n$pemFooter"; - } - - /// 自定义 PEM 格式的 RSA 私钥解析器 - pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) { - // 将十六进制字符串转换为字节数组 - final uint8list = Uint8List.fromList(hexToBytes(privateKeyHex)); - try { - // 使用 asn1lib 的 ASN1Parser 解析 - final asn1Parser = asn1lib.ASN1Parser(uint8list); - final topLevelSeq = asn1Parser.nextObject() as asn1lib.ASN1Sequence; - - final modulus = bytesToBigInt( - (topLevelSeq.elements[1] as asn1lib.ASN1Integer).valueBytes()); - final privateExponent = bytesToBigInt( - (topLevelSeq.elements[3] as asn1lib.ASN1Integer).valueBytes()); - final p = bytesToBigInt( - (topLevelSeq.elements[4] as asn1lib.ASN1Integer).valueBytes()); - final q = bytesToBigInt( - (topLevelSeq.elements[5] as asn1lib.ASN1Integer).valueBytes()); - - return pc.RSAPrivateKey(modulus, privateExponent, p, q); - } catch (e) { - // 如果发生解码错误,打印错误信息 - print("Error decoding private key: $e"); - rethrow; - } - } - - // 将十六进制字符串转换为字节数组 - List hexToBytes(String hex) { - return List.generate(hex.length ~/ 2, - (i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16)); - } - - BigInt bytesToBigInt(Uint8List bytes) { - return BigInt.parse( - bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(), - radix: 16, - ); - } - /// 获取公钥 Future getPublicKey() async { - // 从缓存中获取星图注册节点信息 - // final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = - // await Storage.getStarChartRegisterNodeInfo(); - // return starChartRegisterNodeInfo?.peer?.publicKey ?? ''; final loginData = await Storage.getLoginData(); return loginData?.starchart?.starchartPeerPublicKey ?? ''; } /// 获取私钥 Future getPrivateKey() async { - // 从缓存中获取星图注册节点信息 - // final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = - // await Storage.getStarChartRegisterNodeInfo(); - // return starChartRegisterNodeInfo?.peer?.privateKey ?? ''; final loginData = await Storage.getLoginData(); return loginData?.starchart?.starchartPeerPrivateKey ?? ''; } @@ -881,7 +797,7 @@ class StartChartManage { void startTalkPingMessageTimer() { talkPingTimer ??= Timer.periodic( Duration( - seconds: talkPingIntervalTime, + seconds: _defaultIntervalTime, ), (Timer timer) async { await sendTalkPingMessage( @@ -902,7 +818,7 @@ class StartChartManage { void startTalkExpectTimer() { talkExpectTimer ??= Timer.periodic( Duration( - seconds: talkExpectIntervalTime, + seconds: _defaultIntervalTime, ), (Timer timer) { // 发送期望接受消息 @@ -917,7 +833,7 @@ class StartChartManage { void startTalkAcceptTimer() { talkAcceptTimer ??= Timer.periodic( Duration( - seconds: talkExpectIntervalTime, + seconds: _defaultIntervalTime, ), (Timer timer) { sendTalkAcceptMessage(); @@ -930,102 +846,6 @@ class StartChartManage { talkAcceptTimer = null; // 清除定时器引用 } - /// 通话数据定时器 - void startTalkDataTimer() async { - // 如果已经启动了就不运行 - if (talkDataTimer != null) return; - // 读取 assets 文件 - final ByteData data = await rootBundle.load('assets/talk.h264'); - final List byteData = data.buffer.asUint8List(); - int current = 0; - int start = 0; - int end = 0; - final List chunks = extractChunks(byteData); - talkDataTimer ??= Timer.periodic( - Duration( - milliseconds: talkDataIntervalTime, - ), - (Timer timer) { - if (current >= chunks.length) { - print('数据已经发完'); - start = 0; - end = 0; - current = 0; - timer.cancel(); - return; - } - // 提取 NALU 边界并生成 chunks - end = chunks[current]; - current++; - List frameData = byteData.sublist(start, end); - if (frameData.length == 0) timer.cancel(); - _defaultTalkData = TalkData( - content: frameData, - contentType: TalkData_ContentTypeE.H264, - ); - start = end; - // 发送通话数据 - sendTalkDataMessage(talkData: _defaultTalkData); - }, - ); - } - - List extractChunks(List byteData) { - int i = 0; - int length = byteData.length; - int naluCount = 0; - int value; - int state = 0; - int lastIndex = 0; - List result = []; - const minNaluPerChunk = 22; // 每个数据块包含的最小NALU数量 - - while (i < length) { - value = byteData[i++]; - // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01) - switch (state) { - case 0: - if (value == 0) { - state = 1; - } - break; - case 1: - if (value == 0) { - state = 2; - } else { - state = 0; - } - break; - case 2: - case 3: - if (value == 0) { - state = 3; - } else if (value == 1 && i < length) { - if (lastIndex > 0) { - naluCount++; - } - if (naluCount >= minNaluPerChunk) { - result.add(lastIndex - state - 1); - naluCount = 0; - } - state = 0; - lastIndex = i; - } else { - state = 0; - } - break; - default: - break; - } - } - - if (naluCount > 0) { - result.add(lastIndex); - } - - return result; - } - // 停止发送通话数据 void stopTalkDataTimer() { talkDataTimer?.cancel(); @@ -1061,7 +881,6 @@ class StartChartManage { talkExpect: talkExpectReq); } - /// 修改预期接收到的数据 void sendImageVideoAndG711AudioTalkExpectData() { final talkExpectReq = TalkExpectReq( @@ -1080,15 +899,18 @@ class StartChartManage { stopTalkPingMessageTimer(); stopReStartOnlineStartChartServer(); stopTalkDataTimer(); + stopSendingRbcuInfoMessages(); _resetData(); await Storage.removerRelayInfo(); await Storage.removerStarChartRegisterNodeInfo(); } + /// 重置数据 void _resetData() { _defaultTalkExpect = TalkExpectReq( videoType: [VideoTypeE.IMAGE], audioType: [AudioTypeE.G711], ); + isOnlineStartChartServer = false; } } diff --git a/lib/talk/startChart/views/talkView/talk_view_page.dart b/lib/talk/startChart/views/talkView/talk_view_page.dart index 232c08c3..bc64d9cc 100644 --- a/lib/talk/startChart/views/talkView/talk_view_page.dart +++ b/lib/talk/startChart/views/talkView/talk_view_page.dart @@ -199,15 +199,16 @@ class _TalkViewPageState extends State // 录制 GestureDetector( onTap: () async { - if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { - if (state.isRecordingScreen.value) { - await logic.stopRecording(); - print('停止录屏'); - } else { - await logic.startRecording(); - print('开始录屏'); - } - } + logic.showToast('功能暂未开放'.tr); + // if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { + // if (state.isRecordingScreen.value) { + // await logic.stopRecording(); + // print('停止录屏'); + // } else { + // await logic.startRecording(); + // print('开始录屏'); + // } + // } }, child: Container( width: 50.w, diff --git a/pubspec.yaml b/pubspec.yaml index c2b44e1e..5cf43137 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -260,6 +260,7 @@ dependencies: flutter_screen_recording: 2.0.16 #图库保存 gallery_saver: ^2.3.2 + fixnum: ^1.1.1