[{"content":"日本之行 原本打算作为毕业旅行的日本之行因为我的一拖再拖拖到了现在（毕业旅行给了新疆也是非常满足），也是趁着这个国庆完成了这次为期11天的旅行，东京+京都，现在来看，真是两个截然不同的地方\n东京之旅 9月30日 今天从首都机场直接起飞，飞到东京成田，早上7点就出发了，晚上接近5点才到，整整一天的赶路，成田离着市区还是太远了，如果有条件的话还是飞羽田吧，只不过这不是坐春秋的我考虑的事情（ 飞机下的云海与远处的富士山令人向往，在飞机上也有如此美景，心情也是无比激动 此时的我还没有意识到富士山上，没有雪顶 飞机上的云层 下午五点左右终于到达东京，在成田快线Access上站了一个半小时，在押上站下车第一件事，拍一下夜晚的晴空塔，当时还下着小雨，也是有种别样的美感 夜晚的晴空塔 感觉雨天东京的街道都没怎么有人，明明才七点左右，可能附近是住宅区吧，简单的citywalk了一下，还在路边发现了一个电话亭，这种电话亭在东京真的常见，感觉走几步就有一个，现在也就打个紧急电话 电话亭 来东京的第一顿饭！直接吃拉面，日本好多拉面馆是在门口放一个机器，然后上去选自己想要的东西自助结账，方便是方便，但是坏处是，如果碰到不会弄的会排队（比如我们这种刚来的），店员看出我们的窘迫还给我们拿来了中文菜单对着选，谢谢你，店员桑 尝起来确实比家里这边的拉面馆要好吃，一碗要1080日元，折合人民币45块了，后来才知道这已经是日本这边比较平价的店了，终于体会资本主义国家的物价 辣味增拉面 10月1日 富士山一日游启动，说实话，这一天实际上是准备的有点仓促，因为天气的原因临时决定10月1日来看（看能见度网站只有1号是10），然后急匆匆买车票，看攻略，虽然最后的行程其实还算满意，但是感觉错过了很多内容 出发前坐电车过去，第一次坐拍一下站台，日本电车真的很多这种半封闭甚至不封闭的，怪不得人跳轨呢 中央总武线的两国站 然后再做去河口湖的大巴过去，早上七点坐一个半小时车，九点多才到河口湖站 在大巴上拍的富士山 最主要的还是这个富士山，没有雪顶（再次强调），我不明白为什么现在都已经10月份了，还没有雪顶呢，这是正常的吗，这是正常的吗！你知道的，没有雪顶的富士山，就跟没有冰淇淋的甜筒一样，根本就是啥也没有啊（，然后中段还被云挡住了，爬到这边的山上也基本看不到啥，而且老外特别多，我感觉比我之前见过的所有外国人加起来都多（bushi） 鸟居+富士山 这个湖还是蛮大的，不过去了新疆之后感觉，也就那样（ 河口湖 然后是坐去富士山站，拍一下金鸟居，远处就能看到富士山 金鸟居 沿着这条路往上走，这边叫做天梯小镇，感觉富士山就在眼前了 天梯小镇 然后去坐了一下这边的游船，这个体验还是很好的，特色游船+湖上美景，可以看到河口湖与富士山同框，也算是扳回一局\n江户时期风格的游船 湖上风景 然后下午五点坐大巴再回东京站，趁着还有时间再去看看晚上的东京铁塔 在去之前看到了东京站里的宝可梦店，这里是天堂吗，而且还免税，直接拿下耿鬼和沼王 一墙的宝可梦玩偶 一边听着Vaundy的东京闪景，碰到了晚上的东京铁塔，究竟是什么人会选择这个粉紫色，根本就不好看，之后在涩谷sky上拍的东京铁塔要好看一百倍，但是这个仰拍的视角非常好（如果是白天的话） 夜晚的东京铁塔 走在深夜的东京街头，看着空荡的街道，耳边环绕的“どこへ行こう，どこへ行こう”（要去哪里呢），才知道繁华的背后是无尽的空虚与寂寞\n10月2日 来池袋了嗷家人们，在日本最中国的地方，由我们日本当地人王哥带队来一个池袋一日游（ 你甚至可以看到杨国福，但是据说很贵，所以就不尝试了 杨国福 池袋这边二次元浓度也很浓厚啊，GiGO是日本这边一个挺大的抓娃娃机连锁店，这不是我们中国的鸣潮吗，下次记得标明出处（ 鸣潮立牌 不要把小祥从我身边抢走！（来自著名重女，三角初华） Sunshine City的大楼梯 当当当，来到里面的宝可梦中心了，中间那个大屏幕可以放不同精灵的mega，正好ZA出了很多新mega，拿来展示，进去转了一圈没啥想买的了，主要还是因为在东京站买了不少（，卡牌那边排队特别长，只不过我对PCG没有什么兴趣，就不上去凑热闹了 我去，放了五代的御三家在商场里面吗，有点意思 历代御三家 还有国内不存在的游戏 Pokemon GO 然后接下来是池袋的A店总店，整整九层，最上层还有鲁鲁修20周年纪念展，可惜没买票，不过幸好我也没看过鲁鲁修（，第二层是漫画，太丰富了，看花眼了属于是，买了一本柯南，一本擅长捉弄的高木同学最终话 A店漫画层 然后还有各种动漫的CD，大屏放着邦邦新团的歌，茉团的カーネーションの咲く日に还挺好听，直接就加歌单了 轻音少女的专辑 其他层还有各种动漫谷子，因为没啥兴趣我也就不拍了（人家也不让拍） 下一站是中野百老汇，这里真是什么东西都有，耳机/手表/CD/谷子/模玩/同人志/galgame，喜欢淘的话可以淘到不少好东西，不过这边中古比较多，都是有点年代的东西，这之后去秋叶原，感觉还是秋叶原的东西新一些 中野百老汇 买了个鲷鱼烧吃，红豆味的，好吃！ 红豆味鲷鱼烧 晚饭期间中野这边还有不少人，小巷中，居酒屋里，人头攒动 美食小巷 10月3日 今天准备两次登顶，一次是晴空塔，另一次是涩谷Sky，在不同时间俯瞰东京 白天的晴空塔 没想到还能赶上晴空塔跟chiikawa联动，只不过这个我也不感兴趣（ chiikawa联动墙纸 天气不算特别好，有些阴天，但是能看到阳光穿过云层洒在高楼上，像是天气之子里刚刚雨过天晴的东京，这个蓝调的滤镜还真不错\n眺望东京塔 太阳出来之后正好在晴空塔上方，拍一下他的影子 晴空塔的影子 还有花束般的恋爱里面男主喜欢拍的大圆球（存储液化气的装置） 球形储气罐 来点晴空塔特色冰淇淋 skytree soft 下午来到涩谷了，依旧是人山人海 涩谷街头 小红书上找的一家猪排饭，是家连锁店，吃起来非常满足，碾碎芝麻再加上料汁蘸着吃，然后再来点萝卜咸菜，说到这里又流口水了，只不过吃到后面就有点腻了，而且这一份也不便宜，将近3000日元了 猪排饭 吃完去Tower Records转了一下，最大的唱片店，各种各样的CD，就是为了真夜中和胡子男来的，本来还想买夜鹿的，不过我还是比较喜欢夜鹿的新歌，旧专辑就算了。甚至还有各种各样V家P主的专辑，依旧眼花缭乱 门口最显眼的位置放着绿苹果的新专辑，不愧是现在JPoP的神，大森元贵你这家伙 Tower Records 接下来就是登顶涩谷Sky了，下午的天气是真不错，云都散的差不多了 好强烈的日照 忧郁的美男子（雾 晚霞配上东京铁塔，别有一番风味；夜晚的东京铁塔，更是一绝（终于不是粉紫色的灯了） 傍晚的东京铁塔 夜晚的东京铁塔 还有夜晚的涩谷十字路口，第一次见这么多人一起过十字路口 涩谷十字路口 站在高处，眺望着远处的车水马龙 夜晚东京的车道 甚至还能碰到远处在开花火大会，真是幸运 花火大会 很喜欢这张照片，在涩谷Sky上的酒吧小酌一杯，身边的人擦肩而过，望着远处的灯红酒绿，心里却只有落寞（装的） 涩谷Sky的酒吧 10月4日 今天是浅草寺+新宿，想去的地方太多，已经是特种兵了 先在浅草寺这边吃一个鳗鱼饭，可能因为是中国人开的店，所以还是比较平价的，味道也还可以 鳗鱼饭 浅草寺感觉没啥，可能国内的寺庙我也见了很多很好看的，不过这个100日元求签还是很有意思的，先摇签筒，然后再根据签上的数字去拉抽屉拿结果，抽到了一张吉 雷门 宝藏门 正殿 抽到的吉签 这分明就是我们的古诗吗，GPT给的解释是，云散之后，月亮重新明亮；只要保持诚心，愿望会逐渐得到好的回应。虽然过程中会遇到不少阻碍、事情可能反复，但最终仍会像花重新开放一样，再次走向顺利。也是希望之后的生活会越来越好 接下来登场的是须贺神社，《你的名字》的打卡地，我们去的时候已经有很多人在拍照了，下面基本都是原片的取景，东京的市中心街道确实是干净，也可能是刚下过雨，有种清新爽快的亮堂感 接下来是新宿御苑，是言叶之庭的取景地，感觉跟着新海诚能把整个东京转个遍（，秒速五厘米的在代代木那边，就不去了，反正也没樱花 男女主躲雨的小亭子 不知不觉转到晚上，先去东京都厅那边看一场投影表演，拿政府大楼当投影板也是头一次听说 Tokyo Night ＆ Light 大阪的天守阁 我去，还有初音未来登场吗，东京都厅也是好起来了，请得起大IP了（ 不知名的绿色双马尾少女 接下来就是新宿街头，果然还是晚上的新宿比较热闹，更好看一些 然后来到今天的重量级，歌舞伎町一番街，其实外表上也就是个美食街，但是不愧是红灯区，接下来你将看到，地下偶像在表演，随处可见的牛郎店，偷窥屋。这边也是蛮混乱的，一群人在街边喝酒吵闹，遍地垃圾，之前看视频，有很多女生在街边站街，我过来倒是没怎么看到，据同学说应该是有整治过 出来还看到了裸眼3D的三花猫猫 10月5日 来到东京最繁华的购物中心之一，银座，各种各样品牌的大楼，蒂芙尼，阿玛尼，优衣库，无印良品，伊东屋，不二家，还有各种大型商场，Ginza Six，伊势丹，松屋百货，不是哥们，简直是购物天堂好吧，不过我不明白的是为什么伊势丹里面的TOM FORD，TF20都没有，卖完了说是，这么火吗 蒂芙尼大楼 不二家大楼 优衣库一楼的摇摆衣服 也是来银座吃上烤肉了，还好是工作日的中午，这么几块和牛要我2000多日元，但是也确实好吃，没得说 和牛烤肉 来个可丽饼（咬了一口才想起来拍照），这个抹茶冰淇淋加一点酒，一开始吃起来很不错，后面酒味有点重我就不太喜欢了，by the way，这个也很贵 可丽饼 在Ginza Six的顶楼上拍照，被高楼挡住了，还是下去仰拍的视角更清楚一些 EXITMELSA 商场 银座和光钟楼 晚上来吃鸡汤辛拉面，这个不错，感觉比豚骨拉面要好吃 鸡汤辛拉面 逛完银座直接来东京站坐新干线，在等车的时候有个列车员过来跟我说话，特别热情我还以为是推销的（，实际上是告诉我要晚一点上车，需要先打扫车厢，最后还用中文说谢谢，还挺敬业的（日本人的服务业这一块） 这个新干线的座位，真的究极宽敞，我完完全全能伸开腿，甚至还能放个行李在面前，然后厕所还有小巧思，只要感应一下就能控制冲水和放下马桶圈 感应冲水马桶 日本在厕所这一块的确是有点设计的，之前去大楼里面的厕所基本都是下面这样的，包括酒店里面的，但是那个卫生纸，真的非常非常薄，可能是为了方便直接扔马桶里吧 京都之旅 到达京都，太美丽了京都，看看远处的京都塔吧 京都塔（深夜） 京都站，园子张开双手.jpg 如果说在东京的乐趣是花钱购物，逛各种各样的店，体验大城市的繁华，那么京都的乐趣反而是体验乡土气息，感受日本的历史，虽然我对日本历史一点兴趣也没有，但是走在山水间，看着阳光洒在那些村落、寺庙和鸟居上，会有种心旷神怡的感觉\n10月6日 早上从二年坂一路走到清水寺，穿梭在京都特色建筑林立的小道中，路上还有不少来修学旅行的高中生 二年坂 来点京都特色抹茶，虽然是小红书的网红店，这家GOKAGO的抹茶我感觉要比宇治那边的好吃，可能是因为我选的是甜度是1，所以不是很甜，宇治那边伊藤久右卫门的抹茶芭菲实在是有点甜 加浓抹茶拿铁 清水寺我也没什么特别的感觉，只不过这里是当年新兰修学旅行来的地方（好不容易在清水舞台有张新兰合影），所以来打卡一下，而且现在没有枫叶，看到网上满山飘红的清水寺，那才是最终形态 清水寺 清水舞台，冰箱贴同款 三重塔 这泉水还能直接喝，据说是日本十大名泉之首，三道泉水分别对应学业，恋爱，长寿，上去体验一把 音羽瀑布 京都这边的特色似乎是荞麦面，很多地方都有这种蘸汁荞麦面吃，冰冰的，吃起来还不错 蘸汁荞麦面 前往伏见稻荷大社路上拍的白天的鸭川，还有经常在动漫里面出现的铁路道口 鸭川 铁路道口 来到伏见稻荷大社了，这里最有名的就是千本鸟居，连绵不绝的鸟居连成一排，十分壮观，这里供奉的稻荷大神是五尊神明的总称（不知道哪五位），对保佑五谷丰收、生意兴隆、家庭安全等各种愿望都相当灵验 千本鸟居入口 每个鸟居后面都有不同的会社，好像是他们赞助的，每个人的愿望，都记录在这一条朱红色的甬道中 “身为神明使者的狐狸叼着成功的钥匙，为了实现祈愿者的愿望而四处奔走，每实现一个愿望就跨越一座鸟居，一点一点地接近神明” 鸟居背后的字 为了拍阳光洒在鸟居上，超绝不经意间拍到和服少女（我有罪） 阳光穿过树叶缝隙洒在鸟居上 这个景色真不错，我也来一张 忧郁的美男子2（大雾 实际上整个神社是一个山，不断的往上爬，虽然已经累的不行了，但是比起山下的人山人海，还是很爽的 俯瞰京都市 这种能拍到光线穿到鸟居里的景象真令人着迷 光线穿到鸟居里 傍晚时分，还亮起了小灯笼，不知道晚上又是怎样一种诡异的气氛 鸟居中的灯笼 来吃另一个京都特色，炸牛排，这个肉吃起来很生，可能只有五分熟，根本吃不惯，必须煎一下才能吃下去，这边各种配料，生鸡蛋，咖喱，椒盐，芥末，烤肉酱汁，还有七味粉，每一口都是不一样的味道\n炸牛排 来到夜晚的鸭川，站在四条大桥上拍河岸边上的纳凉床，《迷宫的十字路口》即视感，凶手从上面的台子上把凶器装进瓶子扔下去，顺着河流飘走，然后靠什么保全公司的追踪器回收，现在想想还是有点扯 夜晚的鸭川 坐到河岸上，拍河对岸的四条大桥，灯火通明，调了一下颗粒度，还有一种老照片的感觉 四条大桥 祗园四条往里走就是各种各样的商店街，也是有玉子市场的取景地，但是可能是因为在东京看了不少店铺，这边反倒没什么意思了 新京极商店街 10月7日 岚园之旅，没有走网上说的小火车加各种寺庙的组合（主要也是爱宕寺今天也关门），就走了一条有日本乡下风光，又有日本庭院风光的乡间小路，游客很少 来竹林小径这边了，大老师著名自爆名场面，不过是在晚上（ 竹林小径 这几张真的有点日漫画面的感觉了，很干净的乡下风光 乡下风光 还有各种寺庙，不过不是很感兴趣，就不进去了 常寂光寺 二尊院 本来在网上找到了嵯峨鸟居本的一家老店，结果都到12点半了还不开门，不是我说，这也太自由了，最后还是坐公交车到下面闹市区去吃了茶泡饭，吃起来还挺不错的 鸭肉茶泡饭 渡月桥，横跨在缓慢流经岚山的桂川上的一座木桥，旁边就是岚山山脚，没有樱花，没有红叶，周围只有铺天盖地的绿，也别有一番风味（自我安慰） 渡月桥 岚山山脚 这边还有一个周总理的纪念诗碑，必须过去参观一下\n雨中岚山 这里有位老先生一直为游客介绍周总理的事迹，他用他不是很流利的中文跟我们一点一点介绍周总理留学时期的事迹，以及雨中岚山这首诗的来源，一边说一边还拿笔来写，笔记本上面全是他书写的痕迹 他说我们是他的中文老师，认真聆听他不流利的中文，而他又何尝不是我们的历史老师呢，了解周总理那段留学的历史 他说二战期间给中国人民带来的沉重伤害，在这里向我们道歉，非常的对不起，这不是您的错老先生，而是日本军国主义的错，战争伤害的永远都是各国人民 他说希望世界平和，还给我们送了他自己制作的枫叶书签，是友好的象征，感谢您的书签，也感谢您一直以来的坚持与守候 一起合照时他也拿着周总理的照片，我们走时他还跑过来向我们挥手说再见，祝我们玩的开心，多可爱的一位老先生 今天的天气很好，希望能一直好下去 跟老先生的合影 告别了老先生，我们的下一站是金阁寺，这边的叶子已经有点黄了，阳光配上金阁寺的金色外观，交相映衬 金阁寺还有一个历史事件，一个见习僧人因为无法承受金阁的美，为摆脱美的观念的羁绊，纵火焚烧金阁寺，也就是三岛由纪夫写的《金阁寺》的小说题材，看完之后我只感觉这个僧人真是没吃过好的（ 金阁寺 谁能想到拍个抹茶冰淇淋都比拍金阁寺好看（ 金阁寺特色抹茶冰淇淋 10月8日 今天是京都御苑加上期待已久的任天堂博物馆之行，可以说是最期待的一集 公交车上还有这种调查券，还挺有意思的 公交车调查券 来到京都御苑，这里是历代日本天皇居住的地方，而且完全对市民开放，说实话，日本的古建筑真的没什么好看的，还得回国内看，这个紫宸殿就是正殿了，也没怎么有气势，但是庭院的设计还是很有风格的 正门 特色表演 紫宸殿 仙洞御所 下午准备去任天堂博物馆了，先坐车来宇治吃点抹茶，这边真的很多抹茶店，中村藤吉，辻利茶铺，伊藤久右卫门，不过吃起来有点太甜了，吃的时候没拍什么照片，来点网图吧 抹茶芭菲 往平等院那边走可以看到宇治桥，顺便拍到了桥上电车驶过 桥上的电车 来任天堂博物馆啦，我捏的mii形象也是直接给打印成入馆证了，真是意气风发啊牢师（ 入馆证 还有店员专门给拍照，这下真成马里奥了 经典的马里奥元素 历代游戏 进博物馆展厅里面不让拍照，所以没有照片，展示了从红白机时代到现在switch时代的所有机型，以及每一代机型上的游戏，最感兴趣的还是宝可梦和塞尔达系列，然后还有近些年才有的喷喷，去看了喷喷的原型设计，美术这一块没得说 除了展厅，楼下还有各种游戏可以玩，棒球，双人控制飞机，夹精灵球，还有射击游戏，用的还是喷喷里面的zap，可惜只拿了铜牌，这里的图片都是记录在任天堂官网上的，自动打码路人好评 射击游戏 双人操控飞机 棒球 夹精灵球 真是酣畅淋漓的一场游玩，玩完出来已经是晚上了，逛一逛这边的周边店，这也要设计盲盒吗，可恶的任天堂 任天堂的周边 宝可梦风格的井盖 再见啦任天堂博物馆，在这里体验了各种不同的游戏，玩的真的很开心！ 任天堂博物馆 在附近找到一家大阪烧店铺，一看就是小店，价格也便宜，吃起来也挺不错的，烤完直接放在你面前的烤盘上 大阪烧 夜晚的电车 10月9日 今天准备回东京了，在京都站周围转一下，玩到现在已经累的不行了，已经想回国了，也没有什么拍照的欲望，后面基本就是随手拍一下 京都塔（已明） 同学说这个大楼梯是《Hello World》的取景地，还能看到映在玻璃上的京都塔 京都站的大楼梯 京都市立艺术大学 在五条大桥这边看到的牛若丸与弁庆像，牛若丸就是少年时代的源义经，《义经记》的主角，这些也都是《迷宫的十字路口》里面介绍的 牛若丸与弁庆像 10月10日 终于来宅男圣地秋叶原了，事先声明，我不是宅男，更不是二次元，只是因为这里很出名而已（ 来这边有名的小绿楼看一下，不愧是18禁，上来就这么离谱吗，上去转一圈没给我看吐了 开幕雷击 来点万代经典的模玩，高达 EVA初号机 三国系列的敢达 在book off里还听到一首不错的歌，《リトルワールド》，前奏的钢琴真的很抓耳，已经开始循环了 秋叶原街边 这家猪排饭确实很好吃，秋叶原的一个有名的店，不过也很贵，也是2000多日元 日本最后一顿猪排饭 结语 11日全天就是赶路回国，晚上11点才到家，真的力竭了，虽然整场旅行有些虎头蛇尾吧，但也收获了不少 旅游真的是一件很累的事情，要做各种攻略，会遇到各种情况，但是经历下来回头看看这些照片，想想那些旅途见闻，又觉得一切都是值得的，是我人生中宝贵的记忆\n日本给我的感觉就像是一个精密的老式机器，一切都运转的那么流畅，一切都在规则之下，但他终归是太老了，反而显得有些死板，有些枯燥，就像我前面说的，繁华背后是空虚与寂寞\n再见了，日本，我恐怕不会再来了，我带着期望过来，也带着期望回去，我终于看清了你在现实中的样子，也终于摆脱了少年时期的我，那个对美好虚拟世界向往的我，其实我也知道我向往的不是你，而是他们构造的那个你，接下来的我会向前看，欢迎我来到现实社会。\n","permalink":"https://mariosight.github.io/posts/%E6%97%A5%E6%9C%AC%E4%B9%8B%E8%A1%8C/","summary":"\u003ch2 id=\"日本之行\"\u003e日本之行\u003c/h2\u003e\n\u003cp\u003e原本打算作为毕业旅行的日本之行因为我的一拖再拖拖到了现在（毕业旅行给了新疆也是非常满足），也是趁着这个国庆完成了这次为期11天的旅行，东京+京都，现在来看，真是两个截然不同的地方\u003c/p\u003e","title":"日本之行"},{"content":"参考文献 Go语言动手写Web框架 - Gee第二天 上下文Context | 极客兔兔\n大佬写的非常清晰易懂，跟着走下来对整个web框架的了解变得更加深入了，也整理了一些学习心得\n整体架构 简单介绍一下整体框架的设计思路以及对应需要实现的一些功能，框架的具体实现主要是根据极客兔兔的博客以及极客时间的教学视频进行的 Web框架主要是用于解决标准库中需要频繁处理的问题或者将处理起来较为麻烦的部分进行一定程度上的封装 根据HTTP基础部分整体的框架其实很清楚了，用户这边写的是路由和对应的处理方法，框架用HandlerFunc来接受对应的handler，与对应的路由通过map进行绑定，通过不同的请求可以映射到不同的处理方法 首先要有一个整体代表服务器的抽象，Server，提供生命周期控制、路由注册接口，作为http包到Web框架的桥梁，怎么接入http包？通过http包暴露的一个接口handler来作为Web框架与http包的连接点，其次是对于路由的处理，这也是Web框架的核心之一，通过动态路由将请求映射到函数，还有一些功能如上下文，分组控制，中间件等等。\n功能简介 动态路由：对于路由来说，最重要的当然是注册与匹配了。开发服务时，注册路由规则，映射handler；访问时，匹配路由规则，查找到对应的handler。因此，Trie树需要支持节点的插入与查询。插入功能很简单，递归查找每一层的节点，如果没有匹配到当前part的节点，则新建一个。 上下文：将Handler的参数变成Context，设计上下文(Context)，封装*http.Request和http.ResponseWriter的方法，简化相关接口的调用，提供对 JSON、HTML 等返回类型的支持。 分组控制：分组控制(Group Control)是 Web 框架应提供的基础功能之一。所谓分组，是指路由的分组。如果没有路由分组，我们需要针对每一个路由进行控制。但是真实的业务场景中，往往某一组路由需要相似的处理。\n以/post开头的路由匿名可访问。 以/admin开头的路由需要鉴权。 以/api开头的路由是 RESTful 接口，可以对接第三方平台，需要三方平台鉴权。 中间件：中间件(middlewares)，就是非业务的技术类组件。Web框架本身不可能去理解所有的业务，因而不可能实现所有的功能。因此，框架需要有一个插口，允许用户自己定义功能，嵌入到框架中，仿佛这个功能是框架原生支持的一样。\n插入点在哪？使用框架的人并不关心底层逻辑的具体实现，如果插入点太底层，中间件逻辑就会非常复杂。如果插入点离用户太近，那和用户直接定义一组函数，每次在 Handler 中手工调用没有多大的优势了。 中间件的输入是什么？中间件的输入，决定了扩展能力。暴露的参数太少，用户发挥空间有限。 HTML模板：HTML模板（template）部分主要是实现两个内容：\n实现静态资源服务(Static Resource) 支持HTML模板渲染 将静态文件放在/web，filepath的值即是该目录下文件的相对地址。映射到真实的文件后，将文件返回，静态服务器就实现了。 返回文件直接交给http.FileServer处理就好了 Go语言内置了html/template为 HTML 提供了较为完整的支持。包括普通变量渲染、列表渲染、对象渲染等。 错误处理：用户不正确的参数，可能会触发某些异常，例如数组越界，空指针等。如果因为这些原因导致系统宕机，必然是不可接受的，所以错误处理机制是非常必要的，将错误处理作为一个中间件添加到框架中，向用户返回 Internal Server Error，并且在日志中打印必要的错误信息，方便进行错误定位。\nHttp库下方法的使用 这一部分主要是介绍一下net/http库下的一些基础功能使用，Body的获取，URL的查询，Form表单的输出等等\nhttp.ResponseWriter: 这是一个接口，提供了对 HTTP 响应的返回方法。你可以使用它来写入 HTTP 响应的头信息和主体内容。 *http.Request: 这是一个指向 http.Request 的指针，表示 HTTP 请求。它包含了关于 HTTP 请求的所有信息，如请求方法、URL、头信息、表单数据等 Body和GetBody 1func home(w http.ResponseWriter, r *http.Request) { 2 //body 只能读取一次 3 body, err := io.ReadAll(r.Body) 4 if err != nil { 5 fmt.Println(w, \u0026#34;读取请求体失败\u0026#34;) 6 return 7 } 8 fmt.Fprintln(w, \u0026#34;第一次请求：\u0026#34;, string(body), \u0026#34;长度为\u0026#34;, len(string(body))) 9 //尝试再次读取，不会报错，但是什么也没有读到 10 body, err = io.ReadAll(r.Body) 11 if err != nil { 12 fmt.Println(w, \u0026#34;读取请求体失败\u0026#34;) 13 return 14 } 15 fmt.Fprintln(w, \u0026#34;第二次请求：\u0026#34;, string(body), \u0026#34;长度为\u0026#34;, len(string(body))) 16 if r.GetBody == nil { 17 fmt.Fprintln(w, \u0026#34;GetBody为空\u0026#34;) 18 } else { 19 fmt.Fprintln(w, \u0026#34;GetBody不为空\u0026#34;) 20 } 21} URL 1func query(w http.ResponseWriter, r *http.Request) { 2 //查询参数是一个map，type Values map[string][]string 3 values := r.URL.Query() 4 //url.Values类型是一个映射字符串到字符串切片的映射，通常用于处理URL查询参数 5 fmt.Fprintln(w, reflect.TypeOf(values)) 6 name := values.Get(\u0026#34;name\u0026#34;) 7 fmt.Fprintln(w, \u0026#34;查询对应的值为\u0026#34;, name) 8} 9 10func wholeUrl(w http.ResponseWriter, r *http.Request) { 11 data, _ := json.Marshal(r.URL) 12 fmt.Fprintln(w, string(data)) 13} Form 1func form(w http.ResponseWriter, r *http.Request) { 2 //直接输出表单,没有对应输出 3 fmt.Fprintln(w, r.Form) 4 //需要先解析表单，才会有对应输出 5 r.ParseForm() 6 fmt.Fprintln(w, r.Form) 7} Server实现 Httpserver 是HTTP服务器的封装，实现了Server接口，提供路由注册，生命周期控制以及作为与http包结合的桥梁 ServerHTTP是整个Web框架的核心入口，在其中将实现大部分功能，Context构建，路由匹配以及业务逻辑的执行 封装与抽象：将HTTP请求的处理、响应的生成、路由的注册等逻辑都通过封装类和接口进行抽象，Context封装了请求和响应对象，HandlerBaseOnMap封装了路由逻辑，sdkHttpserver封装了服务器的启动和路由配置。\n1// Server 定义一个服务器接口 2type Server interface { 3 Routable 4 Start(address string) error 5 GET(pattern string, handlerFunc func(c *Context)) 6 POST(pattern string, handlerFunc func(c *Context)) 7} 8type Httpserver struct { 9 Name string 10 handler *Router 11} 12 13func NewHttpServer(name string) Server { 14 return \u0026amp;Httpserver{ 15 Name: name, 16 handler: NewRouter(), 17 } 18} 19func (s *Httpserver) Route(method string, pattern string, handlerFunc HandlerFunc) { 20 s.handler.Route(method, pattern, handlerFunc) 21} 22func (s *Httpserver) GET(pattern string, handlerFunc func(c *Context)) { 23 s.handler.Route(\u0026#34;GET\u0026#34;, pattern, handlerFunc) 24} 25func (s *Httpserver) POST(pattern string, handlerFunc func(c *Context)) { 26 s.handler.Route(\u0026#34;POST\u0026#34;, pattern, handlerFunc) 27} 28func (s *Httpserver) Start(address string) error { 29 //http.Handle(\u0026#34;/\u0026#34;, s) 30 //net.Listen函数创建一个监听器，监听器监听的是TCP网络地址 31 //l, err := net.Listen(\u0026#34;tcp\u0026#34;, address) 32 //if err != nil { // return err //} 33 //http.serve 启动的灵活性更强 34 //return http.Serve(l, s) 35 36 //ListenAndServe监听TCP网络地址addr，并为该网络地址的网络连接提供HTTP服务。 37 return http.ListenAndServe(address, s) 38} 39func (s *Httpserver) ServeHTTP(W http.ResponseWriter, R *http.Request) { 40 c := NewContext(W, R) 41 s.handler.handle(c) 42} 实现Handler接口 Go通过接口实现多态，Go的接口是隐式的，只要结构体上定义的“方法”在形式上（名称、参数、返回值）和 接口定义的“方法”一样，那么这个结构体就自动实现了这个接口，我们就可以使用这个接口变量来指向这个结构体对象，实际上也就是多态和继承。 Handler是一个接口，需要实现方法 ServeHTTP ，也就是说，只要传入任何实现了 ServerHTTP 接口的实例，所有的HTTP请求，都交给了该实例进行处理，ServeHTTP 方法的作用就是，解析请求的路径，查找路由映射表，如果查到，就执行注册的处理方法。 如何设计方法ServeHTTP，这个方法有2个参数，第二个参数是 Request ，该对象包含了该HTTP请求的所有的信息，比如请求地址、Header和Body等信息；第一个参数是 ResponseWriter ，利用 ResponseWriter 可以构造针对该请求的响应\n1// HandlerFunc 定义使用的请求处理程序 2type HandlerFunc func(c *Context) 3 4// 确保Httpserver实现了Server接口 5var _ Server = \u0026amp;Httpserver{} 6 7// Server 定义一个服务器接口 8type Server interface { 9 AddRoute(method string, pattern string, handlerFunc HandlerFunc) 10 Start(address string) error 11 GET(pattern string, handlerFunc HandlerFunc) 12 POST(pattern string, handlerFunc HandlerFunc) 13 NewGroup(prefix string) *RouterGroup 14 Use(middlewares ...HandlerFunc) 15} 16 17type Httpserver struct { 18 Name string 19 handler *Router 20 *RouterGroup 21 Groups []*RouterGroup 22} 23 24func NewHttpServer(name string) Server { 25 server := \u0026amp;Httpserver{ 26 Name: name, 27 handler: NewRouter(), 28 } 29 //初始化 RouterGroup，将其与server关联 30 server.RouterGroup = \u0026amp;RouterGroup{server: server} 31 // 将该 RouterGroup 添加到 server 的 groups 列表中 32 server.Groups = []*RouterGroup{server.RouterGroup} 33 return server 34} 35 36func (s *Httpserver) ServeHTTP(W http.ResponseWriter, R *http.Request) { 37 //当我们接收到一个具体请求时，要判断该请求适用于哪些中间件，在这里我们简单通过URL的前缀来判断。得到中间件列表后，赋值给c.handlers 38 var middlewares []HandlerFunc 39 for _, group := range s.Groups { 40 if strings.HasPrefix(R.URL.Path, group.prefix) { 41 middlewares = append(middlewares, group.middlewares...) 42 } 43 } 44 c := NewContext(W, R) 45 c.handlers = middlewares 46 s.handler.handle(c) 47} 上下文定义 Context随着每一个请求的出现而产生，请求的结束而销毁，和当前请求强相关的信息都应由Context承载。因此，设计Context结构，扩展性和复杂性留在了内部，而对外简化了接口。路由的处理函数，以及将要实现的中间件，参数都统一使用Context实例，Context就像一次会话的百宝箱，可以找到任何东西 我大致明白对应的意思了，实际上就是不需要自己去写对应的读取请求解析请求，而是转交给对应的框架来实现，而这部分框架就是用Context来实现的，用来简化传输的部分\n1type Context struct { 2 R *http.Request 3 W http.ResponseWriter 4} 5 6// ReadJson 读取json数据 7func (c *Context) ReadJson(resp interface{}) error { 8 body, err := io.ReadAll(c.R.Body) 9 if err != nil { 10 return err 11 } 12 err = json.Unmarshal(body, resp) 13 if err != nil { 14 return err 15 } 16 return nil 17} 18 19// WriteJson 写入json数据 20func (c *Context) WriteJson(code int, resp interface{}) error { 21 c.W.WriteHeader(code) 22 data, err := json.Marshal(resp) 23 if err != nil { 24 return err 25 } 26 _, err = c.W.Write(data) 27 if err != nil { 28 return err 29 } 30 return nil 31} 32func (c *Context) OkJson(resp interface{}) error { 33 return c.WriteJson(http.StatusOK, resp) 34} 35func (c *Context) BadRequestJson(resp interface{}) error { 36 return c.WriteJson(http.StatusBadRequest, resp) 37} 路由实现 静态路由（Map实现） 使用map定义一个handler，用来实现路由功能，允许根据HTTP方法和URL路径将请求分发到相应的处理函数 使用map来存储对应的函数，key是method与pattern的组合，将所有方法存储在哈希表中，然后通过key去匹配\n1type Handler interface { 2 http.Handler 3 Routable 4} 5//http.Handler的实现，直接继承过来 6type Handler interface { 7 ServeHTTP(ResponseWriter, *Request) 8} 创建一个实例用来进行我们自己的处理逻辑\n1type HandlerBaseOnMap struct { 2 handlers map[string]func(c *Context) 3} 整体的Handler部分\n1type Routable interface { 2 Route(method string, pattern string, handlerFunc func(c *Context)) 3} 4 5// 基于map的路由 6type Handler interface { 7 http.Handler 8 Routable 9} 10type HandlerBaseOnMap struct { 11 handlers map[string]func(c *Context) 12} 13 14func (h *HandlerBaseOnMap) Route(method string, pattern string, handlerFunc func(c *Context)) { 15 key := h.key(method, pattern) 16 h.handlers[key] = handlerFunc 17} 18 19func (h *HandlerBaseOnMap) ServeHTTP(writer http.ResponseWriter, request *http.Request) { 20 key := h.key(request.Method, request.URL.Path) 21 if handler, ok := h.handlers[key]; ok { 22 handler(NewContext(writer, request)) 23 } else { 24 writer.WriteHeader(http.StatusNotFound) 25 } 26} 27 28func (h *HandlerBaseOnMap) key(method string, pattern string) string { 29 return method + \u0026#34;#\u0026#34; + pattern 30} 31func NewHandlerBasedOnMap() Handler { 32 return \u0026amp;HandlerBaseOnMap{ 33 handlers: make(map[string]func(c *Context)), 34 } 35} 动态路由（Trie树实现） 之前用了一个非常简单的map结构存储了路由表，使用map存储键值对，索引非常高效，但是有一个弊端，键值对的存储的方式，只能用来索引静态路由。那如果我们想支持类似于/hello/:name这样的动态路由怎么办呢？所谓动态路由，即一条路由规则可以匹配某一类型而非某一条固定的路由。例如/hello/:name，可以匹配/hello/geektutu、hello/jack等。 实现的动态路由主要具备两个功能：\n参数匹配:。例如 /p/:lang/doc，可以匹配 /p/c/doc 和 /p/go/doc。 通配*。例如 /static/*filepath，可以匹配/static/fav.ico，也可以匹配/static/js/jQuery.js，这种模式常用于静态服务器，能够递归地匹配子路径 实现动态路由最常用的数据结构，被称为前缀树(Trie树)：每一个节点的所有的子节点都拥有相同的前缀。 介绍一下前缀树的概念： 前缀树本身是一个多叉树结构，树中的每个节点存储一个字符，它与普通树状数据结构最大的差异在于，存储数据的 key 不存放于单个节点中，而是由从根节点 root 出发直到来到目标节点target node之间的沿途路径组成. 基于这样的构造方式，导致拥有相同前缀的字符串可以复用公共的父节点，直到在首次出现不同字符的位置才出现节点分叉，最终形成多叉树状结构. 比如以下面search、see、seat为例，从根节点出发，先存储se，然后进行分叉，分出a和e，接着继续分叉分出r和t 当存储拥有公共前缀的内容时，可以在很大程度上节省空间提高节点利用率. 同时由于这种公共前缀的设计方式，也赋以了Trie树能够支持前缀频率统计以及前缀模糊匹配的功能 分组控制 这里实现的分组控制也是以前缀来区分，并且支持分组的嵌套。例如/post是一个分组，/post/a和/post/b可以是该分组下的子分组。作用在/post分组上的中间件(middleware)，也都会作用在子分组，子分组还可以应用自己特有的中间件。中间件可以给框架提供无限的扩展能力，应用在分组上，可以使得分组控制的收益更为明显，而不是共享相同的路由前缀这么简单。例如/admin的分组，可以应用鉴权中间件；/分组应用日志中间件。 首先是一开始结构体的定义\n1//server作为顶层的分层，要在server里添加RouterGroup 2type Httpserver struct { 3 Name string 4 handler *Router 5 *RouterGroup 6 Groups []*RouterGroup 7} 8 9//在Group中，保存一个指针，指向server 10type RouterGroup struct { 11 prefix string //所有该组中的路由都将共享这个前缀 12 middlewares []HandlerFunc //支持中间件 13 parent *RouterGroup //支持嵌套 14 server *Httpserver //所有组共享一个server实例 15} 16 17func NewHttpServer(name string) Server { 18 server := \u0026amp;Httpserver{ 19 Name: name, 20 handler: NewRouter(), 21 } 22 //初始化 RouterGroup，将其与server关联 23 server.RouterGroup = \u0026amp;RouterGroup{server: server} 24 // 将该 RouterGroup 添加到 server 的 groups 列表中 25 server.Groups = []*RouterGroup{server.RouterGroup} 26 return server 27} Group的构造函数，用来构造RouterGroup\n1// NewGroup 以创建新的 RouterGroup 2// 所有组共享同一个 server 实例 3func (g *RouterGroup) NewGroup(prefix string) *RouterGroup { 4 server := g.server 5 //创建一个新的Group 6 newGroup := \u0026amp;RouterGroup{ 7 prefix: g.prefix + prefix, // 新组的前缀是父组前缀加上新前缀 8 parent: g, 9 server: server, //所有组共享同一个 server 实例 10 } 11 server.Groups = append(server.Groups, newGroup) 12 return newGroup 13} 后面就是将路由相关的函数都通过RouterGroup来实现\n1//将和路由有关的函数，都交给RouterGroup实现 2func (g *RouterGroup) AddRoute(method string, pattern string, handlerFunc HandlerFunc) { 3 //将路由组的前缀（g.prefix）和当前路由的模式（pattern）拼接在一起，形成完整的路由路径 4 pattern = g.prefix + pattern 5 //日志输出，非常直观 6 log.Printf(\u0026#34;Route %4s - %s\u0026#34;, method, pattern) 7 g.server.handler.AddRoute(method, pattern, handlerFunc) 8} 9func (g *RouterGroup) GET(pattern string, handlerFunc HandlerFunc) { 10 g.AddRoute(http.MethodGet, pattern, handlerFunc) 11} 12 13func (g *RouterGroup) POST(pattern string, handlerFunc HandlerFunc) { 14 g.AddRoute(http.MethodPost, pattern, handlerFunc) 15} 然后是在主函数里面进行分组\n1 v1 := server.NewGroup(\u0026#34;/v1\u0026#34;) 2 { 3 v1.GET(\u0026#34;/hello\u0026#34;, Hello) 4 v1.GET(\u0026#34;/signup\u0026#34;, Signup) 5 } 6 v2 := server.NewGroup(\u0026#34;/v2\u0026#34;) 7 { 8 v2.GET(\u0026#34;/hello/:name\u0026#34;, Param) 9 v2.POST(\u0026#34;/login\u0026#34;, Login) 10 } 中间件 中间件的定义与路由映射的Handler一致，处理的输入是Context对象。插入点是框架接收到请求初始化Context对象后，允许用户使用自己定义的中间件做一些额外的处理，例如记录日志等，以及对Context进行二次加工。另外通过调用(*Context).Next()函数，中间件可等待用户自己定义的Handler处理结束后，再做一些额外的操作，例如计算本次处理所用时间等。即所实现的中间件支持用户在请求被处理的前后，做一些额外的操作。 之前的框架设计是这样的，当接收到请求后，匹配路由，该请求的所有信息都保存在Context中，中间件也不例外，接收到请求后，应查找所有应作用于该路由的中间件，保存在Context中，依次进行调用。\n1// Logger 当在中间件中调用Next方法时，控制权交给了下一个中间件，直到调用到最后一个中间件，然后再从后往前，调用每个中间件在Next方法之后定义的部分 2func Logger() HandlerFunc { 3\treturn func(c *Context) { 4\t// 开始计时 5\tt := time.Now() 6\t// 处理请求 7\tc.Next() 8\t// 计算处理时间 9\tlog.Printf(\u0026#34;[%d] %s in %v\u0026#34;, c.StatusCode, c.Req.RequestURI, time.Since(t)) 10\t} 11} HTML模板渲染 处理静态文件\n1// createStaticHandler 静态文件的处理程序 2func (g *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc { 3 absolutePath := path.Join(g.prefix, relativePath) 4 fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) 5 return func(c *Context) { 6 file := c.Param(\u0026#34;filepath\u0026#34;) 7 // 检查文件是否存在 8 if _, err := fs.Open(file); err != nil { 9 c.Status(http.StatusNotFound) 10 return 11 } 12 fileServer.ServeHTTP(c.W, c.R) 13 } 14} 15 16// Static 提供静态文件 17// 用户可以将磁盘上的某个文件夹root映射到路由relativePath 18func (g *RouterGroup) Static(relativePath string, root string) { 19 handler := g.createStaticHandler(relativePath, http.Dir(root)) 20 urlPattern := path.Join(relativePath, \u0026#34;/*filepath\u0026#34;) 21 g.GET(urlPattern, handler) 22} HTML模板渲染\n1Engine struct { 2\t*RouterGroup 3\trouter *router 4\tgroups []*RouterGroup // store all groups 5\thtmlTemplates *template.Template // for html render 6\tfuncMap template.FuncMap // for html render 7} 8 9func (engine *Engine) SetFuncMap(funcMap template.FuncMap) { 10\tengine.funcMap = funcMap 11} 12 13func (s *Httpserver) LoadHTMLGlob(pattern string) { 14 //因为运行配置里写的工作目录是LearnGo，修改一下变成LearnGo\\web就可以了 15 s.htmlTemplates = template.Must(template.New(\u0026#34;\u0026#34;).Funcs(s.funcMap).ParseGlob(pattern)) 16} 错误处理 使用error与panic用来进行错误处理，error一般用于表达可以被处理的错误，panic一般用于表达非常严重不能恢复的错误，遇事不决选error errors包下的方法：New创建一个新的error，Is判断是不是特定的某个error，As类型转换为特定的error，Unwrap解除包装，返回被包装的error\n1func ErrorsPkg() { 2 err := \u0026amp;MyError{} 3 //返回的是一个包装好的error 4 wrappedErr := fmt.Errorf(\u0026#34;this is an wrapped error %w\u0026#34;, err) 5 //再解出来 6 if errors.Is(err, errors.Unwrap(wrappedErr)) { 7 fmt.Println(\u0026#34;unwrapped\u0026#34;) 8 } 9 //判断是否是包装好的error 10 if errors.Is(wrappedErr, err) { 11 fmt.Println(\u0026#34;wrapped is err\u0026#34;) 12 } 13 copyErr := \u0026amp;MyError{} 14 //尝试将wrappedErr转换成为MyError 15 if errors.As(wrappedErr, \u0026amp;copyErr) { 16 fmt.Println(\u0026#34;convert error\u0026#34;) 17 } 18} 延迟（defer）从下往上执行，先进后出，类似于栈\n1defer func() { 2 fmt.Println(\u0026#34;aaa\u0026#34;) 3}() 4defer func() { 5 fmt.Println(\u0026#34;bbb\u0026#34;) 6}() 7defer func() { 8 fmt.Println(\u0026#34;ccc\u0026#34;) 9}() 错误处理也可以作为一个中间件，增强整体框架的能力\n1func Recovery() HandlerFunc { 2\treturn func(c *Context) { 3\tdefer func() { 4\tif err := recover(); err != nil { 5\tmessage := fmt.Sprintf(\u0026#34;%s\u0026#34;, err) 6\tlog.Printf(\u0026#34;%s\\n\\n\u0026#34;, trace(message)) 7\tc.String(http.StatusInternalServerError, \u0026#34;Internal Server Error\u0026#34;) 8\t} 9\t}() 10 11\tc.Next() 12\t} 13} ","permalink":"https://mariosight.github.io/posts/go%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%9E%84%E9%80%A0%E4%B8%80%E4%B8%AAweb%E6%A1%86%E6%9E%B6/","summary":"\u003ch4 id=\"参考文献\"\u003e参考文献\u003c/h4\u003e\n\u003cp\u003e\u003ca href=\"https://geektutu.com/post/gee-day2.html\"\u003eGo语言动手写Web框架 - Gee第二天 上下文Context | 极客兔兔\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e大佬写的非常清晰易懂，跟着走下来对整个web框架的了解变得更加深入了，也整理了一些学习心得\u003c/p\u003e","title":"Go语言之构造一个Web框架"},{"content":"ndnSIM 搭建以及使用 安装准备 1sudo apt install build-essential libsqlite3-dev libboost-all-dev libssl-dev git python-setuptools castxml 2sudo apt install python-dev python-pygraphviz python-kiwi python-gnome2 ipython libcairo2-dev python3-gilibgirepository1.0-dev python-gi python-gi-cairo gir1.2-gtk-3.0 gir1.2-goocanvas-2.0 python-pip 3pip install pygraphviz pycairo PyGObject pygccxml 4sudo apt-get install graphviz libgraphviz-dev graphviz-dev pkg-config 5pip install pygraphviz 下载源码 现在任意文件创建个 ndnsim 文件夹\n1mkdir ndnSIM2.7 然后进入该文件夹下，进行源码的下载\n1cd ndnSIM2.7 2git clone https://github.com/named-data-ndnSIM/ns-3-dev.git ns-3 3git clone https://github.com/named-data-ndnSIM/pybindgen.git pybindgen 4git clone https://github.com/named-data-ndnSIM/ndnSIM.git ns-3/src/ndnSIM 修改对应版本号 这里的命令依靠 git 实现，能到这一步肯定都安装好了，就不多说了\n首先进入到 ndnSIM 的核心源码地带\n1cd ns-3/src/ndnSIM 通过 git checkout 修改版本号\n1git checkout ndnSIM-2.7 也可以通过 git tag 查看所有的版本号，然后修改为想要安装的版本\n然后安装 NFD 和 ndn-cxx 模块\n1git submodule update --init 接下来进入到 ndnSIM2.7/ns-3 文件下进行版本的修改\n1git checkout ndnSIM-ns-3.29 接下对 pybindgen 的版本进行修改\n1cd ndnSIM2.7/pybindgen 2git checkout 0.19.0 然后在此目录下安装安装该 python 模块\n1sudo python setup.py install 可视化出现问题 1PyViz visualizer : not enabled (Python Bindings are needed but not enabled) 2Python Bindings : not enabled (PyBindGen missing) 这个问题其实很好解决，执行一下\n1pip install pybindgen 注:以下代码若不知道存放路径,运行 pip show pybindgen 即可，或者重新运行一遍上面的代码\n1./waf -d debug configure --with-pybindgen=存放路径//ex：./waf -d debug configure --with-pybindgen=/home/antl417/anaconda3/lib/python3.8/site-packages 1#在此虚拟机中应该是下面的 2./waf -d debug configure --with-pybindgen=/home/yin/.local/lib/python3.8/site-packages 然后执行\n1./waf configure --with-pybindgen=/home/yin/.local/lib/python3.8/site-packages --enable-examples 2./waf 3./waf --run ndn-simple --vis 正常情况直接编译完成，出现错误的话请看下面的修改提示\n进入到可视化模块下将‘file=’删除\n1cd ndnSIM2.7/ns-3/src/visualizer/visualizer 进入 base.py 文件，修改保存即可 成功编译： 第二种错误可能出现在运行时加上–vis 可视化模块时，如下图所示 这种情况下，还是进入刚才的那个文件夹，修改 hub.py 文件\n将 from.import 注释，修改为 import core，再次运行就可以了\n第三个错误也可能是出现在加上-vis 的情况，如下图所示： 安装 gi.cairo 即可解决：\n1sudo apt-get install gi.cairo 成功启动 然后执行\n1./waf configure --with-pybindgen=/home/yin/.local/lib/python3.8/site-packages --enable-examples 2./waf 3./waf --run ndn-simple --vis 启动之后的 simple 如下图所示： 参考教程 使用教程：ndnSIM 的使用教程-CSDN 博客\n安装流程：在 Ubuntu 安装 ndnSIM_ndnsim20 安装教程-CSDN 博客\n可视化出现问题解决方法：NS3 可视化问题及解决办法_ns3.34 python binding-CSDN 博客\n","permalink":"https://mariosight.github.io/posts/ndnsim%E4%BB%BF%E7%9C%9F%E5%AE%89%E8%A3%85%E4%BB%A5%E5%8F%8A%E4%BD%BF%E7%94%A8/","summary":"\u003ch3 id=\"ndnsim-搭建以及使用\"\u003endnSIM 搭建以及使用\u003c/h3\u003e\n\u003ch5 id=\"安装准备\"\u003e安装准备\u003c/h5\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003esudo apt install build-essential libsqlite3-dev libboost-all-dev libssl-dev git python-setuptools castxml\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e2\u003c/span\u003e\u003cspan class=\"cl\"\u003esudo apt install python-dev python-pygraphviz python-kiwi python-gnome2 ipython libcairo2-dev python3-gilibgirepository1.0-dev python-gi python-gi-cairo gir1.2-gtk-3.0 gir1.2-goocanvas-2.0 python-pip\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e3\u003c/span\u003e\u003cspan class=\"cl\"\u003epip install pygraphviz pycairo PyGObject pygccxml\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e4\u003c/span\u003e\u003cspan class=\"cl\"\u003esudo apt-get install graphviz libgraphviz-dev graphviz-dev pkg-config\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e5\u003c/span\u003e\u003cspan class=\"cl\"\u003epip install pygraphviz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch5 id=\"下载源码\"\u003e下载源码\u003c/h5\u003e\n\u003cp\u003e现在任意文件创建个 ndnsim 文件夹\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003emkdir ndnSIM2.7\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e然后进入该文件夹下，进行源码的下载\u003c/p\u003e","title":"ndnSIM仿真安装以及使用"},{"content":"介绍 Go 语言是一种静态、编译型、并发型的编程语言。它具有强类型、垃圾回收、快速编译等特性，属于 C 系语言的一种 编译型 vs 解释型\n编译型语言\n优点：编译器一般会有预编译的过程对代码进行优化。因为编译只做一次，运行时不需要编译，所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。\n缺点：编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码，不同的操作系统之间移植就会有问题，需要根据运行的操作系统环境编译不同的可执行文件。\n解释型语言\n优点：有良好的平台兼容性，在任何环境中都可以运行，前提是安装了解释器（虚拟机）灵活，修改代码的时候直接修改就可以，可以快速部署，不用停机维护。\n缺点：每次运行的时候都要解释一遍，性能上不如编译型语言。\n混合型语言\n既然编译型和解释型各有缺点就会有人想到把两种类型整合起来，取其精华去其糟粕。就出现了半编译型语言。比如 C#,C#在编译的时候不是直接编译成机器码而是中间码，.NET 平台提供了中间语言运行库运行中间码，中间语言运行库类似于 Java 虚拟机。.net 在编译成 IL 代码后，保存在 dll 中，首次运行时由 JIT 在编译成机器码缓存在内存中，下次直接执行。\n源码文件 1、命令源码文件：\n声明自己属于 main 代码包、包含无参数声明和结果声明的 main 函数。命令源码文件被安装以后，GOPATH 如果只有一个工作区，那么相应的可执行文件会被存放当前工作区的 bin 文件夹下；如果有多个工作区，就会安装到 GOBIN 指向的目录下。\n2、库源码文件\n库源码文件就是不具备命令源码文件上述两个特征的源码文件。存在于某个代码包中的普通的源码文件。 库源码文件被安装后，相应的归档文件（.a 文件）会被存放到当前工作区的 pkg 的平台相关目录下。\n3、测试源码文件\n名称以 test.go 为后缀的代码文件，并且必须包含 Test 或者 Benchmark 名称前缀的函数。\n输入输出 fmt 中输入函数 Scanln\n1. 使用\u0026#34;\u0026amp;\u0026#34;获取变量的内存地址(即取变量内存地址的运算符)，通过键盘输入为变量指向的内存地址赋初值。 2. fmt.Scanln是一个阻塞的函数，如果它获取不到数据就会一直阻塞哟。 3. fmt.Scanln可以接收多个参数，用户输入参数默认使用空格或者回车换行符分割输入设备传入的参数，直到接收所有的参数为止 fmt 中的输出函数 Println（ln 表示自动换行）\n1. Println格式使用其操作数的默认格式，并写入标准输出。 2. 始终在操作数之间添加空格，并追加换行符。 3. 它返回写入的字节数和遇到的任何写入错误。 注意，如果需要输出如%s,%T 这种类型，则需要使用 Printf，这个跟 C 是类似的\n1fmt.Printf(\u0026#34;type: %T\u0026#34;,str) 数据结构 数据类型 数类型\nuint8 无符号 8 位整型 (0 到 255)\nuint16 无符号 16 位整型 (0 到 65535)\nuint32 无符号 32 位整型 (0 到 4294967295)\nuint64 无符号 64 位整型 (0 到 18446744073709551615)\nint8 有符号 8 位整型 (-128 到 127)\nint16 有符号 16 位整型 (-32768 到 32767)\nint32 有符号 32 位整型 (-2147483648 到 2147483647)\nint64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)\nfloat32 和 float64 范围分别为 3.4e38 和 1.8e308\ncomplex64 和 complex128\n字符类型\nuint8 类型，或者叫 byte 型，代表一个 ASCII 码字符。\nrune 类型，代表一个 UTF-8 字符。\nrune 类型是 int32 的别名，用于表示一个 Unicode 码点（Unicode code point）。每个rune对应一个 Unicode 字符，可以表示任何语言的字符（包括中文、表情符号等） Go 的字符串默认以 UTF-8 编码存储。当遍历字符串时，若用 for range，会自动按 rune 为单位迭代，而非字节：\n1s := \u0026#34;Hello 世界\u0026#34; 2for _, r := range s { 3// r 的类型是 rune 4fmt.Printf(\u0026#34;%c \u0026#34;, r) 5} 6// 输出: H e l l o 世 界 这里是对于 range 的解释：golang 中使用 for range 遍历字符串时常遇到的 bug，byte 和 rune 类型，源码分析 - Harven - 博客园\n数组和切片 数组（Array）和切片（Slice）的区别其实是数组在定义的时候已经规定好了长度，而切片并没有规定长度 数组是需要指定初始化长度（或者叫做容量）的序列，在定义时可以直接设置初始值，如果不设置，初始值为默认值 切片是一个拥有相同类型元素的可变长度的序列，可以通过 append 函数添加元素\n代码书写\n切片可以用内置函数 make 来创建，这也是你创建动态数组的方式。 make 函数会分配一个元素为零值的数组并返回一个引用了它的切片：\n1a := make([]int, 5) // len(a)=5 要指定它的容量，需向 make 传入第三个参数\n1arr := [5]int{0, 1, 2, 3, 4} //创建一个数组 2s1 := arr[0:3] //创建一个子切片,[0:3)左闭右开 3s2 := make([]int, 3, 4) //创建一个切片，长度为3，初始元素为0，容量为4 4s1 = append(s1, 8) //追加一个元素 5s2 = append(s2, 1) //追加一个元素 6//切片比较好的创建方式为make([]int,0,cap) 子切片\n通过[start:end]来获得子切片，arr[start:end]获取的是[start,end)之间的元素，左闭右开 arr[:end]获得[0,end)之间的元素 arr[start:]获得[start,len(arr))之间的元素 子切片与原切片共享底层数组\n理解 slice\n实际上是 Go 中特殊的 ArrayList，但是没有插入和删除的操作，只有 append 操作，下面稍微写了一下对于 slice 的 insert 和 delete，在这其中使用了 copy 函数：当我们在 Go 语言中需要将一个切片的内容复制到另一个切片时，可以使用内置的 copy()函数，copy()函数用于将源切片中的元素复制到目标切片中。 slice 的零值是 nil，通过[]int 这种方式来创建切片时首先会创建一个对应的底层数组\n1//实现指定位置插入元素 2func insert(slice []int,value int,index int) []int { 3\tslice = append(slice, 0) //扩展一个空间 4\tcopy(slice[index+1:],slice[index:]) //向后移动 5\tslice[index] = value //插入元素 6\treturn slice 7} 8//实现指定位置删除元素 9func delete(slice []int,index int) []int { 10\tcopy(slice[index:],slice[index+1:]) //向前移动 11\treturn slice[:len(slice)-1] //删除最后一个元素 12} 练习切片的一个小程序，通过灰度图展示公式\n1package main 2 3import \u0026#34;golang.org/x/tour/pic\u0026#34; 4 5func Pic(dx, dy int) [][]uint8 { 6\tans := make([][]uint,dy) 7\tfor y:=0;y\u0026lt;dy;y++{ 8\trow := make([]uint8,dx) 9\tfor x:=0;x\u0026lt;dx;x++{ 10\trow[x] = uint8(x%(y+1)) 11\t} 12\tans[y]=row 13\t} 14\treturn ans 15} 16 17func main() { 18\tpic.Show(Pic) 19} 字符串 字符串用\u0026quot;abc\u0026quot;表示，单个字符用'a'表示 字符串可以通过索引访问字符\n1s:=\u0026#34;abcde\u0026#34; 2print(s[0]) 字符串可以通过切片访问连续字符\n1s:=\u0026#34;abcde\u0026#34; 2print(s[0:3]) 统计字符串的个数要用特殊的方法\n1s:=\u0026#34;abcde\u0026#34; 2print(utf8.RuneCountInString(s))} 将字符编码转换为字符\n1s:=\u0026#34;abcde\u0026#34; 2print(string(s[1])) 遍历字符串的每一个字符\n1s:=\u0026#34;我爱你中国\u0026#34; 2cs:=[]rune(s) 3for_,ch:=range cs{ 4\tfmt.Println(string(ch)) 5} 使用 buffer 累加字符串\n1var bf bytes.Buffer 2for i:=0;i\u0026lt;10;i++{ 3\tfmt.Fprintf(\u0026amp;bf,\u0026#34;a%d\u0026#34;,i) 4} 5s:=bf.String() 6fmt.Println(s) 指针 \u0026amp;符号会【生成】一个指向其作用对象的指针。 *符号表示指针指向的【底层的值】 1package main 2import \u0026#34;fmt\u0026#34; 3func main() { 4\ti, j := 42, 2701 5\tp := \u0026amp;i // p为i的指针 6\tfmt.Println(*p) // 通过指针显示的是i的值 7\t*p = 21 // 通过指针修改的是i的值 8\tfmt.Println(i) 9\tp = \u0026amp;j // 将p改为j的指针 10\t*p = *p / 37 // 通过指针操作的是j的值，除以37的结果重新通过指针赋给j 11\tfmt.Println(j) 12} 结构体定义 定义类型 定义类型的不同方式，使用也不相同\n1// BigServer type A=B 2type BigServer = server 3// SmellServer type A B 4type SmellServer server 5 6service1 := SmellServer{} 7service2 := BigServer{} 8server(service1).open() 9service2.open() 初始化 Go 没有构造函数，所以 new 直接分配内存然后把内存都置 0\n1man1 := Person{name: \u0026#34;李四\u0026#34;} 2man1.Walk(man1.name) 3man2 := new(Person) 4man2.name = \u0026#34;王五\u0026#34; 5man2.Walk(man2.name) 方法接收器 用于定义结构体对应的方法（也可以为非结构体类型声明方法） 只能为在同一个包中定义的接收者类型声明方法，而不能为其它别的包中定义的类型（包括 int 之类的内置类型）声明方法\n1// 结构体接收器 2func (p Person) ChangeName(newName string) { 3 p.name = newName 4} 5// 指针接收器 6func (p *Person) ChangeAge(newAge int) { 7 p.age = newAge 8} 9// 结构体接收器，内容不会改变 10man1.ChangeName(\u0026#34;赵六\u0026#34;) 11// 指针接收器，内容会改变，我直接遇事不决用指针 12man1.ChangeAge(30) 13fmt.Println(man1) 哈希表 Go 之旅中的一个练习，用的是比较简单的 map，实现 WordCount。它应当返回一个映射，其中包含字符串 s 中每个“单词”的个数。 函数 wc.Test 会为此函数执行一系列测试用例，并输出成功还是失败\n1package main 2 3import ( 4\t\u0026#34;golang.org/x/tour/wc\u0026#34; 5\t\u0026#34;strings\u0026#34; 6) 7func WordCount(s string) map[string]int { 8\tv := strings.Fields(s) //Field函数直接分割 9\tn := len(v) 10\tx := make(map[string]int) 11\tfor i:=0;i\u0026lt;n;i++ { 12\tx[v[i]]++ 13\t} 14\treturn x 15} 16 17func main() { 18\twc.Test(WordCount) 19} 控制结构 for 类似 while 的无限循环 fori 一般的按照下标循环 forr 最为特殊的 range 遍历，可以用于 map 遍历\n1for i := 0; i \u0026lt; len(arr); i++ { 2fmt.Printf(\u0026#34; index = %d,number = %d \\n\u0026#34;, i, arr[i]) 3} 4for index, value := range arr{ 5fmt.Printf(\u0026#34; index = %d,number = %d \\n\u0026#34;, index, value) 6} if-else：没什么可以记录的，跟其他的语言一样的\n函数调用 Go 语言选择了传值的方式，无论是传递基本类型、结构体还是指针，都会对传递的参数进行拷贝\n无需声明原型 支持不定变参 支持多返回值 支持命名返回参数 函数也是一种类型，一个函数可以复制给变量；可以作为参数传递给其他函数 不支持嵌套（一个包不能有重名的函数） 不支持重载 不支持默认参数 整数与数组类型 如下所示的函数 myFunction 接收了两个参数，整型变量 i 和数组 arr，这个函数会将传入的两个参数的地址打印出来，在最外层的主函数也会在 myFunction 函数调用前后分别打印两个参数的地址，最后的结果 main 函数中的参数地址与 myFunction 中的地址完全不同，所以能够证明Go 语言的整型和数组类型都是值传递的\n1func myFunction1(i int, arr [2]int) { 2\tfmt.Printf(\u0026#34;in my_funciton - i=(%d, %p) arr=(%v, %p)\\n\u0026#34;, i, \u0026amp;i, arr, \u0026amp;arr) 3} 4 5func main() { 6\ti := 30 7\tarr := [2]int{66, 77} 8\tfmt.Printf(\u0026#34;before calling - i=(%d, %p) arr=(%v, %p)\\n\u0026#34;, i, \u0026amp;i, arr, \u0026amp;arr) 9\tmyFunction1(i, arr) 10\tfmt.Printf(\u0026#34;after calling - i=(%d, %p) arr=(%v, %p)\\n\u0026#34;, i, \u0026amp;i, arr, \u0026amp;arr) 11} 结构体与指针类型 传递结构体时：会拷贝结构体中的全部内容 传递结构体指针时：会拷贝结构体指针 1func myFunction2(a MyStruct, b *MyStruct) { 2 a.i = 31 3 b.i = 41 4 fmt.Printf(\u0026#34;in my_function - a=(%d, %p) b=(%v, %p)\\n\u0026#34;, a, \u0026amp;a, b, \u0026amp;b) 5} 6func main() { 7\ta := MyStruct{i: 30} 8\tb := \u0026amp;MyStruct{i: 40} 9\tfmt.Printf(\u0026#34;before calling - a=(%d, %p) b=(%v, %p)\\n\u0026#34;, a, \u0026amp;a, b, \u0026amp;b) 10\tmyFunction2(a, b) 11\tfmt.Printf(\u0026#34;after calling - a=(%d, %p) b=(%v, %p)\\n\u0026#34;, a, \u0026amp;a, b, \u0026amp;b) 12} 方法与函数 方法 (func (c *Context) String)： 方法的第一个参数是一个接收者（receiver），即 c *Context，表示该方法属于 Context 类型。 方法的调用方式是通过类型的实例来调用，例如 context.String(...)，其中 context 是 Context 类型的一个实例。 表达的是 String 操作是 Context 类型的一部分，是 Context 对象的一个行为。 更适合表示“某种类型的实例应该具备的行为”。 函数 (func String(c *Context))： 一个普通的函数，它的参数是 c *Context，表示函数接收一个 *Context 类型的参数。 函数的调用方式是直接调用，并传入参数，例如 String(context, ...)，参数列表名称在前，类型在后，可以有多返回值，返回值也可以有名称 是独立于 Context 类型的一个函数，只是恰好接受一个 Context 类型的参数。 更适合表示“一般性的操作或工具函数”，而不是某种类型的固有行为。 方法与指针重定向 1func (v *Vertex) Scale(f float64) { 2\tv.X = v.X * f 3\tv.Y = v.Y * f 4} 5 6func ScaleFunc(v *Vertex, f float64) { 7\tv.X = v.X * f 8\tv.Y = v.Y * f 9} 比较前两个程序，你大概会注意到带指针参数的函数必须接受一个指针：\n1var v Vertex 2ScaleFunc(v, 5) // 编译错误！ 3ScaleFunc(\u0026amp;v, 5) // OK 而接收者为指针的的方法被调用时，接收者既能是值又能是指针：\n1var v Vertex 2p := \u0026amp;Vertex{4, 3} 3p.Scale(3) //OK 4(*p).Scale(8) //OK 对于语句 v.Scale(5) 来说，即便 v 是一个值而非指针，带指针接收者的方法也能被直接调用。 也就是说，由于 Scale 方法有一个指针接收者，为方便起见，Go 会将语句 v.Scale(5) 解释为 (\u0026amp;v).Scale(5)。 反之同理，接受一个值作为参数的函数必须接受一个指定类型的值，而以值为接收者的方法被调用时，接收者既能为值又能为指针，这种情况下，方法调用 p.Abs() 会被解释为 (*p).Abs()，其实就是遇到不同情况自动转换。\n接口 接口是计算机系统中多个组件共享的边界，不同的组件能够在边界上交换信息。如下图所示，接口的本质是引入一个新的中间层，调用方可以通过接口与具体实现分离，解除上下游的耦合，上层的模块不再需要依赖下层的具体模块，只需要依赖一个约定好的接口。 在 Java 中：实现接口需要显式地声明接口并实现所有方法，在接口中不仅可以定义方法签名，还可以定义变量，这些定义的变量可以直接在实现接口的类中使用\n1public interface MyInterface { 2 public String hello = \u0026#34;Hello\u0026#34;; 3 public void sayHello(); 4} 5 6public class MyInterfaceImpl implements MyInterface { 7 public void sayHello() { 8 System.out.println(MyInterface.hello); 9 } 10} 在 Go 中：实现接口的所有方法就隐式地实现了接口，但是在接口中我们只能定义方法签名，不能包含成员变量\n1type error interface { 2\tError() string 3} 4type RPCError struct { 5\tCode int64 6\tMessage string 7} 8func (e *RPCError) Error() string { 9\treturn fmt.Sprintf(\u0026#34;%s, code=%d\u0026#34;, e.Message, e.Code) 10} Java 中的类必须显式地声明实现的接口，与 Java 不同的是 Go 语言中接口的实现都是隐式的，我们只需要实现 Error() string 方法就实现了 error 接口\n接口方法 接口就是规定了一个需要实现的方法列表，在 Go 语言中一个类型只要实现了接口中规定的所有方法，那么我们就称它实现了这个接口 我们定义的 Singer 接口类型，它包含一个 Sing 方法。\n1type Singer interface { 2\tSing() 3} 然后有一个 Bird 结构体\n1type Bird struct {} 因为 Singer 接口只包含一个 Sing 方法，所以只需要给 Bird 结构体添加一个 Sing 方法就可以满足 Singer 接口的要求。\n1// Sing Bird类型的Sing方法 2func (b Bird) Sing() { 3\tfmt.Println(\u0026#34;汪汪汪\u0026#34;) 4} 为什么要用接口呢，因为有时不止一个类型需要实现这个方法，可能有多个结构体需要 Sing() 这个方法，这时就可以使用接口类型，把所有会叫的动物当成 Singer 类型来处理\n1package main 2 3import \u0026#34;fmt\u0026#34; 4 5type Sayer interface { 6 Say() 7} 8type Cat struct { 9} 10 11type Dog struct{} 12 13func (c *Cat) Say() { 14 fmt.Println(\u0026#34;cat miao\u0026#34;) 15} 16func (d *Dog) Say() { 17 fmt.Println(\u0026#34;dog woof\u0026#34;) 18} 19func Hungry(s Sayer) { 20 s.Say() 21} 22 23func main() { 24 var c Cat 25 var d Dog 26 Hungry(\u0026amp;c) 27 Hungry(\u0026amp;d) 28} Stringer 是 fmt 包中定义的 Stringer 是最普遍的接口之一，是一个可以用字符串描述自己的类型，任何实现了 String() string 方法的类型都隐式满足该接口。fmt 包（还有很多包）都通过此接口来打印值，可以规范化对应值的输出\n1package main 2 3import \u0026#34;fmt\u0026#34; 4 5type IPAddr [4]byte 6 7// TODO: 为 IPAddr 添加一个 \u0026#34;String() string\u0026#34; 方法。 8func (I IPAddr) String() string{ 9\treturn fmt.Sprintf(\u0026#34;%v.%v.%v.%v\u0026#34;,I[0],I[1],I[2],I[3]) 10} 11func main() { 12\thosts := map[string]IPAddr{ 13\t\u0026#34;loopback\u0026#34;: {127, 0, 0, 1}, 14\t\u0026#34;googleDNS\u0026#34;: {8, 8, 8, 8}, 15\t} 16\tfor name, ip := range hosts { 17\tfmt.Printf(\u0026#34;%v: %v\\n\u0026#34;, name, ip) 18\t} 19} 与 fmt.Stringer 类似，error 类型是一个内建接口\n1type error interface { 2 Error() string 3} 类似的还有，sort 接口，json 序列化，io 读写，以及 http 的请求处理 rot13Reader 的实现，一个接口组合的实现\n1package main 2 3import ( 4\t\u0026#34;io\u0026#34; 5\t\u0026#34;os\u0026#34; 6\t\u0026#34;strings\u0026#34; 7) 8 9type rot13Reader struct { 10\tr io.Reader 11} 12 13func rot13(c byte) byte { 14\tswitch { 15\tcase c \u0026gt;= \u0026#39;A\u0026#39; \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;Z\u0026#39;: 16\treturn \u0026#39;A\u0026#39; + (c-\u0026#39;A\u0026#39;+13)%26 17\tcase c \u0026gt;= \u0026#39;a\u0026#39; \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;z\u0026#39;: 18\treturn \u0026#39;a\u0026#39; + (c-\u0026#39;a\u0026#39;+13)%26 19\tdefault: 20\treturn c 21\t} 22} 23 24func (r *rot13Reader) Read(b []byte) (n int, err error) { 25\t// 要调用底层的Read 26 n, err = r.r.Read(b) 27 for i := 0; i \u0026lt; n; i++ { 28 b[i] = rot13(b[i]) 29 } 30 return n, err 31} 32 33func main() { 34\ts := strings.NewReader(\u0026#34;Lbh penpxrq gur pbqr!\u0026#34;) 35\tr := rot13Reader{s} 36\tio.Copy(os.Stdout, \u0026amp;r) 37} 图像生成器\n1package main 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5\t\u0026#34;golang.org/x/tour/pic\u0026#34; 6\t\u0026#34;image\u0026#34; 7\t\u0026#34;image/color\u0026#34; 8) 9 10type Image struct{ 11\tWidth int 12\tHeight int 13} 14// Bounds image.Rectangle 15func (img Image)Bounds() image.Rectangle{ 16\treturn image.Rect(0,0,img.Width,img.Height) 17} 18 19// Color color.RGBModel 20func (img Image) ColorModel() color.Model{ 21\treturn color.RGBAModel 22} 23// At color.Color 24func (img Image) At(x,y int) color.Color{ 25\tv := uint8(x^y) 26\treturn color.RGBA{v,v,255,255} 27} 28 29func main() { 30\tm := Image{256,256} 31\tpic.ShowImage(m) 32} 错误（error） Go 程序使用 error 值来表示错误状态。通常函数会返回一个 error 值，调用它的代码应当判断这个错误是否等于 nil 来进行错误处理。\n1package main 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5\t\u0026#34;math\u0026#34; 6) 7type ErrNegativeSqrt struct{ 8\tValue float64 9} 10 11func Sqrt(x float64) (float64, error) { 12\tif x\u0026lt;0 { 13\treturn x , ErrNegativeSqrt{Value: x} 14\t}else { 15\treturn math.Sqrt(x),nil 16\t} 17} 18// 重写Error方法，输出更多错误提示信息 19func (e ErrNegativeSqrt) Error() string{ 20\treturn fmt.Sprintf(\u0026#34;cannot Sqrt negative number: %v\u0026#34;,float64(e)) 21} 22 23func main() { 24\tfmt.Println(Sqrt(2)) 25\tfmt.Println(Sqrt(-2)) 26} 接口值 接口类型的值可以是任意一个实现了该接口的类型值，所以接口值除了需要记录具体值之外，还需要记录这个值属于的类型。也就是说接口值由“类型”和“值”组成。 接口值（Interface Value） 本质上是一个动态容器，它可以持有任意具体类型的值，并在运行时记录类型信息，隐式接口从接口的实现中解耦了定义，这样接口的实现可以出现在任何包中，无需提前准备\nnil 接口值既不保存值也不保存类型，为 nil 接口调用方法会产生运行时错误，因为接口的元组内并未包含能够指明该调用哪个具体方法的类型 指定了零个方法的接口值被称为空接口，空接口可保存任何类型的值，空接口被用来处理未知类型的值\n类型断言 类型断言是一种可以从接口值中提取其动态类型具体值的方法，给一个什么值，他就变成对应的类型 该语句断言接口值 i 保存了具体类型 Type，并将其底层类型为 Type 的值赋予变量 t，如果未保存，那么该语句就会触发 panic\n1\tt := i.(T) 为了 判断 一个接口值是否保存了一个特定的类型，类型断言可返回两个值：其底层值以及一个报告断言是否成功的布尔值。\n1\tt, ok := i.(T) 若 i 保存了一个 T，那么 t 将会是其底层值，而 ok 为 true 否则，ok 将为 false 而 t 将为 T 类型的零值，程序并不会产生 panic\n类型选择 类型选择 是一种按顺序从几个类型断言中选择分支的结构，它们针对给定接口值所存储的值的类型进行比较\n1func Describe(s Shape) { 2 switch v := s.(type) { 3 case Circle: 4 fmt.Printf(\u0026#34;圆形，半径: %v\\n\u0026#34;, v.Radius) 5 case Rectangle: 6 fmt.Printf(\u0026#34;矩形，长: %v, 宽: %v\\n\u0026#34;, v.Width, v.Height) 7 default: 8 fmt.Println(\u0026#34;未知形状\u0026#34;) 9 } 10} 11 12func main() { 13 Describe(Circle{Radius: 3}) // 输出：圆形，半径: 3 14 Describe(Rectangle{4, 5}) // 输出：矩形，长: 4, 宽: 5 15} 闭包（closure） 闭包（closure）是指一个函数可以捕获并引用其外部作用域中的变量，即使在这些变量的作用域已经结束之后，换句话说，该函数被“绑定”到了这些变量。闭包的实现依赖于函数本身以及它所引用的环境\n1i := 13 2a := func() { 3 fmt.Println(\u0026#34;i = \u0026#34;, i) 4} 5a() 延迟绑定：闭包捕获的是变量的引用，而不是它在循环中每次迭代的值\n1func Delay() { 2 fns := make([]func(), 0, 10) 3 for i := 0; i \u0026lt; 10; i++ { 4 fns = append(fns, func() { 5 fmt.Println(\u0026#34;i = \u0026#34;, i) 6 }) 7 } 8 //执行的时候i = 10，因为闭包捕获的是变量i的引用,而不是值 9 //因此在循环中使用闭包时需要小心处理 10 //？？为什么我这里是i从0-9，好诡异啊 11 for _, fn := range fns { 12 fn() 13 } 14} 杂项记录 查看对应变量的类型 1//使用%T 2fmt.Printf(\u0026#34;type: %T\u0026#34;, value) 3 4//使用reflect下的Typeof，要引入reflect包 5fmt.Println(reflect.Typeof(value)) 输出其命令行参数 1package main 2 3import ( 4\u0026#34;fmt\u0026#34; 5\u0026#34;os\u0026#34; 6) 7 8func main() { 9fmt.Println(\u0026#34;Hello, World!\u0026#34;) 10var s, sep string 11for _, arg := range os.Args[0:] { //输出目标文件命令的名字 12s += sep + arg 13sep = \u0026#34; \u0026#34; 14} 15//for i := 1; i \u0026lt; len(os.Args); i++ { 16// s += sep + os.Args[i] 17// sep = \u0026#34; \u0026#34; 18//} 19fmt.Println(s) 20} OK 的使用 在 Go 语言中，ok 通常与 map 结合使用，用于判断某个键（key）是否存在于 map 中，是 Go 中一种常见的“安全读取”模式。 通过第二个返回值 ok（布尔类型）可以判断键是否存在：\n1 m := map[string]int{\u0026#34;apple\u0026#34;: 5, \u0026#34;banana\u0026#34;: 3} 2 3 // 检查键是否存在 4 value, ok := m[\u0026#34;apple\u0026#34;] 5 if ok { 6 fmt.Println(\u0026#34;apple 存在，值是\u0026#34;, value) 7 } else { 8 fmt.Println(\u0026#34;apple 不存在\u0026#34;) 9 } 在练习 LeetCode 的时候也看到有高级的语句记录了下来，对于 ok 在 if 中的使用含义，经常在 map 中使用：\n1func learnErrorHandling() { 2// \u0026#34;, ok\u0026#34;用来判断有没有正常工作 3m := map[int]string{3: \u0026#34;three\u0026#34;, 4: \u0026#34;four\u0026#34;} 4if x, ok := m[1]; !ok { // ok 为false，因为m中没有1 5fmt.Println(\u0026#34;no one there\u0026#34;) 6} else { 7fmt.Print(x) // 如果x在map中的话，x就是那个值喽。 8} 9} 上述代码中，在 if 语句中，ok这个 bool 变量的含义是，如果其中有1这个 key，那么其 value 赋值给x然后令 ok 为 true，如果没有则令 ok 为 false\n引用包时出错 包在之后的代码中并未引用，可以通过在包名前加_进行匿名引用\n定义变量 var 后面定义有默认类型，如果不想按照默认类型来的话，就需要自己写 go 是强类型语言，不做任何类型转换 或者直接:=进行赋值\n1var a = 13 // 默认为int 2var b uint =15 3a := 13 求 string 的长度 len()函数求出来的是字节的长度，而不是字符长度 应该用utf8.RuneCountInString(\u0026quot;你好\u0026quot;)进行求解\n对二维数组进行排序 这里的 slices.SortFunc 通过传入一个比较函数（func(p, q []int) int）来实现对 intervals 进行排序 比较函数：func(p, q []int) int。它对两个元素 p 和 q 进行比较。\n如果返回值 \u0026lt; 0，表示 p 应排在 q 之前。 如果返回值 \u0026gt; 0，表示 q 应排在 p 之前。 如果返回值 = 0，表示 p 和 q 的顺序无所谓。 1slices.SortFunc(intervals, func(p, q []int) int { return p[0] - q[0] }) ","permalink":"https://mariosight.github.io/posts/go%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/","summary":"\u003ch4 id=\"介绍\"\u003e介绍\u003c/h4\u003e\n\u003cp\u003eGo 语言是一种静态、编译型、并发型的编程语言。它具有强类型、垃圾回收、快速编译等特性，属于 C 系语言的一种\n编译型 vs 解释型\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e编译型语言\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e优点\u003c/strong\u003e：编译器一般会有预编译的过程对代码进行优化。因为编译只做一次，运行时不需要编译，所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。\u003c/p\u003e","title":"Go语言基础学习"},{"content":"Ubuntu22.04LTS 下Python3.7.9+Charm-Crypto+pypbc+Jupyter环境搭建 参考文献 Linux安装Charm-crypto环境详细流程_android使用charm-crypto-CSDN博客\n1 前置工具 为了方便本地操作，先安装openssh-server和net-tools 1sudo apt install openssh-server net-tools -y 连接到本地xshell以后，再进行如下安装 1sudo apt install gcc g++ make vim vsftpd wget m4 flex bison python3-setuptools python3-dev python3-pip -y 安装python编译依赖\n1sudo apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev 配置vsftpd 1sudo vim /etc/vsftpd.conf 配置pip源 1cd 2sudo mkdir .pip 3cd .pip 4sudo touch pip.conf 5sudo vim pip.conf 粘贴以下内容到pip.conf\n[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple [install] trusted-host = https://pypi.tuna.tsinghua.edu.cn 安装git 安装git\n1sudo apt install git git配置\n1git config --global user.name 名字 2git config --global user.email 邮箱 cd到家目录下的Downloads目录下，下载所有源码包\n1sudo wget https://gmplib.org/download/gmp/gmp-5.1.3.tar.bz2 2sudo wget https://crypto.stanford.edu/pbc/files/pbc-0.5.14.tar.gz 3sudo wget https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tgz 4 5# charm和pypbc获取 6# 可以从github导入到gitee加快下载速度 7git clone https://gitee.com/liihoo/charm.git 8git clone https://gitee.com/liihoo/pypbc.git 安装GMP 1sudo tar -jxvf gmp-5.1.3.tar.bz2 2cd gmp-5.1.3/ 3sudo ./configure 4sudo make 5sudo make install 安装PBC 安装PBC\n1sudo tar -zxvf pbc-0.5.14.tar.gz 2cd pbc-0.5.14/ 3sudo ./configure 4sudo make 5sudo make install 添加libpbc到系统链接库\n1sudo vim /etc/ld.so.conf 写入以下内容\n1/usr/local/lib 更新动态库\n1sudo ldconfig 安装Python3.7.9 新建一个文件用于安装目录，比如/opt/python3/python3.7.9\n1sudo tar -zxvf Python-3.7.9.tgz 2sudo ./configure --prefix=/opt/python3/python3.7.9 3sudo make 4sudo make install 创建Python3.7.9的软连接。不要覆盖系统默认的版本\n1sudo ln -s /opt/python3/python3.7.9/bin/pip3 /usr/bin/pip3.7 2sudo ln -s /opt/python3/python3.7.9/bin/python3.7 /usr/bin/python3.7 3pip3.7 install pyparsing==2.2.1 安装Jupyter 1pip3.7 install jupyter notebook 打开家目录下的.bashrc添加jupyter路径\nvim .bashrc 更新配置\n1source .bashrc 生成配置文件\n1jupyter notebook --generate-config 使用python中的passwd()创建密码，终端输入ipython打开ipython并输入，复制打印的密码\n1In [1]: from notebook.auth import passwd 2In [2]: passwd() 打开配置文件\n1vim ~/.jupyter/jupyter_notebook_config.py 在文件末尾添加：\n1c.NotebookApp.allow_remote_access = True #允许远程连接 2c.NotebookApp.ip=\u0026#39;*\u0026#39; # 设置所有ip皆可访问 3c.NotebookApp.open_browser = False # 禁止自动打开浏览器 4c.NotebookApp.port =8888 #任意指定一个端口 5c.NotebookApp.password = u\u0026#39;sha:..\u0026#39; #之前复制的密码 安装charm-crypto 打开charm-crypto的GitHub地址，下载源码的压缩包 网络原因，需要想办法，最后打开xftp上传到Ubuntu\n1sudo unzip charm-dev.zip 打开configure.sh修改python配置文件位置指向python3.7.9的位置，如下\n1cd charm 2sudo ./configure.sh --python=/opt/python3/python3.7.9/bin/python3.7 3sudo make 4sudo make install 安装pypbc 打开pypbc.c文件，将第1338行注释掉，否则无法进行$Zr$上的除法运算\n1cd pypbc 2sudo python3.7 setup.py install 3sudo pip3.7 install pypbc 后续 删除所有自己安装的gmp库\n1sudo rm -rf /usr/local/lib/libgmp* 更新动态库\n1sudo ldconfig 常用的python库安装\n1pip3.7 install numpy scipy pandas matplotlib pycryptodome ","permalink":"https://mariosight.github.io/posts/ubuntu22.04%E4%B8%8Bcharm%E5%AE%9E%E9%AA%8C%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/","summary":"\u003ch1 id=\"ubuntu2204lts-下python379charm-cryptopypbcjupyter环境搭建\"\u003eUbuntu22.04LTS 下Python3.7.9+Charm-Crypto+pypbc+Jupyter环境搭建\u003c/h1\u003e\n\u003ch2 id=\"参考文献\"\u003e参考文献\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://blog.csdn.net/qq_33976344/article/details/115383904\"\u003eLinux安装Charm-crypto环境详细流程_android使用charm-crypto-CSDN博客\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"1-前置工具\"\u003e1 前置工具\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e为了方便本地操作，先安装openssh-server和net-tools\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003esudo apt install openssh-server net-tools -y\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003e连接到本地xshell以后，再进行如下安装\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"ln\"\u003e1\u003c/span\u003e\u003cspan class=\"cl\"\u003esudo apt install gcc g++ make vim vsftpd wget m4 flex bison python3-setuptools python3-dev python3-pip -y\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e安装python编译依赖\u003c/p\u003e","title":"Ubuntu22.04LTS 下Python3.7.9+Charm-Crypto+pypbc+Jupyter环境搭建"},{"content":"遵循视频教程： Fabric 环境搭建，感谢 UP 主年青_的教学，受益良多\n一、前言： 本教程是协助大家在虚拟机中搭建 Fabric 环境，参照 UP 主所给文件 fabric 宝典 所写，并加入了一些自己在安装过程中碰到的一些问题以及解决办法 本教程使用的系统为 Ubuntu 22.04.2 LTS，其他安装软件版本为 fabric：hyperledger-fabric-linux-amd64-2.4.1 fabric-ca：hyperledger-fabric-ca-linux-amd64-1.5.2 go：go 1.20.6.linux-amd64\n二、环境准备 1、apt 换源 在浏览器中访问 https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/ 将其中的内容复制然后粘贴到 sources.list 中，然后进行更新\n1sudo gedit /etc/apt/sources.list 2sudo apt update 2、安装 docker、docker-compose 1sudo apt install docker docker-compose 2sudo systemctl enable docker 3sudo usermod -a -G docker \u0026lt;username\u0026gt;/*注意此处为自己系统的用户名*/ 3、安装 golang 浏览器访问 https://go.dev/doc/install ，访问速度过慢可以在本机中访问并下载，通过 Xftp 传入 Ubuntu Xftp 与虚拟机相连的具体教学可以参考以下博客： 使用 NAT 模式并设置静态 IP 地址： https://blog.csdn.net/xzm5708796/article/details/93410325 Xftp 中设置： Xftp 连接虚拟机或者服务器的使用方法-你必须会使用的文件传输工具_ISmileLi 的博客-CSDN 博客 记得在虚拟机中允许远程链接：xftp 与 xshell 无法连接虚拟机的解决办法（99%适用）\n1sudo su 2rm -rf /usr/local/go \u0026amp;\u0026amp; tar -C /usr/local -xzf go 1.20.6. Linux-amd 64.tar.gz 3gedit /etc/profile 4\texport PATH=$PATH:/usr/local/go/bin 5gedit ~/.bashrc 6\tsource /etc/profile 4、docker 加速器 在浏览器中打开 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors 根据其操作文档进行配置即可\n三、安装 fabric-sample 1、手动创建脚本，安装 samples、docker 在 根目录下 建立一个新文件夹 fabric 用于存储 fabric-samples\n1mkdir fabric 从网页中将 bootstrap.sh 的内容复制下来放入新建的 Fabric 文件夹下 https://github.com/hyperledger/fabric/blob/main/scripts/bootstrap.sh\n1touch bootstrap.sh 2gedit bootstrap.sh 修改脚本其中的 binaries=false\n1sudo chmod u+x bootstrap.sh 2./bootstrap.sh 2、安装 binaries 内容 https://github.com/hyperledger/fabric/releases/download/v2.4.1/hyperledger-fabric-linux-amd64-2.4.1.tar.gz https://github.com/hyperledger/fabric-ca/releases/download/v1.5.2/hyperledger-fabric-ca-linux-amd64-1.5.2.tar.gz 同理，如果虚拟机中访问速度过慢可以通过 Xftp 进行传输 tar -xzvf 压缩包名 -C 目的地 具体情况请具体分析，因为我直接将压缩包放入了 fabric 文件夹下，所以我进行解压操作如下：\n1tar -xzvf hyperledger-fabric-linux-amd64-2.4.1.tar.gz -C fabric-samples 2tar -xzvf hyperledger-fabric-ca-linux-amd64-1.5.2.tar.gz -C fabric-samples 在解压完成后可以看到 fabric-samples 文件夹下有 bin 和 config 文件夹 3、配置 go 代理\n1go env -w GO111MODULE=on 2go env -w GOPROXY=https://goproxy.cn,direct 四、环境测试 可以根据官方的测试文档进行测试：Using the Fabric test network — hyperledger-fabricdocs main documentation\n1cd fabric-samples/test-network 2./network.sh down 3./network.sh up 同时也可以去到 fabcar 文件夹下测试是否正常\n1cd fabcar 2./startFabric.sh 可能在过程中会出现这种情况： 只需要执行sudo apt install jq安装 jq 即可 再去到 fabcar 下的 go 目录执行对应脚本\n1cd go 2go run fabric.go 出现以下界面就算成功安装啦~ 五、其他问题： 在评论区碰到很多老哥有一些建议和其他的问题，还有 UP 的一些回答，有解决的我就贴到这里来了 1.bootstrap.sh 脚本里写的版本是会更新的，如果直接用 up 给的 2.4.1 版本二进制文件，在执行 test-network 时会报错 缺少二进制文件 ，所以下载二进制文件时记得更改域名地址里的版本号 2.Q：fabric-samples 下没有 fabcar 文件夹 UP：github 显示作者在 3 月 1 日移除了 fabcar 等示例，如果你们下载的是最新的 fabric-samples 就没有 fabcar 了。解决方案有两个：一是下载我视频中对应的版本，二是参考这个英文的最新官方教程https://hyperledger-fabric.readthedocs.io/en/release-2.5/whatis.html，不要看中文版，中文版还没更新过来 3.执行./network.sh up 出现一次“/compose-test-net.yaml is unsupported.”这个错误是 docker-compose 版本的问题，执行一下这个应该就行了：\n1curl -L https://github.com/docker/compose/releases/download/1.27.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 作为新手十分感谢 UP 主提供的教学，也感谢评论区提出问题并提供解决方法的大佬们（鞠躬）\n","permalink":"https://mariosight.github.io/posts/fabric%E5%9F%BA%E7%A1%80%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/","summary":"\u003cp\u003e\u003cstrong\u003e遵循视频教程\u003c/strong\u003e： \u003ca href=\"https://www.bilibili.com/video/BV1g3411h71Z\"\u003eFabric 环境搭建\u003c/a\u003e，感谢 UP 主年青_的教学，受益良多\u003c/p\u003e\n\u003ch3 id=\"一前言\"\u003e一、前言：\u003c/h3\u003e\n\u003cp\u003e本教程是协助大家在虚拟机中搭建 \u003cstrong\u003eFabric\u003c/strong\u003e 环境，参照 UP 主所给文件 \u003cstrong\u003efabric 宝典\u003c/strong\u003e 所写，并加入了一些自己在安装过程中碰到的一些问题以及解决办法\n本教程使用的系统为 Ubuntu 22.04.2 LTS，其他安装软件版本为\nfabric：hyperledger-fabric-linux-amd64-2.4.1\nfabric-ca：hyperledger-fabric-ca-linux-amd64-1.5.2\ngo：go 1.20.6.linux-amd64\u003c/p\u003e","title":"Fabric基础环境搭建"},{"content":"南京之行：找小徐玩咯 2023-06-23 南京大屠杀纪念馆（铭记苦难，向往和平） 南京大屠杀纪念馆，一直很想去看看的，看日本动漫听日本歌曲也不代表我们会忘记那段屈辱悲惨的过去，我们会永远记得，永远记得日本军国主义对我们的同胞惨无人道的屠戮，永远记得他们犯下的所有令人唾弃的罪行，永远记得落后就要挨打的历史教训，我们永远记得\n在馆前有可以通过向纪念馆捐赠任意金额获得一枝白色花朵，献给在南京大屠杀中的遇难者，一开始我以为是在卖花，因为之前去北京的毛主席纪念堂的时候有人在卖，感觉这种行为不是很好，是在利用逝去的人们谋取利益，但是感觉这种捐赠的方式就很好，而且信息是公开到网站和公众号的，我也知道这笔钱一定会到纪念馆的日常开销中 矗立在馆前的一座雕塑，仿佛在向着天空咆哮 遇难者人数和南京大屠杀持续时间 到了展厅，全黑的墙壁，惨白的灯光，似乎都在诉说着那个年代的不公与凄惨，墙上一幅幅图片一段段文字，无不表达着对当时日本军队恶行的控诉，对深受苦难的中国人民的哀悼，因为感觉在场馆里面拍照是对那段历史的亵渎，所以只拍了一张我比较喜欢的话 刚刚入馆的标题 “我不是复仇主义者，我无意于把日本军国主义欠下我们的血债写在日本人民的账上。但是，我相信，忘记过去的苦难可能招致未来的灾祸”，在参观之后有一个笔记的留念上我也写下了这句话，我们对热爱和平的日本人民是欢迎的，但是我们在心里永远铭刻着这场灾难，为的是防止之后在发生这样惨绝人寰的事情 远东国际军事法庭法官梅汝璈 刻在石板上的数字，也刻在每个中国人心里；向往和平的女神像，她手中的和平鸽是否会一直飞下去呢 遇难者人数 向往和平的女神像 夫子庙街道（现代与历史的文化融合） 感受完历史的沉重，又来到了南京的秦淮区，夫子庙街道，人山人海，好不繁华，各种小吃店、商品店、饭店，给我的感觉是将历史文化与现代气息融合起来了，虽然我不是很喜欢这种夹杂着现代气息的文化景点，但也算是一种商业尝试吧 秦淮-戏院里（应该是一个饭店吧） 中国科举博物馆 “天下文枢”出自重建于明万历年间的南京文庙（即南京夫子庙），清初坊额上方的字为著名书法家陈澍所写。今坊额上的“天下文枢”四个大字选自颜真卿的字帖，它是步入文庙的第一道大门（来自百度百科） 这边好多人在排队，可能是去拍照吧，人流量有点太恐怖了（牵手手就不怕走丢啦 🤤） 天下文枢 在小桥上拍的照片，真的很有江南水乡的韵味，虽然亮起的灯有点出戏吧（，之前总是在电视里看着那些江南风景，现在终于看到了。听小徐说在苏州那边的江南水乡气息更加浓厚一些，有点想去看看了 河里的游船 河边的房子 灯火通明 2023-06-24： 中山陵（潮流沧海引，礼服号中山） 第二天！早上出来的时候下了雨，本来觉得下不久就会停，但是感觉南京的天空似乎不太愿意停下，不断下着细雨，时停时密的钟山，钟山又称紫金山，历史上便是知名的风景区，其历史可以追溯至中国的东晋时期。钟山风景名胜区现由两大区域构成，即东部主体钟山主体区域中山陵园风景区（包含中山陵景区、明孝陵景区、灵谷景区三大核心景区和其他区域景点）以及西部的区域（包括玄武湖景区和九华山区域）（还是百度百科），过来见一见老朱和老孙，了解一下民国和明朝时期的历史文化中山陵的这条路上好多人，对不起中山先生，看见这么多人来我也就放心了，也不缺我一个人，我还是去看看旁边的事迹展会吧（ 中山陵前的博爱坊 孙中山史迹展会 中山陵旁边就是音乐台，感觉这种民国时期的建筑风格的确更偏西式一点。这边好多鸽子，白鸽、灰鸽都有，合理怀疑这边是什么鸽子养殖基地（\n正面拍一下，上面停满了鸽子 侧面再来一张 美龄宫，当时的正式名称是国民政府主席官邸，国民政府从重庆迁回首都南京后，将此处设为蒋介石官邸，蒋介石常与宋美龄来此休息和度假，蒋介石也曾多次在此接待外国贵宾。老蒋我来了嗷，敢不敢跟我比划比划（，诶我身份证呢，老蒋你真是不讲武德，你太卑鄙了，你偷我身份证干嘛！（在美龄宫门前找不到身份证了呜呜呜，联系观光车也没有找到，在女朋友面前丢大人了） 外面是中式建筑的风格，里面却是西式的装潢 那个年代的老式汽车，经常出现在抗日剧里面的 “革命尚未成功，同志仍需努力” 中山先生千古 明孝陵（千秋帝业成春梦，万古江山作画屏） 明孝陵，是明代开国皇帝朱元璋和皇后马氏的合葬陵墓。因皇后谥“孝慈”，故名孝陵，作为中国明陵之首的明孝陵壮观宏伟，代表了明初建筑和石刻艺术的最高成就，影响了明清两代 500 多年帝王陵寝的形制（嗯，是的） 明孝陵 神圣功德碑楼，内有立于龟座（赑屃，bi xi）上的石碑，碑高 6.7 米，赑屃高 2.08 米，碑文由明成祖朱棣亲自撰写明太祖的功德 神功圣德碑 石像路神道，位于神功圣德碑亭之北，过五孔御河桥，列石像生十二对，自前向后依次为坐狮、立狮、坐獬豸（xie zhi）、立獬豸、卧驼、立驼、卧象、立象、坐麒麟、立麒麟、卧马、立马。石兽之后又有望柱一对、武臣二对、文臣二对 不要随意攀爬古遗迹雕塑啊，走在神道上看到有小孩子去攀爬我心疼的很，你等老朱从陵墓里面爬出来把你做成稻草人挂在上面（ 没有拍照片，因为当时下雨走路感觉不方便拍，来点网图 文武方门，陵宫的正门，不怎么下雨咯 文武方门 明楼，明楼为长方形城堡式建筑，以石条垒成，下为须弥座，通高 16.25 米，正中辟一门券，高 3.86 米。里面是楼梯，上去之后则是斜着的路面，雨天路滑，小心摔倒哦（ 明楼 明楼上还有 VR 体验陵墓遗迹，科技改变历史古迹？（ 登上明楼，视野开阔 逛了一天，也下了一天的小雨，走到出口时雨也停了，在池塘边看到正盛开的荷花，正是“惟有绿荷红菡萏，卷舒开合任天真”，令人心旷神怡 从钟山下来咯，出口旁边的荷花池 太平天国博物馆（始为黎民成半壁,终成皇帝败江山） 一刻都没有为逛完钟山而感到劳累,立刻赶到的地方是——瞻园中的太平天国历史博物馆（ 太平天国，太平天国是中国近代的一次大规模农民起义，其疆域最广阔之时曾占有中国半壁江山，在南京定都，发展到江南各个地区，但最后还是因为农民的阶级局限性而导致失败，可悲可叹 太平天国历史陈列 太平军攻破南京城 瞻园（名园一自邀游赏，未许凡人到此来） 瞻园是南京现存历史最久的明代古典园林、“江南四大名园”之一，也是 87 版红楼梦的取景地。跟小徐一起看的 87 版的红楼梦，感叹贾府的兴盛衰败，感慨红楼女子的凄惨命运。可能自己本身就是一个偏悲观的人，一直记得黛玉和湘云在中秋时节在凹晶馆联诗的那句“寒塘渡鹤影，冷月葬花魂”，还有黛玉葬花吟中的那句，“花谢花飞花满天，红消香断有谁怜”，盛世时期的诗句倒是没记住几个（\n古典园林风景 远处的亭子 经典的圆拱门 假山上的台阶 夜游南京（凤箫声动，玉壶光转，一夜鱼龙舞） 明早就要走了，从酒店里出来逛逛，走到百家湖那边了，突然想起辛弃疾的那首青玉案，“众里寻他千百度，蓦然回首，那人却在灯火阑珊处”，（转头一看，她不在，失落，继续往前走），南京的夜景，繁华的街道，又一次站在人生岔路口的我，也不知道未来该走向何方（什么青春伤痛文学） 百家湖文化馆，好炫酷的建筑，必须拍一张 百家湖夜景 凤凰台 在桥上拍的江宁眼摩天轮 再见了，南京，我们下次有缘还会再见的，不知下次再在这里咏诵着宋词又将是什么时候了呢\n","permalink":"https://mariosight.github.io/posts/%E5%8D%97%E4%BA%AC%E4%B9%8B%E8%A1%8C/","summary":"\u003ch3 id=\"南京之行找小徐玩咯\"\u003e南京之行：找小徐玩咯\u003c/h3\u003e\n\u003ch4 id=\"2023-06-23\"\u003e2023-06-23\u003c/h4\u003e\n\u003ch5 id=\"南京大屠杀纪念馆铭记苦难向往和平\"\u003e南京大屠杀纪念馆（铭记苦难，向往和平）\u003c/h5\u003e\n\u003cp\u003e\u003cstrong\u003e南京大屠杀纪念馆\u003c/strong\u003e，一直很想去看看的，看日本动漫听日本歌曲也不代表我们会忘记那段屈辱悲惨的过去，我们会永远记得，永远记得日本军国主义对我们的同胞惨无人道的屠戮，永远记得他们犯下的所有令人唾弃的罪行，永远记得落后就要挨打的历史教训，\u003cstrong\u003e我们永远记得\u003c/strong\u003e\u003c/p\u003e","title":"南京之行"},{"content":"","permalink":"https://mariosight.github.io/about/","summary":"about","title":"About"}]