摘要:本实践是基于Windows版MindStudio 5.0.RC3,远程连接ECS服务器使用,ECS是基于官方分享的CANN6.0.RC1_MindX_Vision3.0.RC3镜像创建的。本文分享自华为云社区《【MindStudio训练营第一季】基于U-Net网络的图像分割的MindStudio实践》,作者:Tianyi_Li 。
[U-Net 论文]: Olaf Ronneberger, Philipp Fischer, Thomas Brox. “U-Net: Convolutional Networks for Biomedical Image Segmentation.” conditionally accepted at MICCAI 2015. 2015.
本项目支持MindStudio运行和终端运行。
项目文件结构├── unet_sdk
├── README.md
├── data //数据集
│ ├── 1
│ │ ├──image.png //图片
│ │ ├──mask.png //标签
│ ...
├── model
│ ├──air2om.sh // air模型转om脚本
│ ├──xxx.air //air模型
│ ├──xxx.om //om模型
│ ├──aipp_unet_simple_opencv.cfg // aipp文件
├── pipeline
│ ├──unet_simple_opencv.pipeline // pipeline文件
├── main.py // 推理文件
├── run.sh // 执行文件
├── requirements.txt // 需要的三方库

cd unet_sdk/model/ # 切换至模型存储目录
atc --framework=1 --model=unet_hw960_bs1.air --output=unet_hw960_bs1 --input_format=NCHW --soc_version=Ascend310 --log=error --insert_op_conf=aipp_unet_simple_opencv.cfgframework:原始框架类型。
model:原始模型文件路径与文件名。
output:转换后的离线模型的路径以及文件名。
input_format:输入数据格式。
soc_version:模型转换时指定芯片版本。
log:显示日志的级别。
insert_op_conf:插入算子的配置文件路径与文件名,这里使用AIPP预处理配置文件,用于图像数据预处理。
模型转换成功之后,可以使用MindX SDK mxVision运行脚本,在Ascend 310上进行推理。
MindX SDK基础概念介绍:
MindX SDK基础插件:
MindX SDK业务流程编排:Stream配置文件以json格式编写,用户必须指定业务流名称、元件名称和插件名称,并根据需要,补充元件属性和下游元件名称信息。以下表格为本实验pipeline/unet_simple_opencv.pipeline文件及其对应的名称及描述:
pipeline/unet_simple_opencv.pipeline文件内容如下,可根据实际开发情况进行修改。{
"unet_mindspore": {
"stream_config": {
"deviceId": "0"
},
"appsrc0": {
"props": {
"blocksize": "4096000"
},
"factory": "appsrc",
"next": "mxpi_imagedecoder0"
},
"mxpi_imagedecoder0": {
"props": {
"cvProcessor": "opencv",
"outputDataFormat": "BGR"
},
"factory": "mxpi_imagedecoder",
"next": "mxpi_imagecrop0"
},
"mxpi_imagecrop0": {
"props": {
"cvProcessor": "opencv",
"dataSource": "ExternalObjects"
},
"factory": "mxpi_imagecrop",
"next": "mxpi_imageresize0"
},
"mxpi_imageresize0": {
"props": {
"handleMethod": "opencv",
"resizeType": "Resizer_Stretch",
"resizeHeight": "960",
"resizeWidth": "960"
},
"factory": "mxpi_imageresize",
"next": "mxpi_tensorinfer0"
},
"mxpi_tensorinfer0": {
"props": {
"dataSource": "mxpi_imageresize0",
"modelPath": "model/unet_hw960_bs1_AIPP.om"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_dumpdata0"
},
"mxpi_dumpdata0": {
"props": {
"requiredMetaDataKeys": "mxpi_tensorinfer0"
},
"factory": "mxpi_dumpdata",
"next": "appsink0"
},
"appsink0": {
"props": {
"blocksize": "4096000"
},
"factory": "appsink"
}
}
}"modelPath": "model/unet_hw960_bs1.om"import argparse
import base64
import json
import os
import cv2
import numpy as np
from StreamManagerApi import *
import MxpiDataType_pb2 as MxpiDataType
x0 = 2200 # w:2200~4000; h:1000~2800
y0 = 1000
x1 = 4000
y1 = 2800
ori_w = x1 - x0
ori_h = y1 - y0
def _parse_arg():
parser = argparse.ArgumentParser(description="SDK infer")
parser.add_argument("-d", "--dataset", type=str, default="data/",
help="Specify the directory of dataset")
parser.add_argument("-p", "--pipeline", type=str,
default="pipeline/unet_simple_opencv.pipeline",
help="Specify the path of pipeline file")
return parser.parse_args()
def _get_dataset(dataset_dir):
img_ids = sorted(next(os.walk(dataset_dir))[1])
for img_id in img_ids:
img_path = os.path.join(dataset_dir, img_id)
yield img_path
def _process_mask(mask_path):
# 手动裁剪
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)[y0:y1, x0:x1]
return mask
def _get_stream_manager(pipeline_path):
stream_mgr_api = StreamManagerApi()
ret = stream_mgr_api.InitManager() #初始化stream
if ret != 0:
print(f"Failed to init Stream manager, ret={ret}")
exit(1)
with open(pipeline_path, 'rb') as f:
pipeline_content = f.read()
ret = stream_mgr_api.CreateMultipleStreams(pipeline_content) # 创建stream
if ret != 0:
print(f"Failed to create stream, ret={ret}")
exit(1)
return stream_mgr_api
def _do_infer_image(stream_mgr_api, image_path):
stream_name = b'unet_mindspore' # 与pipeline中stream name一致
data_input = MxDataInput()
with open(image_path, 'rb') as f:
data_input.data = f.read()
# 插入抠图的功能,扣1800*1800大小
roiVector = RoiBoxVector()
roi = RoiBox()
roi.x0 = x0
roi.y0 = y0
roi.x1 = x1
roi.y1 = y1
roiVector.push_back(roi)
data_input.roiBoxs = roiVector
unique_id = stream_mgr_api.SendData(stream_name, 0, data_input) # 向指定Stream上的输入元件发送数据(appsrc)
if unique_id < 0:
print("Failed to send data to stream.")
exit(1)
infer_result = stream_mgr_api.GetResult(stream_name, unique_id) # 获得Stream上的输出元件的结果(appsink)
if infer_result.errorCode != 0:
print(f"GetResult error. errorCode={infer_result.errorCode},"
f"errorMsg={infer_result.data.decode()}")
exit(1)
# 用dumpdata获取数据
infer_result_data = json.loads(infer_result.data.decode())
content = json.loads(infer_result_data['metaData'][0]['content'])
tensor_vec = content['tensorPackageVec'][0]['tensorVec'][1] # 1是argmax结果
data_str = tensor_vec['dataStr']
tensor_shape = tensor_vec['tensorShape']
argmax_res = np.frombuffer(base64.b64decode(data_str), dtype=np.float32).reshape(tensor_shape)
np.save("argmax_result.npy", argmax_res)
tensor_vec = content['tensorPackageVec'][0]['tensorVec'][0] # 0是softmax结果
data_str = tensor_vec['dataStr']
tensor_shape = tensor_vec['tensorShape']
softmax_res = np.frombuffer(base64.b64decode(data_str), dtype=np.float32).reshape(tensor_shape)
np.save("softmax_result.npy", softmax_res)
return softmax_res # ndarray
# 自定义dice系数和iou函数
def _calculate_accuracy(infer_image, mask_image):
mask_image = cv2.resize(mask_image, infer_image.shape[1:3])
mask_image = mask_image / 255.0
mask_image = (mask_image > 0.5).astype(np.int)
mask_image = (np.arange(2) == mask_image[..., None]).astype(np.int)
infer_image = np.squeeze(infer_image, axis=0)
inter = np.dot(infer_image.flatten(), mask_image.flatten())
union = np.dot(infer_image.flatten(), infer_image.flatten()) + \
np.dot(mask_image.flatten(), mask_image.flatten())
single_dice = 2 * float(inter) / float(union + 1e-6)
single_iou = single_dice / (2 - single_dice)
return single_dice, single_iou
def main(_args):
dice_sum = 0.0
iou_sum = 0.0
cnt = 0
stream_mgr_api = _get_stream_manager(_args.pipeline)
for image_path in _get_dataset(_args.dataset):
infer_image = _do_infer_image(stream_mgr_api, os.path.join(image_path, 'image.png')) # 抠图并且reshape后的shape,1hw
mask_image = _process_mask(os.path.join(image_path, 'mask.png')) # 抠图后的shape, hw
dice, iou = _calculate_accuracy(infer_image, mask_image)
dice_sum += dice
iou_sum += iou
cnt += 1
print(f"image: {image_path}, dice: {dice}, iou: {iou}")
print(f"========== Cross Valid dice coeff is: {dice_sum / cnt}")
print(f"========== Cross Valid IOU is: {iou_sum / cnt}")
stream_mgr_api.DestroyAllStreams() # 销毁stream
if __name__ == "__main__":
args = _parse_arg()
main(args)set -e
CUR_PATH=$(cd "$(dirname "$0")" || { warn "Failed to check path/to/run.sh" ; exit ; } ; pwd)
# Simple log helper functions
info() { echo -e "\033[1;34m[INFO ][MxStream] $1\033[1;37m" ; }
warn() { echo >&2 -e "\033[1;31m[WARN ][MxStream] $1\033[1;37m" ; }
#export MX_SDK_HOME=${CUR_PATH}/../../..
export LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:${LD_LIBRARY_PATH}
export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner
export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins
#to set PYTHONPATH, import the StreamManagerApi.py
export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python
python3 main.py
exit 0. /root/mxVision/set_env.shcd /root/unet_sdk/ # 切换至推理脚本目录
bash run.sh
通过MindStudio运行,会自动上传代码到预设路径,并执行,运行结果如下:

