分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019

技术文章列表

  • 大数据 13、推荐算法-CF

    前文链接:https://write-bug.com/article/2376.html
    推荐算法在大数据Hadoop平台2-2、MapReduce和上节的实践中都提到了检索或推荐系统的数据来源的底层来源,那么这里即是召回阶段的算法。算法目的是侧重召回候选集合,粗排。
    我们在看爱奇艺网站时,上面会有一些推荐的热榜栏目和个性化推荐栏目等,那么这里的基础算法是什么样的呢?
    推荐算法简单分为两种:基于内容和协同过滤(行为)推荐
    1.1 基于内容Content base1.1.1 引入Item属性的Content Based推荐
    在前面介绍过的实践中,我们利用token实现item的倒排,其实就类似找到了一些item和item之间的相关性信息。我们做的正排表的分词就是此图内容分析索引,而相关性计算就是倒排索引,把它保存在nosql后就可以直接查询了。

    通常在网站主页中,不会做大面积的个性化推荐,其透漏着隐私问题会给用户带来困扰。
    1.1.2 引入User属性的Content Based推荐

    这里我们需要把item的token改成可以描述我用户行为偏好的token。
    个性化:

    Step1:user搜索、点击、收藏过这些item时(也就是用户行为数据),可以用这些item的token 给 (用户分析) user做用户画像(兴趣建模)
    Step2:—->倒排索引—-》token—-》索引—->item
    Step3:相关计算cos、jaccard ∩/∪等
    Step4:排序

    1.2 基于协同Collaboration Filtering核心:User对Item的打分形成UI稀疏矩阵(即只存储有行为的数据)。
    存储可以类比原来的倒排索引:userid(key)—->(value)item item item
    原来的倒排:一个token被几个item同时包含。
    这里的倒排:一个用户对几个item有过行为,即被一个用户作用过的几个item有着相关性。

    那稀疏矩阵是什么样子呢?

    数据例:
    1,100001,5 1,100002,3 1,100003,4 1,100004,3 1,100005,3 1,100007,4 1,100008,1 1,100009,5 1,1000011,2这个稀疏矩阵就是UI矩阵,只存储用户有行为的数据,由此数据量可以大大减小:即倒排索引。
    随着时间的推移,日志服务器会积累越来越多的行为数据形成UI矩阵。通常中型以上的互联网公司,用户量最少一亿以上(一个用户多个账号),音乐量级几十万以上,所以这个UI矩阵很大很大,需要用稀疏矩阵存储。

    由这个UI矩阵我们知道,userA对ItemBCD有过行为,那么由前面倒排索引的知识,我们可以得到itemA被哪些用户有过行为,得到了userlist,那我把ItemB为key就得到了UserA的好友列表。例:微博
    根据user A的好友列表userBCD,可以得到每个user背后的item列表,把这些item聚合推荐给userA。例:个性化推荐
    根据itemA背后的user列表做相似度关联,把每个用户背后的item聚合,在和itemA相关联。例:非个性化推荐


    可由1我们得到user和user的矩阵即UU=UI*IU(UI的转置矩阵);可由3我们得到item和item的矩阵即II=IU*UI。由此我们引出了两种协同:User-Base CF和Item-Base CF。
    1.2.1 user-Base —- CF:—————外交假设:
    – 用户喜欢那些跟他有相似爱好的用户喜欢的东西
    – 具有相似兴趣的用户在未来也具有相似兴趣
    方法:
    – 给定用户u,找到一个用户的集合N(u),他们和u具有相似的兴趣
    – 将N(u)喜欢的物品推荐给用户.

    UI矩阵计算——————》 UU矩阵—————-用户之间相似度
    流程:转置,归一,cos
    目的:如果把一个user没看过的电影推荐给他,打一个分预测用户,对这个电影有多大的兴趣

    r(userid,itemid)=两个用户的相似度×另一个用户的打分 / 所有用户(随机选择一部分)相似度加和这个结果,只需要基于同一套算法(UU / II)的排序,不需要具体分数。
    1.2.2 item -Base —- CF:———自身历史行为假设:
    – 用户喜欢跟他过去喜欢的物品相似的物品
    – 历史上相似的物品在未来也相似
    方法:
    – 给定用户u,找到他过去喜欢的物品的集合R(u)
    – 把和R(u)相似的物品推荐给u
    UI矩阵计算——————》 II矩阵—————电影之间相似度
    流程:转置,归一,cos
    目的:如果把一个user没看过的电影推荐给他,打一个分预测用户,对这个电影有多大的兴趣

    r(userid,itemid)=两个item的相似度×该用户的打分 / 所有item(随机选择一部分)相似度加和什么时候用哪个算法?

    性能角度:谁维度小用谁
    领域:UB时效性(看是否火,用户之间相互影响),IB倾向于个性化(历史相似物品)
    实时性:IB更高:新行为导致推荐结果实时变化
    冷启动:UB需要时间离线计算,IB围绕产生新行为的物品无限推荐
    推荐理由:UB难解释

    冷启动三类:—————-新东西——粗粒度吸引

    用户:推荐热门排行榜、注册年龄性别等做画像粗粒度推荐、社交网站导入好友信息推荐、新登陆反馈标签推荐
    物品:新物品通过基于内容推荐——-CB
    系统:不太好解释的,用户画像(职业)和物品(不懂这东西)———引入专家知识(知识图谱,人工校验等)建立相关物品矩阵

    I I/UU 相似度计算公式:———————cos

    类比cos=向量内积 / 向量模 ------把两个劈开相乘
    ——————> 归一化 ui / ui^2———————->两个Rui 评分相乘
    比如上面的两个电影 M 和 T 的相似度是0.57
    U(i,j)=A B D
    R(ua,M)=5 , R(ua,T)=1R(ub,M)=1 , R(ub,T)=5R(ud,M)=4 , R(ud,T)=3归一化:
    25+1+16=42---根号42M:5/1/4/1+25+9=35------根号35T:1/5/3/M*T=实践:协同过滤————————给定一个item,推荐相关的item集合(II矩阵)
    倒排式、分块式
    倒排式流程:
    遍历排列组合同一个人观看的所有item,得到很多相似度的pair;如果两个物品被越多的人贡献过,相似度越准确,去除偶然性误点。也就是排列出所有组合pair对,-----有几个相同对,相当于被几个人贡献过。相同的pairkey会分在同一个桶里,计算相似度分数就会越准确---套公式(reduce中)

    Step1:归一UI矩阵:

    Map:
    矩阵输入数据:U,I,S输出:Key(i),Value(u,s)对
    Reduce:
    输入:Key(i),Value(u,s)对输出:Key(u),Value(i,s_new)对

    Step2:衍生 I I Pair对:

    Map:
    输入:Key(u),Value(i,s_new)对输出:Key(u),Value(i,s_new)对
    Reduce:
    输入:Key(u),Value list((i,s_new))对输出:Key_1(i) , Value_1(j,s_new_is_new_j)Key_2(i) , Value_2(i,s_new_is_new_j)

    Step3:result :

    Map:
    输入: Key(i) , Value(j,s_new_i*s_new_j)输出:Key<i,j> , Value(s_new_i*s_new_j)
    Reduce:
    输入:Key<i,j> , Value list((s_new_i*s_new_j))输出:Key(i) , Value(j,score)


    前面说了一些最基础的推荐算法,但是其中存在了一些问题,我们会在后面更新基础的聚类算法中指出。并且在其中我们使用倒排式实现了协同过滤,突出了MR批量计算和对大规模UI矩阵的批量处理,但在其中代码的缺点也很明显,我们输入的UI矩阵产生pair对时,会产生比UI大得多的数据,并会有一些重复数据,很容易爆掉内存,所以我们需要优化在UI矩阵中取物品需要对每一个用户的物品候选集合设置阈值,也就是pair对多少的阈值,如果说一个用户点击了几乎所有item,pair数据指数级增长,内存就会立刻爆掉。后面:聚类、MF(ALS、LFM、SVD、SVD++)、ANN
    2 留言 2019-04-25 11:23:42 奖励20点积分
  • 【Cocos Creator实战教程(6)】——镜头跟随

    1. 知识点讲解Camera就是标记一块区域,这块区域就是镜头范围。
    拿拍电影来举例子,当拍一个场景时,场景里常常有主角,而主角常常有主角光环,所以摄像机的镜头就要对准主角,镜头要跟着主角移动。
    这里的镜头移动方式有两种。一种是中心移动,也就是以主角为中心,主角移动镜头就同步移动;另一种是边界移动,这种移动方式下我们给镜头设定一个范围,当主角移动出这个范围时,摄像机才移动。
    我们这里用的是边界移动的方式(中心移动的我也试了一下,真的有点晕)。
    2. 步骤新建一个Camera节点并且添加脚本,改变位置。
    CameraManager2.js
    cc.Class({ extends: cc.Component, properties: { bgSky:cc.Node, skySca:0, bgHill:cc.Node, hillSca:0, bgHillnear:cc.Node, hillnearSca:0, bgFloor:cc.Node, floorSca:0, cloudLayer:cc.Node, cloudSca:0, camera: cc.Node, player: cc.Node, }, init: function(game){ this.game = game; }, moveBg: function(distance){ //当主角跳出镜头边界时镜头才移动 if(this.player.y > this.camera.y+this.camera.height/2){ this.bgSky.y -= distance * this.skySca; this.bgHill.y -= distance * this.hillSca; this.bgHillnear.y -= distance * this.hillnearSca; this.bgFloor.y -= distance * this.floorSca; this.cloudLayer.y -= distance * this.cloudSca; this.player.y -= distance; } }});
    3. 总结如果大家想对两种视角有个更好的了解,可以试试英雄联盟的视角锁定,自由视角和中心视角的切换按钮是Y。
    这几次的知识点并成一个游戏给大家。
    2 留言 2018-11-27 01:36:29 奖励20点积分
  • ring0层下实现的文件强制删除

    7.6 文件强删在日常生活使用电脑的过程中,遇到删不掉的文件的时候,我们总会通过一些软件提供的强制删除文件功能来删除顽固的文件。文件强删技术对于杀软来说是清楚病毒木马的武器,在扫描器检测出恶意文件的时候,就需要强删功能来清除恶意文件。而对于病毒木马来说,反过来可以用强删技术来强制删除系统保护文件或是受杀软保护的文件。
    本文将会介绍文件强删技术,即使文件正在运行,也能强制删除本地文件。
    实现过程当文件是PE文件而且已经被加载到内存中的时候,正常情况下是无法通过资源管理器explorer.exe来删除本地文件的,因为在删除运行中的文件或者被加载的DLL文件的时候,系统会调用MmFlushImageSection内核函数来检测文件是否处于运行状态,若是,则拒绝删除操作。其中,系统中的MmFlushImageSection内核函数,主要是通过检查文件对象中的PSECTION_OBJECT_POINTERS结构数据来判断该文件是否处于运行状态、是否可以删除。
    同时,在发送IRP删除文件的时候,系统同样会判断文件的属性是否是只读,若是只读属性则会拒绝删除操作。
    所以,根据上述的删除原理,文件强制删除的具体实现流程如下所示。
    首先,发送IRP打开删除文件,并获取文件对象。
    然后,发送IRP设置文件属性,属性类型为FileBasicInformation,将文件属性重新设置为FILE_ATTRIBUTE_NORMAL,防止原来的文件属性为只读属性。并保存文件对象中的PSECTION_OBJECT_POINTERS结构的值,保存完成后,再对该结构进行清空处理。
    接着,发送IRP设置文件属性,属性类型为FileDispositionInformation,实现删除文件操作。这样,即使是运行中的文件也能被强制删除。
    最后,还原文件对象中的PSECTION_OBJECT_POINTERS结构,并调用ObDereferenceObject函数释放文件对象,完成清理工作。
    那么,实现文件强制删除的实现代码如下所示。
    // 强制删除文件NTSTATUS ForceDeleteFile(UNICODE_STRING ustrFileName){ NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT pFileObject = NULL; IO_STATUS_BLOCK iosb = { 0 }; FILE_BASIC_INFORMATION fileBaseInfo = { 0 }; FILE_DISPOSITION_INFORMATION fileDispositionInfo = { 0 }; PVOID pImageSectionObject = NULL; PVOID pDataSectionObject = NULL; PVOID pSharedCacheMap = NULL; // 发送IRP打开文件 status = IrpCreateFile(&pFileObject, GENERIC_READ | GENERIC_WRITE, &ustrFileName, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(status)) { DbgPrint("IrpCreateFile Error[0x%X]\n", status); return FALSE; } // 发送IRP设置文件属性, 去掉只读属性, 修改为 FILE_ATTRIBUTE_NORMAL RtlZeroMemory(&fileBaseInfo, sizeof(fileBaseInfo)); fileBaseInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; status = IrpSetInformationFile(pFileObject, &iosb, &fileBaseInfo, sizeof(fileBaseInfo), FileBasicInformation); if (!NT_SUCCESS(status)) { DbgPrint("IrpSetInformationFile[SetInformation] Error[0x%X]\n", status); return status; } // 清空PSECTION_OBJECT_POINTERS结构 if (pFileObject->SectionObjectPointer) { // 保存旧值 pImageSectionObject = pFileObject->SectionObjectPointer->ImageSectionObject; pDataSectionObject = pFileObject->SectionObjectPointer->DataSectionObject; pSharedCacheMap = pFileObject->SectionObjectPointer->SharedCacheMap; // 置为空 pFileObject->SectionObjectPointer->ImageSectionObject = NULL; pFileObject->SectionObjectPointer->DataSectionObject = NULL; pFileObject->SectionObjectPointer->SharedCacheMap = NULL; } // 发送IRP设置文件属性, 设置删除文件操作 RtlZeroMemory(&fileDispositionInfo, sizeof(fileDispositionInfo)); fileDispositionInfo.DeleteFile = TRUE; status = IrpSetInformationFile(pFileObject, &iosb, &fileDispositionInfo, sizeof(fileDispositionInfo), FileDispositionInformation); if (!NT_SUCCESS(status)) { DbgPrint("IrpSetInformationFile[DeleteFile] Error[0x%X]\n", status); return status; } //还原旧值 if (pFileObject->SectionObjectPointer) { pFileObject->SectionObjectPointer->ImageSectionObject = pImageSectionObject; pFileObject->SectionObjectPointer->DataSectionObject = pDataSectionObject; pFileObject->SectionObjectPointer->SharedCacheMap = pSharedCacheMap; } // 关闭文件对象 ObDereferenceObject(pFileObject); return status;}
    测试在64位Windows 10操作系统上,运行C:\520.exe程序后,直接加载并运行上述驱动程序,强制删除正在运行的520.exe文件。520.exe本地文件成功被删除,如图7-6所示。

    小结本文介绍的文件强删技术的实现原理是,通过发送IRP打开文件,获取文件对象;发送IRP设置文件属性,去掉只读属性;清空文件对象中的PSECTION_OBJECT_POINTERS结构,使运行中的程序文件能够被删除;最后发送IRP删除文件并释放文件对象。
    本文介绍的文件强删技术可以删除正在运行的exe文件,但是不适用于前面介绍的发送IRP打开文件不关闭的文件保护方法。要想强制删除发送IRP打开文件不关闭的文件保护,需要关闭打开的文件对象。可以利用XCB解锁技术,通过硬编码定位出文件对象中SCB(Stream Control Block)、FCB(File Control Blokc)、CCB(Context Control Block)结构的CleanupCount变量,并将CleanupCount都置为1,再调用ObDereferenceObject函数关闭文件对象并释放资源。XCB解锁技术不仅可以解锁发送IRP打开文件的保护方式,同样适用于硬链接文件保护。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2019-04-28 10:04:32
  • python数据分析(4)——数据预处理(上)

    数据预处理的主要内容包括数据清洗、数据集成、数据变换和数据规约。

    1 数据清洗数据清洗主要是删除原始数据集中的无关数据、重复数据,平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值、异常值。
    1.1 缺失值处理方法主要分为删除记录、数据插补和不处理,其中常用的数据插补方法如下。

    这里主要介绍拉格朗日插值法和牛顿插值法。其他的插值方法还有Hermite插值、分段插值和样条插值。


    P(x)是牛顿插值逼近函数,R(x)是误差函数。
    3)将缺失的函数值对应的点x代入插值多项式得到缺失值的近似值f(x).
    牛顿插值法也是多项式插值,但采用了另一种构造插值多项式的方法,与拉格朗日插值相比,具有承袭型和易于变动节点的特点。从本质上来说,两者给出的结果是一样的(相同次数、相同系数的多项式),只不过表示的形式不同。因此,在Python的Scipy库中,只提供了拉格朗日插值法的函数(因为实现上比较容易),如果需要牛顿插值法,则需要自行编写函数。
    例子是catering_sale.xls
    #拉格朗日插值代码import pandas as pd #导入数据分析库Pandasfrom scipy.interpolate import lagrange #导入拉格朗日插值函数inputfile = 'catering_sale.xls' #销量数据路径outputfile = 'sales.xls' #输出数据路径data = pd.read_excel(inputfile) #读入数据data[u'销量'][(data[u'销量'] < 400) | (data[u'销量'] > 5000)] = None #过滤异常值,将其变为空值#自定义列向量插值函数#s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5def ployinterp_column(s, n, k=5): y = s[list(range(n-k, n)) + list(range(n+1, n+1+k))] #取数 y = y[y.notnull()] #剔除空值 return lagrange(y.index, list(y))(n) #插值并返回插值结果#逐个元素判断是否需要插值for i in data.columns: for j in range(len(data)): if (data[i].isnull())[j]: #如果为空即插值。 data[i][j] = ployinterp_column(data[i], j)data.to_excel(outputfile) #输出结果,写入文件
    1.2 异常值处理
    我们一般将异常值视为缺失值进行插补。
    python中判断每个元素是否空值/非空值

    D.isnull/notnull()

    2. 数据集成2.1 实体识别实体识别是指从不同数据源识别出现实世界的实体,它的任务是统一不同源数据的矛盾之处。
    2.1.1 同名异义数据源中同样是属性ID,不一定是同一实体。
    2.1.2 异名同义不同数据源中不同名字的数据项表示是同一实体。
    2.1.3 单位不统一描述同一个实体分别用的是国际单位和中国传统的计量单位。
    检测和解决这些冲突就是实体识别的任务。
    2.2 冗余属性识别
    同一属性多次出现同一属性命名不一致导致重复
    有些冗余属性可以用相关分析检测。
    python去除数据中的重复元素

    D.unique()
    np.unique(D)
    1 留言 2018-12-25 14:24:18 奖励15点积分
  • 5G将渗透哪些领域

    5G爆发前夕将渗透哪些领域?5G 已经被吹捧为许多细分市场的新的赋能者,它将给移动电话、自动驾驶、虚拟现实(VR)和物联网等行业带来机会。但是,这个新的无线通信标准将在何时和将以何种方式影响这些细分市场,以及它将对半导体设计产生什么样的影响,目前尚有很多问题和不确定性。
    基于 5G 承诺的通信速度的大幅度提高和延迟的大幅度降低,系统供应商必须在将数据处理放在本地或在云端之间作出决定。这将对半导体体系结构产生重大影响,包括处理器和内存的片上吞吐量、I/O 速度、功率预算,甚至电池大小等等。此外,这些决定还将受到 5G 基础设施接入和通信频率的影响。
    不过,这些事情可能要等到数年后才会发生。一开始,大部分的 5G 应用将在低于 6GHz 的范围内,也就是和 4.5G 相当。最大的受益者将是移动电话行业,它将在一段时间内仍然是 5G 技术的最大消费者。移动电话的标准和存储容量将会因为 5G 技术的不断引入而持续进化,它们将依然是 5G 技术的发展的重要资金来源。
    下一个阶段的发展是在毫米波技术引进之时。这时候最重要的变化将开始发生。一般的经验法则是,任何一项新技术想要成功,必须提供10倍的增益,要么是性能提高、或功率降低、成本降低、面积更小或者多种增益的组合。只有在那个时候,5G 技术才会真正地大放光彩。
    “5G 将在连接性能方面提供显著的改进,其目标是比目前 4G 的连接性能提高1000倍”。Steven Woo 说,他是 Rambus 实验室负责系统和解决方案的副总裁、杰出的发明家。 “除了带宽方面的改进,5G 还承诺了降低延迟和更好的覆盖范围。”
    在毫米波技术的应用初期,设计成本和硅晶元出货面积将会显著上升。电力消耗将成为主要的问题,这取决于基础设施和它能承受的负荷压力,因为它们需要承担信号发送和信号到达时的计算工作。
    物联网一个真正能从 5G 中受益的领域是边缘计算,其中功耗是一个限制因素。“我们希望 5G 的引入能使物联网边缘设备的功耗更低,因为平均来说 5G 拉近了它们与接入点的距离。” Cadence 的产品营销总监 Neil Robinson 说:“这意味着与 4G 通信所需要的更长距离相比,5G 通信所需要的功耗更低”。
    这样就打开了一扇大门,让我们有能力处理比目前更加复杂的处理流程和通信方案。Woo 说:“5G 将提供更高的带宽,这意味着越来越多的终端(endpoint)将会更容把它们的数据传输到相邻位置,在那里这些数据得以在本地处理。”这意味着只有少量的,级别更高的数据/信息,才需要传输到云端处理。
    但这种方案也会很快变得复杂。“5G 所需的高带宽和多天线策略意味着,从本质上讲,它们需要更高的能耗。”西门子仿真部门市场营销高级总监 Jean-Marie Brunet 指出。“然而,人们普遍认为物联网的大部分将是机对机(M2M)通信。而机对机通信模式比人工启动的物联网更容易预测,因此机对机实例的低功耗算法应该更高效,这种说法是有争议的。”
    实际上的好处将随着不同的应用和区域有所不同。“归根结底,5G 将使每比特功耗降低10倍到100倍,同时带来相当于10倍的带宽增幅,这些变化将在元器件的寿命方面产生净增益,并同时提高10倍的功效。因使用模式的不同这些好处会有所不同。”
    这是否足以影响架构?物联网边缘设备已开始将推理运算本地化,以避免与向云端传输大量原始数据而带来的带宽影响。
    “更高的带宽和更低的延迟将使在云端进行推理运算变得更容易,如果我们需要这样做的话。” Cadence 的 Robinson 说。“但是,隐私、安全、延迟和功耗问题可能会让这种方式变得不合适。”
    Cadence 的 Tensilica IP 产品管理和营销高级总监 Lazaar Louis 也回应了这些担忧。“对隐私和安全问题的担忧将会使推理运算继续留在边缘设备进行,”他说。“将传感器收集到的信息传送到云端会消耗能量,因此在边缘设备进行推理运算,可以节省在边缘设备上消耗的能量。”
    Rambus 实验室的 Woo 同意这一说法。“更高的带宽可能不能排除在边缘设备上进行推理运算的必要性,但它们将允许更大数量的物联网设备相互连接,使基础设施能够跟上不断增长的物联网设备的需求,并使其捕获和传输的数据量不断增长。”
    VR(虚拟现实)和AR(增强现实)虚拟现实(VR)碰到了难题。如果没有更高的数据速率,设备供应商将很难消除晕眩不适感,这大大地限制了 VR 的使用。毫米波技术可以帮助解决这个问题。虽然毫米波信号不能通过墙壁,只能在相对较短的距离内工作,但 VR 耳机和控制器的距离通常只有几英尺远。
    “与以前运行同一款游戏的 4G 产品相比,高分辨率的 8K 游戏流媒体肯定会更快地将电池的电量耗光。” Brunet 承认 “但是这并不是 5G 造成的,而是因为更高级的 CPU 和显示设备。在毫米波频率下,对于空中下载(OTA)的功耗需求,收发器的功耗将会以加速度级地增加。”
    自动驾驶自动驾驶需要将许多技术结合在一起,5G 就是其中之一。“更低的延迟为具有自主驾驶能力的联网车辆带来了好处,在这种情况下,响应时间至关重要,尤其是在高速公路的行驶速度下。”Woo说道。“5G 覆盖范围的改进,加上增强的本地处理能力,将允许在数据产生的终端设备附近对数据进行聚合和处理,从而减少数据传输。减少长距离的数据传输对于 5G 来说是一个重要的好处,因为它可以同时改进了延迟和功耗。”
    汽车很可能成为通讯枢纽。“汽车将是微型发射器,” Robinson 说。“和现在的 802.11p(V2V)和4G/OnStar(V2X)面临的情况类似,5G 也面临着来自电视网络的阻挠,他们希望使用已经分配给 V2V(车辆对车辆)的相同频段。他们声称,自动驾驶不需要 V2V,在路上使用 V2X(车辆对所有)就可以来了解其他车辆的存在/意图。”
    这个观点得到其他人的同意。“车辆到所有(V2X)将主导通信,车辆将成为拥有很多发射器的移动网络,在许多情况下,每个功能域都有多个发射器。” Brunet 说:“V2X 将是实现安全的关键因素,因为激光雷达或雷达根本看不到拐角处,而 5G 可以依据拐角处的反射将这种实现变得可能。”
    到底是从其他车辆还是从路边的信息中得到这些信息,现在尚没有明确的答案。“汔车能获得的信息越多,它们就越能在自动驾驶体验方面做出更好的决定。” Cadence 公司的 Louis 指出。 “更先进的自动驾驶将得益于和其他车辆/基础设施的 V2X 通信,例如路线规划和车道变更辅助。”
    新的通信能力也可能带给我们今天还没有想到的好处。“联网车辆只是众多可能受益的设备之一。” Woo 说。“更高的带宽和更高的覆盖率,再加上更强的本地处理能力,将帮助车辆与周围环境以及本地地图数据进行通信,从而在将来实现道路导航。今天的联网汽车已经捕获了大量的数据,其中一些信息被传送到云端。预计汽车将继续发展而成为信息枢纽,因为它们可以作为乘客设备的连接点(就像手机可以作为智能手表和健身设备等外围设备的连接点一样),并与其他车辆进行点对点通信,以交流像车道变化之类的预期行动,以及交通状况和危险信息。”
    实施注意事项今天大多数 5G 的实现都还只是原型,而且并不是所有的问题都已经得到解决。例如,5G 可以提供每秒10到20千兆位的传输速度,但数字系统必须具备相应的工作速度才能受益于 5G 的高速度。如果为了降低成本而仍然使用旧的工艺节点生成数字信息,这可能使得能使用的工艺节点变得有限,或者我们需要更先过的封装解决方案。
    “这是摩尔定律的一个结合,它减慢了速度,同时增加了芯片的复杂性和所需要的工艺的复杂性。。” Rambus 的产品管理高级主管 Frank Ferro 表示:“你不需要做一个大型的 ASIC,你必须问一下分解它是否更划算。做两个更小的 ASIC 或重新使用你已经进行了大量投资的混合信号器件是否更便宜?如果你已经在高速工艺技术上进行了投资,你想继续扩大规模吗?或者,您可以使用现有的技术,加上接口技术,而不必每次更改工艺节点时都开发 SerDes 呢?“
    同样,不同的产品领域可能会得出不同的结论。“我不知道 5G 节点的期望值。” Robinson 说道。“这将归结为每个产品或公司的成本
    1 留言 2019-04-22 10:31:20 奖励15点积分
  • Ring0内核层下实现删除文件或空目录

    背景本文讲解的文件的基础操作就是指对文件进行增、删、改、查等基础的操作,这也是管理文件常用的操作。在应用层,我们都是直接使用 WIN32 API 来直接操作。到了内核层,虽然不能使用 WIN32 API,但是 Windows 也专门提供了相应的内核 API 给我们开发者使用,我们直接调用内核 API 就能实现我们的功能。
    现在,我就单独将文件基础管理的常用操作一一解析,每个技术点都形成独立文档,来向大家讲解下怎么使用内核 API 在内核下创建文件或目录、删除文件或目录、 计算获取文件大小、文件的读和写操作实现文件复制、文件重命名、文件查询遍历等。
    本文主要讲解的是,使用内核 API 删除文件或空目录的实现原理和过程。
    函数介绍InitializeObjectAttributes 函数
    InitializeObjectAttributes宏初始化不透明OBJECT_ATTRIBUTES结构,该结构指定了打开句柄的例程的对象句柄的属性。
    函数声明
    VOID InitializeObjectAttributes( [out] POBJECT_ATTRIBUTES InitializedAttributes, [in] PUNICODE_STRING ObjectName, [in] ULONG Attributes, [in] HANDLE RootDirectory, [in, optional] PSECURITY_DESCRIPTOR SecurityDescriptor);
    参数

    InitializedAttributes [out]指定要初始化的OBJECT_ATTRIBUTES结构。ObjectName [in]指向Unicode字符串的指针,该字符串包含要打开句柄的对象的名称。这必须是完全限定的对象名称,或者是由RootDirectory参数指定的对象目录的相对路径名。Attributes[in]指定一个或多个以下标志:OBJ_INHERIT该句柄可以由当前进程的子进程继承。OBJ_PERMANENT此标志仅适用于在对象管理器中命名的对象。默认情况下,当所有打开的句柄关闭时,这些对象将被删除。如果指定了此标志,则当所有打开的手柄都关闭时,该对象不会被删除。驱动程序可以使用ZwMakeTemporaryObject删除永久对象。OBJ_EXCLUSIVE此对象只能打开单个句柄。OBJ_CASE_INSENSITIVE如果指定了此标志,则在将ObjectName参数与现有对象的名称匹配时使用不区分大小写的比较。否则,使用默认系统设置比较对象名称。OBJ_OPENIF如果将此标志指定给创建对象的例程,并且该对象已经存在,则例程应该打开该对象。否则,创建对象的例程将返回STATUS_OBJECT_NAME_COLLISION的NTSTATUS代码。OBJ_KERNEL_HANDLE指定句柄只能在内核模式下访问。OBJ_FORCE_ACCESS_CHECK打开句柄的例程应强制执行对象的所有访问检查,即使在内核模式下打开句柄。RootDirectory [in]在ObjectName参数中指定的路径名的根对象目录的句柄。如果ObjectName是一个完全限定的对象名称,则RootDirectory为NULL。使用ZwCreateDirectoryObject获取对象目录的句柄。SecurityDescriptor [in]指定在对象创建时应用于安全描述符。此参数是可选的。驱动程序可以指定NULL来接受对象的默认安全性。
    返回值

    无返回值。

    ZwDeleteFile 函数
    删除指定文件。
    函数声明
    NTSTATUS ZwDeleteFile( _In_ POBJECT_ATTRIBUTES ObjectAttributes);
    参数

    ObjectAttributes [in]指向OBJECT_ATTRIBUTES结构的指针,其中包含调用者为文件对象提供的属性。 这些属性将包括ObjectName和SECURITY_DESCRIPTOR。 该参数通过调用InitializeObjectAttributes宏来初始化。
    返回值

    成功时,返回STATUS_SUCCESS;失败时,返回适当的NTSTATUS错误代码。

    实现原理对于文件的删除,在应用层上我们使用 DeleteFile 来实现,删除空目录则使用 RemoveDirectory。而对于内核层来说,我们直接可以使用 ZwDeleteFile 函数来删除指定文件以及空目录。现在,我们给出具体实现步骤:
    首先,我们调用 InitializeObjectAttributes 宏来初始化对象属性,包括文件或者目录路径和其它属性。
    然后,再调用 ZwDeleteFile 函数,根据传入的对象属性参数删除指定对象。如果对象是个文件,则删除文件;若对象是一个空目录,则删除目录。注意,若对象是一个非空目录,则返回不操作。
    特别注意一点,内核下表示的文件或者目录路径要在路径前面加上 \??\,例如表示 C 盘下的 test.txt 文件路径:\??\C:\test.txt。
    编码实现删除文件或空目录// 删除文件或是空目录 \??\C:\MyCreateFolder\520\520.exeBOOLEAN MyDeleteFileOrFileFolder(UNICODE_STRING ustrFileName){ NTSTATUS status = STATUS_SUCCESS; OBJECT_ATTRIBUTES objectAttributes = { 0 }; InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); // 执行删除操作 status = ZwDeleteFile(&objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwDeleteFile", status); return FALSE; } return TRUE;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行;在 Win10 64 位系统下,驱动程序正常执行。
    总结ZwDeleteFile 内核函数和应用层的 DeleteFile 函数类似的,都是删除指定文件。不同的是,ZwDeleteFile 还可以删除目录,若对象是一个空目录,则删除目录。注意,若对象是一个非空目录,则返回不操作。
    特别注意一点,内核下表示的文件或者目录路径要在路径前面加上 \??\,例如表示 C 盘下的 test.txt 文件路径:\??\C:\test.txt。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2019-01-26 18:53:59 奖励8点积分
  • C语言-测试时用来快速转换数组与单链表

    数组与链表操作结构体定义typedef struct ListNode {int val;struct ListNode *next; };数组转化为单链表void getHeadNode(int a[],int length, ListNode* get) { struct ListNode *q; for (int i = 0; i < length; i++) { struct ListNode* p = (struct ListNode *)malloc(sizeof(struct ListNode)); p->val = a[i]; p->next = NULL; q = get; while (q->next != NULL) { q = q->next; } q->next = p; //如果不需要头结点则反注释 ////释放头指针 //q = get; //get = get->next; //free(q); } }输出单链表//存在头结点void outputHead(struct ListNode* h){ while (h->next!= NULL) { printf("%d ", h->next->val); h = h->next; } printf("\n");}
    1 留言 2018-11-09 20:59:52 奖励5点积分
  • python爬虫--爬取网站中的多个网页

    爬取7k7k小游戏的URL
    # -*- coding: utf-8 -*-"""Created on Sun Mar 24 10:04:58 2019@author: pry"""import requestsfrom bs4 import BeautifulSoupimport osimport reimport urllibfrom lxml import etreedef parse_page(): t = 1 headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3642.0 Safari/537.36' } for i in range(1,5): url_i = 'http://www.7k7k.com/flash_fl/461_' + str(i) + '.htm' response_i = requests.get(url_i, headers = headers) selector = etree.HTML(response_i.text, parser=etree.HTMLParser(encoding = 'utf-8')) print(url_i) content = selector.xpath('//a/@href') for i in content: if i[0] == "j": continue if i[0] == "/": i = url_i + i with open('7k7k_urls.txt','a+') as file: file.write(i) file.write("\n") file.close() print(i) t = t + 1 print(t) print('ok')if __name__ == '__main__': parse_page()
    1 留言 2019-04-25 08:45:45 奖励5点积分
  • 大数据 12、中文分词

    前文链接:https://write-bug.com/article/2367.html
    文本相似度一般在讨论相似度时,我们讨论相似度与距离之间的关系:相似度越低,距离越远
    我们这里先分为两类:
    语义相似:

    个人简介
    人物介绍

    解决方案:协同过滤(用户行为)、回归算法
    eg:用户搜索歌神时,大部分人点击张学友,由此两个词间便存在了某种联系
    字面相似:

    我吃饱饭了
    我吃不饱饭

    解决方案:LCS最长公共子序列、中文分词(词:token)
    eg:通过token列出词粒度集合,通过算法打分或者取交集求取相似度
    解决字面相似:
    1.cosine余弦相似度
    以前在中学学习余弦时都是基于二维坐标系:
    cos(θ)=a·b/|a|* |b|我们词粒度的维度是由token数量决定的,由此我们拓展到n维得:

    • 句子A:(1,1,2,1,1,1,0,0,0)
    • 句子B:(1,1,1,0,1,1,1,1,1)

    a,b即通过分词后得到的词粒度集合向量化
    注意:由分词得到的去重集合(BOW bag of word)一旦列举出后顺序不能变
    计算流程:

    通过TFIDF找出两篇文章的关键词
    每篇文章各取出若干个关键词,合并成一个集合,计算每篇文章对于这个集合中的 词的词频
    生成两篇文章各自的词频向量
    计算两个向量的余弦相似度,值越大就表示越相似

    TFIDF:找关键词
    1)TF(Term Frequency):词频
    即假设如果一个词很重要,那么它在这篇文章中会多次出现。但是,出现最多的不一定是最重要的,比如停用词:“的”“是”“在”。。。(可提前过滤)那么再次假设关键词:在当前文章出现较多,但在其他文章出现少。
    公式:
    TF1=某词在文章中出现次数 / 文章词总数(基数大) 越大越重要TF2=某词在文章中出现次数 / 该文章次数最多的词的出现的次数(基数小)TF越大,越重要
    2)IDF:反文档频率,即某一个词在某些文章中出现个数多少的衡量
    公式:
    log(语库文档总数 / 包含该词的文档数+1) > 0单调函数,x越大,y越平滑。出现少,分母越小,IDF越大,词越重要。
    score= TF*IDF像停用词这样的词,TF大,IDF小
    拓展:自动摘要
    1)确定关键词集合
    两种方法:
    1.score—Top10
    2.阈值截断>0.8
    2)那些句子包含关键词,取出这些句子
    3)对关键词排序,对句子做等级划分(含该关键词的分数线性加和)
    4)把等级高的句子取出来,就是摘要
    优化:关键词算法、中心思想、第一末尾自然段等等。
    tfidf:

    优点:简单快速,结果比较符合实际状况
    缺点:缺少通顺度,而且关键词不是唯一的权重,比如开头结尾,中心思想。

    单纯以“词频”做衡量标准,不够全面,有时重要的词可能出现的次 数并不多
    实践:
    1.分词
    2.数据预处理,把所有文章内容全部收集到一个文件中,每行为一篇文章
    convert.py input_tfidf_dir/ >convert.data3.MR批量计算IDF

    map.py: word list去重,每一个单词在一篇文章中出现一次计1
    reduce.py: wordcount + IDF计算

    得到token,score集合
    排序:cat part-00000 |sort -k2 -nr |head
    2.LCS 最长公共子序列:顺序性,不连续
    从字面的角度,衡量字面相似度的方法之一
    应用:

    推荐场景 , item推荐,推荐列表—-保持多样性,满足新鲜感需求,放在推荐引擎中可方便调控相似度阈值过滤(即候选集合后)
    辨别抄袭
    生物学家常利用该算法进行基因序列比对,以推测序列的结构、功能和演化过程

    – 字符串12455与245576的最长公共子序列为2455
    – 字符串acdfg与adfc的最长公共子序列为adf
    计算方法:
    暴力穷举:abc排列组合,取LCS,穷举搜索法时间复杂度O(2^m \∗ 2^n);
    动态规划法:
    抽象公式:

    当xy两字符串最后一位相等时+1,并循环求lcs,不相等时求左右各减一位的最大值,如果有一方为空循环停止,值为0
    代码二维数组:

    i和j为行和列,从上到下从左到右看,最开始都为空,行列都为0,接着从左到右有相同项左上角+1,不相同的左和上取最大值,最后得出最大序列数
    score=4len(x)=7len(y)=6sim(x,y)=4*2/6+7=0.615*2为了归一化实践:
    通过数组维护tokenlist,左右两个字符串按照上面形式计算。
    lcs适合粗粒度过滤,适合场景没有cos广泛因为机器学习中涉及到cos的地方太多了
    3.中文分词中文并不像英文,每个单词自动空格分隔,并且没有一个衡量标准好坏问题,词粒度分割不同,意思也就不同。
    我们前面一直提到搜索和推荐场景,在这里,推荐适合粗粒度的分词,因为需要关心词的语义去推荐,而搜索适合细粒度的分词场景,侧重于召回更多的item到候选集合。
    表示方法:
    那我们如果对于一句话做分词,人类可以一眼就看出来,那么对于计算机来说怎么表示,那我们假设,切开位置表示为1,其他位置为0;那么这句话表示为:有/意见/分歧——》11010本田雅阁/汽车——》100010那么这种表示方法对计算机确实很友好,但是对我们来说很头疼,于是通常情况下,我们用分词节点序列表示:有/意见/分歧——》{0,1,3,5}
    那么接下来的问题,这串索引怎么得到呢?这里有一种原始方法:最大长度查找(字典匹配)这个又分两种:前向查找,后向查找。加载词典:
    在查找过程中我们如果一个词一个词去匹配未免过于耗费资源,所以这里用一个加速查找字典的方法:trie树—数据结构 查找过程如下:• 从根结点开始一次搜索;• 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;• 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。• 迭代过程……• 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。其他操作类似处理.时间复杂度 O(n)—-空间换时间数据结构,并且在python中的字典数据结构这种速度是近乎于O(1)
    匹配词典:
    前面这是加载词典,而匹配词典是下面几种方法:1、最大长度查找前向查找:先从头匹配第一个字,之后看第二个字是否在第一个字后面,以此类推,若不在则砍一刀,之后继续查找当前第一个字。后向查找:即语料库词为倒序,即反向建库通常情况下后向查找会更符合语义。2、我们把每个字都分开,之后在语料库中匹配出每种排列组合的可能,用前面说的索引序列表示,与此同时,我们把每种可能都用一条线首尾相连,而这么多条线就构成了一张图:即有向无环图,也叫DAG图。
    由此一句话的编码表示:DAG: {0: [0, 1, 3], 1: [1], 2: [2, 3, 5], 3: [3], 4: [4, 5], 5: [5], 6: [6, 7], 7: [7]}
    那么我们这些边该如何去切分和选择呢?
    由此引出:语言模型概念
    我们由语料库已经有了很多列举后的切分方案,那么我们由这些切分方案可以得到每条方案的条件概率,比如:C=本田雅阁汽车S1=本田/雅阁/汽车S2=本田雅阁/汽车则:P(S1|C)表示S1这条切分方案的条件概率
    目标:我们希望得到的就是最大概率的那条切分方案。
    那我们如何求取这条概率呢?:贝叶斯公式
    p(s|c)=p(c|s)p(s)/p(c)
    推导:p(s|c) p(c)=p(s,c)—联合概率(同时发生的概率)->p(c|s)p(s)=p(c,s)-> p(s|c) p(c)= p(c|s)p(s)-> p(s|c)= p(c|s)p(s)/p(c)
    P(c):即这句话的概率,但是人类说每句话的概率都是相同的,即常量。所以有:p(s|c)= p(c|s)p(s)P(c|s):即p(本田雅阁汽车|本田/雅阁/汽车)=100%,即不管哪种切分方案都可以还原出原始的句子。所以有:p(s|c)= p(s)目标:P(S)独立性假设,每个词之间无任何联系,都是独立的那么有:p(本田/雅阁/汽车)=p(本田)p(雅阁)p(汽车)也就是每个词token的概率:即TF——word count由于P(S1)>p(S2),所以选P(S1)
    那么由此我们选择概率最大的那条路线,就是这条句子的切分最佳方案。
    但是,我们概率相乘是很容易形成很小的小数的,造成向下溢出,所以有:
    求log后因为概率是小于1的数所以为负数,可比性高,并且加法运行速度比乘法更快。
    我们前面是假设每个token前后是无关联关系的,但在实际生活中词与词都是有关联的,所以我们刚才的概率模型就是一元模型(只考虑到了词频概率)。
    多元模型:一元模型(Unigram) :一个词的出现不依赖于它前面出现的词—3个参数P(S)=P(w1) P(w2)P(w3)…P(wn)二元模型(Bigram):一个词的出现仅依赖于它前面出现的一个词—3+6个参数P(S)=P(w1) P(w2|w1)P(w3|w2)…P(wn|wn-1)三元模型 (Trigram):简化成一个词的出现仅依赖于它前面出现的两个词。3+6+。。
    每次参数排列组合爆炸级增长。
    J i e b a 分词简介官方链接:https://github.com/fxsjy/jiebajieba用于中文分词,支持的文本编码格式为utf-8,支持的功能包括:中文分词、关键字提取、词性标注整体功能如下图:
    框架结构:
    第一阶段:加载词库,用trie树分割,生成句子中汉字所有可能成词情况所构成的DAG图—-FULL
    词表格式: token,TF,词性 ——-》trie树——>DAG图——》找概率Route概率:获得词频最大切分P= TF / total(TF总和)—》log(p)—-》负数总词数:Linux:awk汉字计数:cat dict.txt | awk ‘BEGIN{a=0}{a+=$2}END{print a}’—— 60101967(total)
    第二阶段:动态规划或者贝叶斯计算最大路径概率,找出最大切分组合,Token英文中文识别我们前面得到了每条边和每个汉字的概率(即所有可能词的概率),接着用倒序索引找最大路径(类似开始说的第二种表示方法)route: {0: (-33.271126717488308, 1),1: (-32.231489259807965, 1),2: (-23.899234625632083, 5),3: (-31.52324681384394, 3),4: (-22.214895405024865, 5),5: (-19.00846787368323, 5),6: (-8.7800749471799175, 7),7: (-8.800692190498415, 7), 8: (0.0, ‘’)}从后向前导,每种切分方案是单个字概率大还是词概率大,确定后的方案概率不变,继续向前导,求最大方案,并累加概率值,以此类推。所以最后方案:0-1,2-5,6-7
    第三阶段:所有单个汉字的传给——>HMM模型粘贴词库中没有的被切碎的词(未登录词)—->再传回来改回为词更新句子,Viterbi动态规划算法
    HMM:隐马尔科夫模型定位:针对单字的粘合
    马尔可夫模型每个状态只依赖之前有限个状态之前的二元模型可等价于一阶马尔可夫模型——即依赖前一个状态 p(w1,w2,w3,……,wn) = p(w1)p(w2|w1)p(w3|w1,w2)……p(wn|w1,w2,……,wn-1) =p(w1)p(w2|w1)p(w3|w2)……p(wn|wn-1) 例如: p(w1=今天,w2=我,w3=写,w4=了,w5=一个,w6=程序) =p(w1=今天)p(w2=我|w1=今天)p(w3=写|w2=我)……p(w6=程序|w5=一个)马尔可夫模型3类参数: 状态(对于分词来说,每一个token即一个状态) 初始概率(某开头词汇出现的文章次数 / 总文章数),例p(w1=今天) 转移概率(条件概率) 统计:(P(B|A)=2/3 A出现时B出现概率)
    马尔可夫模型缺点:只是对一串序列做建模做不了情况:两个序列:实时翻译、文字和词性、语音识别
    由此引出了HMM—-隐马尔科夫模型——生成式模型即对观察序列(O)和隐藏序列(S)进行分析处理
    状态序列(隐藏序列)由:《位置信息(BMES)+词性》B:begin M:middle E:end S:single——做分词我/今天/真高兴==》SBEBME词性:如果一个词是N词,那么他的字也是N词—做词性标注
    HMM有5类参数:状态序列(隐藏序列)M个*4=256初始概率 M个转移概率 M*M发射概率 M*N观察序列 N个=常用汉字
    jieba表:位置+词性+概率prob_start:初始概率—————-256个prob_trans:转移概率,从一个状态到另一个状态的概率值,词性一样prob_emit:发射概率,从状态到字的概率,每个位置和词性后面匹配的字的概率获取方法:文章统计(词性人工标注)在中文分词中,我们需要用大量的已经分好词的数据作为先验知识,在其中统计BMES找到一些规律prob,以便于我们后期利用工具做中文分词。
    假设有3个观察、3个状态:P(S1,S2,S3,O1,O2,O3)联合概率(同时发生)=P(S1)*P(O1|S1)*P(S2|S1)…. 初始、发射、转移我们的目标:给定HMM模型,我们输入观测序列,希望得到最优的状态序列即最优的分词策略P(S|O)=P(S,O) / P(O) P(O)=100%P(S|O)=P(S,O)所有遍历一遍时间复杂度:M^T
    最优路径:动态规划:Viterbi只需要关心当前状态与前一状态的最优路径(最大概率),两两计算,再次向后迭代score1: t位置的状态概率,score2:从t到t+1的转移概率概率,O:从状态到字的概率计算:score1初始概率*score2转移概率*O发射概率=score3新分数——-类似孩子长个复杂度:M*M*(N-1)
    实践:1.MR批量分词Client本地:代码、jieba包Datanode:数据文件分发—file模式—-代码内解压
    2.pyweb+分词Get、Post引用jieba方法
    3.中文分词完成倒排索引正排索引item-》token——》分词倒排索引token-》item——->召回
    (1)map正排:一对一:name->token—————————分词+jieba权重scoretoken1^Ascore token2^Bscore(2)map倒排:token-》name
    (3)reducetoken1-》name name name———>wordcount思路拼接字符串
    1 留言 2019-04-12 12:23:32 奖励11点积分
  • C++语言算符优先分析器的设计与实现

    一、实验目的及要求1.1 目的加深对语法分析器工作过程的理解;加强对算符优先分析法实现语法分析程序的掌握;能够采用一种编程语言实现简单的语法分析程序;能够使用自己编写的分析程序对简单的程序段进行语法翻译。
    1.2 要求
    对语法规则有明确的定义
    编写的分析程序能够对实验一的结果进行正确的语法分析
    对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程
    软件、硬件环境:code::blocks 10.05,win10系统

    二、实验步骤2.1 实验步骤
    定义目标语言的语法规则
    求解预测分析方法需要的符号集和分析表
    输入将要进行分析的句子
    根据预测分析的方法进行语法分析,直到源程序结束
    对遇到的语法错误做出错误处理

    2.2 文法定义E->E*TF->TT->(E)T->i2.3 求出FIRSTVT和LASTVTFIRSTVT LASTVTE * ( i * ) iT ( i ) i2.4 画出算符优先分析表* ( ) i #* > < > < >( < < = < >) > > i > > ># < < < =三、实验内容3.1 流程图3.2 源代码#include<stdlib.h>#include<stdio.h>#include<string.h>#include<iostream>#define SIZE 128char priority[6][6]={{0},{'>','>','<','<','>','>'},{'>','>','$','$','>','>'},{'<','<','<','<','=','$'},{'>','>','$','$','>','>'},{'<','<','<','<','$','='}};char input[SIZE]; char remain[SIZE]; char AnalyseStack[SIZE]; int testchar(char x),k; void remainString(); int testchar(char x){ int m; if(x=='+') m=0; if(x=='*') m=1; if(x=='i') m=2; if(x=='(') m=3; if(x==')') m=4; if(x=='#') m=5; return m;}void analyse(){ int i,j,f,z,z1,n,n1,z2,n2; int count=0; char a; char p,Q,p1,p2; f=strlen(input); for(i=0;i<=f;i++) { a=input[i]; if(i==0) remainString(); if(AnalyseStack[k]=='+'||AnalyseStack[k]=='*'||AnalyseStack[k]=='i' ||AnalyseStack[k]=='('||AnalyseStack[k]==')'||AnalyseStack[k]=='#') j=k; else j=k-1; z=testchar(AnalyseStack[j]); if(a=='+'||a=='*'||a=='i'||a=='('||a==')'||a=='#') n=testchar(a); else //如果句子含有不是终结符集合里的其它字符,不合法 printf("错误!该句子不是该文法的合法句子!\n"); break; p=priority[z][n]; if(p=='$'){ printf("错误!该句子不是该文法的合法句子!\n"); return; } if(p=='>'){ for( ; ; ){ Q=AnalyseStack[j]; if(AnalyseStack[j-1]=='+'||AnalyseStack[j-1]=='*'||AnalyseStack[j-1]=='i' ||AnalyseStack[j-1]=='('||AnalyseStack[j-1]==')'||AnalyseStack[j-1]=='#') j=j-1; else j=j-2; z1=testchar(AnalyseStack[j]); n1=testchar(Q); p1=priority[z1][n1]; if(p1=='<') { count++; printf("(%d) %s\t%10c\t%5c%17s\t 归约\n",count,AnalyseStack,p,a,remain); k=j+1; i--; AnalyseStack[k]='N'; int r,r1; r=strlen(AnalyseStack); for(r1=k+1;r1<r;r1++) AnalyseStack[r1]='\0'; break; } else continue; } } else{ if(p=='<'){ count++; printf("(%d) %s\t%10c\t%5c%17s\t 移进\n",count,AnalyseStack,p,a,remain); k=k+1; AnalyseStack[k]=a; remainString(); } else{ if(p=='='){ z2=testchar(AnalyseStack[j]); n2=testchar('#'); p2=priority[z2][n2]; if(p2=='='){ count++; printf("(%d) %s\t%10c\t%5c%17s\t 接受\n",count,AnalyseStack,p,a,remain); printf("该句子是该文法的合法句子。\n"); break; } else{ count++; printf("(%d) %s\t%10c\t%5c%17s\t 移进\n",count,AnalyseStack,p,a,remain); k=k+1; AnalyseStack[k]=a; remainString(); } } else printf("错误!该句子不是该文法的合法句子!\n"); break; } } }}void remainString(){ int i,j; i=strlen(remain); for(j=0;j<i;j++) remain[j]=remain[j+1]; remain[i-1]='\0';}int main(){ printf("请输入要进行分析的句子(以#号结束输入):\n"); gets(input); k=0; printf("步骤 栈 优先关系 当前符号 剩余输入串 移进或归约\n"); AnalyseStack[k]='#'; AnalyseStack[k+1]='\0'; int length,i; length=strlen(input); for(i=0;i<length;i++) remain[i]=input[i]; remain[i]='\0'; analyse(); return 0;}
    四、实验结果4.1 运行截图
    五、实验总结本次实验基本完成了实验题目的要求,定义了一个文法,求出了每一个非终结符的VT集和LASTVT集,画出了算符优先关系表,并判定出给定的文法是否是算符优先文法。当给定一个 句子时,能够判定是否是文法中的句子,并能够将分析过程打印出来。
    这个实验最大的收获,不仅仅使我更进一步的了解到了算符优先算法是一个省略了所有单非终结符产生式对应的归约步骤,其分析效率是很高的,同时,通过此次实验让我的处理问题的能力,思考问题的角度都得到了很大的提高,从词法分析到语法分析再到语义分析,基本走完了编译器的大致流程,这对我理解编译的过程和具体的实现都是有极大的帮助的。
    0 留言 2019-04-24 15:22:39 奖励12点积分
  • 如何将本地项目部署到Github上

    Github上是一个面向开源及私有软件项目的托管平台那么如何上传项目至GitHub上:
    首先注册一个GitHub的账号
    然后,你需要下载安装GIT中(这里就不说了)
    第一步:创建新存储库
    在右上角,在您的头像或identicon旁边,单击然后选择新的存储库。
    填好后,单击创建存储库
    打开设置,有一个Github Pages的设置,点击源中的的无使其变成master分支,然后点击保存。
    刷新后,github pages设置框处,多了一行网址,这就是你的github pages的网址;选择一个盘,如E盘,右击空白处点击git bash here。输入如下命令:
    之后,输入git clone https://github.com/p525z/TbMis.git 。(注意克隆地址格式为:https://github.com/用户名/项目名.git )
    这时,你的e盘就会多一个演示文件,打开演示/ TbMis后会有一个README.md文件。
    将自己的项目文件复制至TbMis文件中

    执行:cd TbMis /执行:git status后会出现执行:git add .(别忘了加 .)
    之后执行如下命令,到这里,如果是第一次操作则需要你输入相关账号的信息:
    最后输入git pull和git push即可,如果是第一次操作则会有弹出框让你输入你的GitHub账号和密码,记得New SSH key。接下来就是等待啦。最后刷新GitHub页面进入刚才新建的那个仓库里面就会发现项目已经成功上传了。
    0 留言 2019-04-24 14:19:44 奖励5点积分
  • Http Server实现原理解读

    Tinyhttpd 是 J. David Blackstone 在1999年写的一个不到 500 行的超轻量型 Http Server,用来学习非常不错,可以帮助我们真正理解服务器程序的本质。官网:http://tinyhttpd.sourceforge.net
    每个函数的作用处理从套接字上监听到的一个 HTTP 请求,在这里可以很大一部分地体现服务器处理请求流程。

    bad_request:返回给客户端这是个错误请求,HTTP 状态吗 400 BAD REQUEST
    cat:读取服务器上某个文件写到 socket 套接字
    cannot_execute:主要处理发生在执行 cgi 程序时出现的错误
    error_die:把错误信息写到 perror 并退出
    execute_cgi:运行 cgi 程序的处理,也是个主要函数
    get_line:读取套接字的一行,把回车换行等情况都统一为换行符结束
    headers:把 HTTP 响应的头部写到套接字
    not_found:主要处理找不到请求的文件时的情况
    sever_file:调用 cat 把服务器文件返回给浏览器
    startup:初始化 httpd 服务,包括建立套接字,绑定端口,进行监听等
    unimplemented:返回给浏览器表明收到的 HTTP 请求所用的 method 不被支持

    建议源码阅读顺序:main —> startup —> accept_request —> execute_cgi,通晓主要工作流程后再仔细把每个函数的源码看一看。
    工作流程1) 服务器启动,在指定端口或随机选取端口绑定 httpd 服务
    2) 收到一个 HTTP 请求时(其实就是 listen 的端口 accpet 的时候),派生一个线程运行 accept_request 函数
    3) 取出 HTTP 请求中的 method (GET 或 POST) 和 url,。对于 GET 方法,如果有携带参数,则 query_string 指针指向 url 中 ?后面的 GET 参数
    4) 格式化 url 到 path 数组,表示浏览器请求的服务器文件路径,在 tinyhttpd 中服务器文件是在 htdocs 文件夹下。当 url 以 / 结尾,或 url 是个目录,则默认在 path 中加上 index.html,表示访问主页
    5) 如果文件路径合法,对于无参数的 GET 请求,直接输出服务器文件到浏览器,即用 HTTP 格式写到套接字上,跳到(10)。其他情况(带参数 GET,POST 方式,url 为可执行文件),则调用 excute_cgi 函数执行 cgi 脚本
    6) 读取整个 HTTP 请求并丢弃,如果是 POST 则找出 Content-Length. 把 HTTP 200 状态码写到套接字
    7) 建立两个管道,cgi_input 和 cgi_output, 并 fork 一个进程
    8) 在子进程中,把 STDOUT 重定向到 cgi_outputt 的写入端,把 STDIN 重定向到 cgi_input 的读取端,关闭 cgi_input 的写入端 和 cgi_output 的读取端,设置 request_method 的环境变量,GET 的话设置 query_string 的环境变量,POST 的话设置 content_length 的环境变量,这些环境变量都是为了给 cgi 脚本调用,接着用 execl 运行 cgi 程序
    9) 在父进程中,关闭 cgi_input 的读取端 和 cgi_output 的写入端,如果 POST 的话,把 POST 数据写入 cgi_input,已被重定向到 STDIN,读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT。接着关闭所有管道,等待子进程结束。这一部分比较乱,见下图说明:
    管道初始状态

    管道最终状态

    10) 关闭与浏览器的连接,完成了一次 HTTP 请求与回应,因为 HTTP 是无连接的
    1 留言 2019-04-24 10:50:21 奖励15点积分
  • 内存映射文件

    背景
    内存映射文件提供了一组独立的函数,使应用程序能够通过内存指针像访问内存一样访问磁盘上的文件。通过内存映射文件函数可以将磁盘上的文件全部或者部分映射到进程的虚拟地址空间的某个位置。一旦完成映射,对磁盘文件的访问就可以像访问内存文件一样简单。这样,向文件中写入数据的操作就是直接对内存进行赋值,而从文件的某个特定位置读取数据也就是直接从内存中取数据
    当内存映射文件提供了对文件某个特定位置的直接读写时,真正对磁盘文件的读写操作是由系统底层处理的。而且在写操作时,数据也并非在每次操作时即时写入到磁盘,而是通过缓冲处理来提高系统整体性能。
    内存映射文件的一个好处之一是,程序代码以标准的内存地址形式来访问文件数据,按照页面大小周期从磁盘写入数据的操作发生在后台,由操作系统底层来实现,这个过程对应用程序是完全透明的。虽然,内存映射文件最终还是要将文件从磁盘读入内存,实质上并没有省略什么操作,整体性能可能并没有获得什么提高,但是程序的结构会从中受益,缓冲区边界等问题将不复存在。而且对文件内容更新后的写操作也有操作系统自动完成,有操作系统判断内存中的页面是否为脏页面并仅将脏页面写入磁盘,比程序自己将全部数据写入文件的效率要高很多。

    函数介绍CreateFile 函数
    可打开或创建以下对象,并返回可访问的句柄:控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道。
    函数声明
    HANDLE WINAPI CreateFile( _In_ LPCTSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _In_ DWORD dwCreationDisposition, _In_ DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile);
    参数

    lpFileName:要打开的文件的名或设备名。这个字符串的最大长度在ANSI版本中为MAX_PATH,在unicode版本中为32767。dwDesiredAccess:指定类型的访问对象。如果为 GENERIC_READ 表示允许对设备进行读访问;如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);如果为零,表示只允许获取与一个设备有关的信息 。dwShareMode:如果是零表示不共享; 如果是FILE_SHARE_DELETE表示随后打开操作对象会成功只要删除访问请求;如果是FILE_SHARE_READ随后打开操作对象会成功只有请求读访问;如果是FILE_SHARE_WRITE 随后打开操作对象会成功只有请求写访问。lpSecurityAttributes: 指向一个SECURITY_ATTRIBUTES结构的指针,定义了文件的安全特性。dwCreationDisposition:创建方式。hTemplateFile:为一个文件或设备句柄,表示按这个参数给出的句柄为模板创建文件(就是将该句柄文件拷贝到lpFileName指定的路径,然后再打开)。它将指定该文件的属性扩展到新创建的文件上面,这个参数可用于将某个新文件的属性设置成与现有文件一样,并且这样会忽略dwAttrsAndFlags。通常这个参数设置为NULL,为空表示不使用模板,一般为空。
    返回值

    如执行成功,则返回文件句柄。INVALID_HANDLE_VALUE表示出错,会设置GetLastError。

    CreateFileMapping 函数
    创建一个新的文件映射内核对象。
    函数声明
    HANDLE WINAPI CreateFileMapping( _In_HANDLE hFile, _In_opt_LPSECURITY_ATTRIBUTES lpAttributes, _In_DWORD flProtect, _In_DWORD dwMaximumSizeHigh, _In_DWORD dwMaximumSizeLow, _In_opt_LPCTSTR lpName);
    参数

    hFile
    需要映射到进程地址空间的文件的句柄。如果指定为INVALID_HANDLE_VALUE时,表示创建一个无文件句柄的文件映射,这种主要用来共享内存。(注意CreateFile的失败时的返回值为INVALID_HANDLE_VALUE。如果不加以检查,可能会出现潜在的问题,即创建了一个共享内存)
    lpAttributes
    安全属性。一般为NULL,表示使用默认的安全属性。
    flProtect
    下述常数之一:PAGE_READONLY:以只读方式打开映射PAGE_READWRITE:以可读、可写方式打开映射PAGE_WRITECOPY:为写操作留下备份可组合使用下述一个或多个常数:SEC_COMMIT:为文件映射一个小节中的所有页分配内存SEC_IMAGE:文件是个可执行文件SEC_RESERVE:为没有分配实际内存的一个小节保留虚拟内存空间
    dwMaximumSizeHigh
    文件映射的最大长度的高32位。
    dwMaximumSizeLow
    文件映射的最大长度的低32位。如这个参数dwMaximumSizeHigh
    都是零,就用磁盘文件的实际长度。
    lpName
    以0为终止符的字符串,用来给文件映射对象指定一个名称。一般用于进程间共享文件映射内核对象的。

    返回值

    返回值为文件映射的对象句柄。无法创建时,返回NULL。

    MapViewOfFile 函数
    将一个文件映射对象映射到当前应用程序的地址空间。
    函数声明
    LPVOID WINAPI MapViewOfFile(   __in HANDLE hFileMappingObject,   __in DWORD dwDesiredAccess,   __in DWORD dwFileOffsetHigh,   __in DWORD dwFileOffsetLow,   __in SIZE_T dwNumberOfBytesToMap);
    参数

    hFileMappingObject:为CreateFileMapping()返回的文件映像对象句柄。dwDesiredAccess:映射对象的文件数据的访问方式,而且同样要与CreateFileMapping 函数所设置的保护属性相匹配。 可取以下值:FILE_MAP_ALL_ACCESS:等价于CreateFileMapping的 FILE_MAP_WRITE | FILE_MAP_READ,文件映射对象被创建时必须指定PAGE_READWRITE 选项;FILE_MAP_COPY:可以读取和写入文件.写入操作会导致系统为该页面创建一份副本,在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性;FILE_MAP_EXECUTE:可以将文件中的数据作为代码来执行,在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性;FILE_MAP_READ:可以读取文件,在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性;FILE_MAP_WRITE:可以读取和写入文件,在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性;dwFileOffsetHigh:表示文件映射起始偏移的高32位。dwFileOffsetLow:表示文件映射起始偏移的低32位。(64KB对齐不是必须的)dwNumberOfBytes:指定映射文件的字节数。
    返回值

    如果成功,则返回映射视图文件的开始地址值。如果失败,则返回 NULL,可调用 GetLastError 查看错误。

    实现过程使用WIN32 API来实现内存映射文件,实现步骤如下:

    调用 CreatFile 打开想要映射的文件,获得句柄hFile
    调用 CreatFileMapping 函数生成一个建立在CreatFile函数创建的文件对象基础上的内存映射对象,得到句柄hFileMap
    调用 MapViewOfFile 函数把整个文件的一个区域或者整个区域映射到内存中,得到指向映射到内存的第一个字节的指针 lpMemory
    用该指针来读写文件
    调用 UnmapViewOfFile 来解除文件映射,传入参数为 lpMemory
    调用 CloseHandle 来关闭内存映射文件,传入参数为 hFileMap
    调用 CloseHandle 来关闭文件,传入函数为 hFile

    编码实现BOOL MemoryMapFile(char *pszFileName){ HANDLE hFile = NULL; HANDLE hFileMap = NULL; LPVOID lpMemory = NULL; // 打开文件获取文件句柄 hFile = ::CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL); if (INVALID_HANDLE_VALUE == hFile) { ShowError("CreateFile"); return FALSE; } // 创建文件映射内核对象 hFileMap = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (NULL == hFileMap) { ShowError("CreateFileMapping"); return FALSE; } // 将文件的数据映射到进程地址空间 lpMemory = ::MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (NULL == lpMemory) { ShowError("MapViewOfFile"); return FALSE; } // 用 lpMemory 指针来读写文件 ::RtlCopyMemory(lpMemory, "My Name Is Demon`Gan", 21); // 关闭句柄释放内存 ::UnmapViewOfFile(lpMemory); ::CloseHandle(hFileMap); ::CloseHandle(hFile); return TRUE;}
    程序测试我们直接运行程序,对 520.txt 文件数据进行修改。修改前,520.txt 的内容如下所示:

    程序执行完毕之后,成功修改了文件数据。

    总结内存映射文件的实现步骤都是固定的,大家理解清晰使用到的函数意义及其各个参数的意义就好。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2018-11-07 11:35:08 奖励10点积分
  • 在DLL中创建共享内存

    背景我们都知道在 32 位系统上,每个进程都有自己 4GB 大小的独立空间,互不影响。当然,对内核了解的同学则会质疑说,4GB 大小的内容可以大致分为两部分,低 2 GB内存空间是用户地址空间,高 2 GB是内核地址空间,而内核地址空间是共享的,并非独立。是的,这没错。所以,本文中所指的独立空间是用户地址空间。
    举个例子来说,进程1 的 0x400000 内存地址和 进程2 中 0x400000 内存地址是没有任何关联。任意修改其中一个内存里的数据,是不影响另一个的。这边是进程独立性。
    但是,本文要介绍的这个知识点,就是要突破这个独立性的显示,创建进程共享内存。确切的说是在DLL中创建共享内存,就是在DLL中创建一个变量,然后DLL被加载到多个进程空间,只要其中一个进程修改了该变量的值,其他进程DLL中的这个值也会改变,就相当于多个进程共享一块内存。
    实现原理实现原理比较简单,就是先为DLL创建一个数据段,然后再对程序的链接器进行设置,使其在程序编译完毕开始链接的时候,根据设置的链接选项,把指定的数据段链接为共享数据段。这样,就可以创建共享内存了。
    #pragma data_seg("MySeg") char g_szText[256] = {0};#pragma data_seg()#pragma comment(linker, "/section:MySeg,RWS")
    例如,在上面的代码中,我们使用 #pragma data_seg 创建了一个名为 MySeg 的数据段,接着使用 /section:MySeg,RWS 把 MySeg 数据段设置为可读、可写、可共享的共享数据段。
    程序测试我们开发一个程序加载这个创建了共享内存的DLL,然后调用它的导出函数 Change 来修改共享内存变量的值,调用 Show 来显示共享内存变量的值。发现,只要有一个进程修改了这个变量,其他所有进程里的变量也跟着改变。

    总结这个实现上比较简单,难点就是在思想上的转换,要理解什么是共享内存的思想。
    参考参考自《Windows黑客编程技术详解》一书
    3 留言 2018-11-07 11:28:40 奖励5点积分
  • 跨域

    一、什么是跨域?1.1 什么是同源策略及其限制内容?同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个IP地址,也非同源。
    同源策略限制内容有:

    Cookie、LocalStorage、IndexedDB 等存储性内容
    DOM 节点
    AJAX 请求发送后,结果被浏览器拦截了

    但是有三个标签是允许跨域加载资源:

    <imgsrc=XXX>

    <linkhref=XXX>

    <scriptsrc=XXX>



    1.2 常见跨域场景当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。
    特别说明两点:

    第一:如果是协议和端口造成的跨域问题“前台”是无能为力的
    第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”

    这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?
    跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
    你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?
    因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。
    同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。
    1.3 跨域解决方案1.3.1 jsonpJSONP原理
    利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
    JSONP和AJAX对比
    JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)
    JSONP优缺点
    JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。
    JSONP的实现流程

    声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)
    创建一个 <script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)
    服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是 show(‘我不爱你’)
    最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作

    JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。
    1.3.2 corsCORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
    浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
    服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
    虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。
    简单请求
    只要同时满足以下两大条件,就属于简单请求

    条件1:使用下列方法之一:

    GETHEADPOST
    条件2:Content-Type 的值仅限于下列三者之一:

    text/plainmultipart/form-dataapplication/x-www-form-urlencoded

    请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
    复杂请求
    不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。
    1.3.3 postMessagepostMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

    页面和其打开的新窗口的数据传递
    多窗口之间消息传递
    页面与嵌套的iframe消息传递
    上面三个场景的跨域数据传递

    postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
    otherWindow.postMessage(message, targetOrigin, [transfer]);
    message: 将要发送到其他 window的数据。
    targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串”*”(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送
    transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

    1.3.4 websocketWebsocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。
    但是WebSocket是一种双向通信协议,在建立连接之后,WebSocket的server与 client都能主动向对方发送或接收数据。
    同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
    原生WebSocket API使用起来不太方便,我们使用 Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
    1.3.5 Node中间件代理(两次跨域)实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。代理服务器,需要做以下几个步骤:

    接受客户端请求
    将 请求 转发给服务器
    拿到服务器 响应 数据
    将 响应 转发给客户端

    1.3.6 nginx反向代理实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。
    使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。
    实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
    1.4 总结
    CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
    JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
    不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
    日常工作中,用得比较多的跨域方案是cors和nginx反向代理

    参考文章
    跨域资源共享 CORS 详解
    前端面试之道
    window.postMessage
    前端常见跨域解决方案(全)
    深入跨域问题(4) - 利用代理解决跨域
    3 留言 2019-04-08 11:06:29 奖励15点积分
显示 45 到 60 ,共 15 条
eject