各参数具体说明如下:
运行完成后,会自动跳转到如下图所示界面。
这里的Model Performance Report是模型性能的总结报告。根据该页面,可以知道模型性能是好是坏,也可以知道模型的吞吐率和运行时间,AI Core的利用率,Tiling策略是否合理,部分字段说明如下所示,,具体可参见https://www.hiascend.com/document/detail/zh/mindstudio/50RC3/msug/msug_000285.html。
从上述截图的分析结果可以看出我们所用的U-Net模型的芯片利用率是偏低的,甚至可以说是很差,有很大的优化的空间。下面我们来详细分析。
先来看根据总体性能数据汇总计算得出的Model Performance,显示为Bad,情况不乐观啊。接着看看汇总信息。
可以看到Cube吞吐量Cube Throughput约393 GOps,Vector吞吐量Vector Throughput约0.89 GOps,AI Core执行时间88712us,任务执行时间177183us,平均BlockDim利用率Avg BlockDim Usage,算子执行时的平均核心数,数值为1,这反映了反映芯片利用情况,考虑到我们ECS的Ascend 310总计2个AI Core,且由系统自动调度,这里为1还可以接收。但下面的数据可难看了。
如图所示,芯片利用率Chip Utilization。按照规则,80为优,显示为绿色;小于80则为差,显示为红色。根据Pipeline Bound的数值计算得出。而我们这里直接为0了。再看Cube利用率Cube Ratio约为0.41,Vector利用率Vector Ratio约为0.29,Scalar利用率Scalar Ratio约为0.26,还有优化空间,而MTE1瓶颈、MTE2瓶颈、MTE3瓶颈都还不小。特别是MTE2瓶颈,约为0.39。下面接着看。
来看内存读入量的数据切片策略Tiling Strategy为5.23,情况糟糕。根据规则,数值达到80为优,显示为绿色;小于80则为差,显示为红色。这是根据Memory Redundant的数值计算得出。同时,可以看到真实内存读入量Real Memory Input(GB)约为1.44GB,真实内存写出量Real Memory Output(GB)约为0.60GB,都在可用范围内。接下来进入Computational Graph Optimization界面,如图红框所示两处都可以,二者就是简版和精装版的区别了。在这里,我们可以看到计算图优化,算子融合推荐功能专家系统分析建议,会分行展示可融合的算子。
先来看看需要进行UB融合的算子,点击算子,会自动跳转相应的计算图,既直观又方便啊。同时我们看到可融合算子的执行时间Fusion Operator Duration达到了约9585us,接近10ms了,算是比较大了。本模型没有AIPP融合推荐和L2Cache融合推荐。
我们直接看TransData算子融合推荐,如图所示。该算子执行持续时间在2.5ms左右,也不算小了,也是一个值得优化的地方啊。
但遗憾的是本次Roofline页面(基于Roofline模型的算子瓶颈识别与优化建议功能输出结果)和Model Graph Optimization页面(基于Timeline的AICPU算子优化功能输出结果)都没什么结果展示,具体分别如下图所示。
这里提个建议,看了下运行日志(显示了这一句[ERROR] Analyze model(RooflineModel) failed, please check dfx log.),Roofline之所以没有结果显示,可能是Roofline运行失败,不过这个日志有点太简洁了,不太友好,也没有给出 dfx log的具体路径或位置,哪怕弹出一个链接给用户,让用户去自行查找呢,这都没有,感觉界面不太友好啊。专家系统提供对推理模型和算子的性能瓶颈分析能力并输出专家建议,但实际性能调优还需用户自行修改,不能做到性能调优一键式闭环。而性能调优一键式闭环提供一键式性能问题分析和优化能力,有效提升用户性能分析和优化效率。同时,性能调优一键式闭环当前实现生态知识库ONNX模型的部分推理场景瓶颈识别和自动化调优能力。但这需要准备待优化的ONNX模型文件(.onnx)以及ONNX模型经过ATC转换后的OM模型文件(.om),而我们这次是.air模型转om模型,虽然可以获得onnx模型,但太麻烦了,我们这次就不尝试了。这里再提个建议,性能调优一键式闭环还需要下载ONNX模型调优知识库,我按照文档(文档链接:https://www.hiascend.com/document/detail/zh/mindstudio/50RC3/msug/msug_000303.html)中提供Link跳转下载(如下图所示),未能找到文档后续所说的KnowledgeBase Configuration知识库配置文件echosystem.json。不知道是不是我的问题,建议工程师看看,验证一下。
个人认为,这应该是本地电脑没安装Python或者安装了,但是没有添加到系统Path,以致无法调用,这应该Profiling需要在Windows上调用Python做一些操作,但无法调用Python导致的。那么我们只要安装Python的时候,选择添加到Path,或者已经安装Python的同学,将Python添加到Path,最终使得能够在Windows终端下直接调用Python即可。最终效果示例如下:
此外,因为ECS终端默认启动的Python是/usr/bin/python,而在ECS默认是Python2直接运行程序会报错,而我们需要用Python3,所以需要重新链接符号,具体流程为:删除python链接文件–>>新建链接文件到python3,下面是操作步骤:
那么有人可能就要问了,为什么是/usr/local/python3.9.5/bin/python3呢?因为我们在通过MindStudio直接在远程ECS上运行的时候,就是用的这个啊,来张图看看:
这里提个建议,我之前运行会报错,详情见https://www.hiascend.com/forum/thread-0229107014330773104-1-1.html,连着两天重启,试了好几次,就是不行,后来又试了一次,突然就可以了,感觉很奇怪,莫明其妙地报错,莫名其秒地好了,这给我一种IDE很不稳定的感觉。建议优化一下,提升下稳定性。
配置“Project Properties”,配置工程名称“Project Name”和选择工程路径“Project Location”。单击“Next”进入下一步。(2)进入“Executable Properties”配置界面。
(3)进入“Profiling Options”配置界面,配置项按默认配置
完成上述配置后单击窗口右下角的“Start”按钮,启动Profiling。工程执行完成后,MindStudio自动弹出Profiling结果视图。
先来看下全量迭代耗时数据,在Timeline视图下查看Step Trace数据迭代耗时情况,识别耗时较长迭代进行分析。注意,可选择导出对应迭代Timeline数据,单击耗时较长迭代按钮弹出对话框,单击“Yes”导出对应迭代Timeline数据。如图所示,如果最初看不见,建议将鼠标放到图上,之后滑动放大,就能看见了。
还可以查看迭代内耗时情况:存在较长耗时算子时,可以进一步找算子详细信息辅助定位;存在通信耗时或调度间隙较长时,分析调用过程中接口耗时。如图所示。
在界面下方还能查看对应的算子统计表:查看迭代内每个AICORE和AICPU算子的耗时及详细信息,进一步定位分析算子的Metrics指标数据,分析算子数据搬运、执行流水的占比情况,识别算子瓶颈点。
此外,这里还能查看组件接口耗时统计表:查看迭代内AscendCL API和Runtime API的接口耗时情况,辅助分析接口调用对性能的影响。
这个要说一下,限于屏幕大小,上述这次数据的完整展示,可能需要放大或缩小,或者调整某些部分大小,这些操作很卡顿,操作起来没什么反应,仿佛这个界面卡死了,可用性差,用户体验不好,建议优化一下。我们可以看到下图中这个Op的Task Wait Time要9.895us,而其他Op基本为0,所以可以考虑试试能不能减少这个Wait Time,从而提升性能。
这里说一下,上图中这个Op Name很长,我需要将这栏横向拉伸很长才能完整显示,这有点麻烦,我本来想将鼠标悬停此处,让其自动显示完整名称,但好像不行,能否考虑加一下这个悬停显示全部内容的操作,否则我要先拉伸看,之后再拉回去看其他,比较麻烦。还记得之前我们在专家系统工具那时提到的MTE2瓶颈相比之下有些大(约0.39)嘛?这里从下图看到Mte2时间较长,约3.7ms,说一下,mte2类型指令应该是DDR->AI Core搬运类指令,这里我们量化一下看看,如下图所示,AI Core时间约为6.8ms,但Task Duration约12.5ms,几乎翻倍了,说明我们Task只有约一半时间是真正用于AI Core计算,很明显很多时间被浪费了。
说明这一点还是比较值得优化的。而各个工具之间也是可以相互辅助,相互验证更好地帮助我们定位问题。下面我们再看看,先确认下Timeline颜色配置调色板,如下图所示。而我们之前看到的Timeline基本是绿色,未见黄色或红色,估计没什么优化空间了。还是优先做明显地值得优化地点吧,这也算抓大放小吧。
好了,我们再看点其他的,比如Analysis Summary,这里展示了比较详细的软硬件信息,比如Host Computer Name、Host Operating System等,还有我们ECS上用的CPU的详细信息,包括型号、逻辑核心数量等,令我感兴趣的是,还有Ascend 310的信息,包括AI Core数量2, AI CPU数量4,Control CPU数量4,Control CPU Type为ARMv8_Cortex_A55,以及一个TS CPU,很详细了,很好地展示了Ascend 310的内部硬件信息,更有助于我们了解这款处理器。
同时,再来看看Baseline Comparison吧。
总的来说,MindStudio提供了Host+Device侧丰富的性能数据采集能力和全景Timeline交互分析能力,展示了Host+Device侧各项性能指标,帮助用户快速发现和定位AI应用、芯片及算子的性能瓶颈,包括资源瓶颈导致的AI算法短板,指导算法性能提升和系统资源利用率的优化。MindStudio支持Host+Device侧的资源利用可视化统计分析,具体包括Host侧CPU、Memory、Disk、Network利用率和Device侧APP工程的硬件和软件性能数据。很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识