分类

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

技术文章列表

  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端SSM框架的B/S微博系统的设计与实现

    第一章 绪 论时至今日,网络对于现代人来说,早已成为人类科技发展进步的桥梁,而通过网络衍生出的一系列产品也不断的冲击着人们的日常生活,截至2016年年底,我国网名数量达到7亿,有超多一半多人在使用网络,而它天涯咫尺的作用,不但消除了人与人地域上的距离,更是拉近了心灵的距离,沟通与互动变的异常频繁与重要。
    随着互联网新时代的来临,微博借着互联网的桥梁,逐渐进入网名的视野中,没有博客的长篇大论,也不需要严谨的逻辑层次,这使得网络中一大批的原创文章被生产发掘,短短几句话便可以在网络中激起千层浪,普通人也可能在一夜之间成为拥有数千万粉丝的“网红”。微博的便捷、原创、和草根性使它成为中国网民茶余饭后的网上生活。
    早在2006年3月,Obvious推出了Twitter服务,这个世界上最早同时也是最着名的微博系统,而在那是,微博也仅仅只是为了给好友发送手机短信,在中国,饭否网的上线标志中国微博的开端,之后腾讯滔滔、叽歪、嘀咕等微博的开荒者终究也都没能站在最后。
    2010年,我国的微博得到迅速发展,这一年,无论微博的用户还是影响力都达到前所未有的高度,以新浪门户为首的腾讯、新浪、搜狐等微博展现出全新的活力。到了2013年6月,中国微博用户规模高达3.31亿人,在微博中流动的信息有超过2亿多条。
    2010年11月,新浪微博推出群组功能,这个功能不但可以与好友实时联系,又可以随时发布最新信息,2012年添加新功能“悄悄关注”,在用户关注后不通知被关注用户,也不显示给被关注用户,2013年,微博推出包括iPhone和Android的移动客户端,新增“密友”功能,同年10月份新浪微博“粉丝服务平台”上线,粉丝服务平台帮助认证用户为订阅自己的用户提供精彩内容和互动服务,自此微博由“营销”向“营销+服务”转型!而新浪微博依旧延续这自己的名人效应,一个普通账号,在没有粉丝关注的情况下,发布的微博却很难被他人看到,如果需要在微博中求助,没有“名人大V”的帮助,很难被别人所注意到。想要在微博中寻求帮助,就需要微博提供更多的服务。而本系统通过使用积分悬赏功能使得用户可以通过积分悬赏自己的问题,来让更多的用户回答自己的问题,得到更多人的帮助。
    第二章 相关技术介绍2.1 架构概述B/S架构(Browser/Server,浏览器/服务器模式):是一种通过将浏览器作为客户端的网络结构模式,利用已经逐步成熟的web浏览器技术,结合浏览器的多种功能,使用浏览器来作为早先C/S(Client/Serve)架构下复杂的客户端,使用C/S架构使得用户的客户端得到统一,将软件系统的核心功能集中在服务器端,系统的升级和维护更加简单,开发人员只需要管理服务器就可以做到对如软件系统的更新和维护,B/S架构所带来的众多优点使得它成为将来信息化发展的主流方向。
    MVC模式:即模型(Model),视图(View),控制器(Controller)是一种软件开发的设计模式,Model主要负责分离出来的业务逻辑的数据交互,View主要负责数据的提交和展示,Controller主要负责获取请求和请求的转发。
    SSM框架(SpringMVC+Spring+Mybatis):由这三个开源框架组成的web项目的框架,是标准的MVC模式,spring MVC负责请求的转发和视图管理,spring实现业务对象管理和事务控制,mybatis作为持久化引擎,相对于早先流行的SSH(Struts+Spring+Hibernate)框架来说,SSM作为轻量级框架更适合开发中小型项目。
    2.2 关键技术简介前端技术
    JSP(Java Server Pages):嵌入了java代码的html页面,本质是一个servlet,它实现了在htmld的语法中扩展java代码,使用<% %>格式。
    JavaScript:是一种弱类型的脚本语言,由ECMAScript描述JavaScript的基本对象和语法,文档对象模型(DOM),浏览器对象模型(BOM)三部分组成。
    Ajax(Asynchronous Javascript And XML):异步的JavaScript和XML,实现前后台之间的异步交互,在不需要重新加载整个页面的前提下对页面上的部分内容做局部刷新。

    后台技术java:一种面向对象的编程语言,主要特性包括有

    简单性:抛弃了C++中复杂的语法和指针多继承等特征,开发人员不需要关注底层优化,只需要关注业务逻辑就行
    面向对象性,对程序员而言,只需要注意对应的数据和处理数据的方法,不用严格按照过程的方式来编写代码,因为java语言是面对对象的,因此也拥有面向对象的三大特征,继承、封装和多态
    跨平台性,java语言的跨平台性也就是JVM(java虚拟机)的跨平台性。Java文件编译后不会直接生成机器直接运行的二进制文件,而是编译成class文件,JVM会将编译的class文件根据不同操作系统下的JVM来生成对应系统的二进制文件,只需要编译一次,就可以在不同的平台上运行

    SpringMVC:是Spring框架提供的一个模块,通过实现MVC模式来地将数据、业务与展示进行分离,简化了Web开发。和其他MVC框架一样都是请求驱动,控制转发,它的核心Servlet是DispatcherServlet,即前端控制器,用来分发请求到控制器,它和Spring框架完全整合。这也使得SpringMVC成为目前非常流行的MVC框架。
    Spring:一款轻量级java开发框架,Spring框架有着分层的体系结构,因此可以使用Spring中 独立的任意部分。而它的架构依旧是内在稳定的,Spring提供了J2EE应用各层解决方案,贯穿于三层架构的每一层,但Spring依旧可以很好的兼容其他框架,本项目主要用到了Spring中IOC(控制反转)和AOP(面向切面编程)模块。
    Mybatis:是一个简化Java持久化层的轻量级开源框架。并且支持注解的Mapper,Mybatis消除了绝大部分的JDBC代码,使得java对象持久化到数据库的过程变的更加容易,相对于其他的java持久化框架,Mybatis的优点在于,消除了大量的JDBC冗余代码、简单易学、可见的Sql语句、提供了于Spring的整合,引入更好的性能。
    2.3 开发工具Tomcat服务器:是一个Web应用服务器,它是轻量且开源的,是中小型Web项目和开发调试和学习的首选。
    Oracle数据库(Oracle Database):是由Oracle公司开发的一款关系型数据库,是商业运用第一的关系型数据库,系统使用方便,功能强大,可移植性强。适用于各种大中小环境,在大数据时代,作为分布式数据库,它实现了分布式处理的功能,具有完整的数据管理功能、完备的关系型数据库、分布式处理功能。
    Eclipse开发工具:一个be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端java开源的可扩展开发平台,它不但包括java集成开发环境,还包括插件开发环境,如SVN、CVS等团队开发插件。
    2.4 本章小结本章主要介绍了开发项目用到的一些主要技术,项目所使用的架构和设计模式,项目中所使用到的主要框架技术,项目在浏览器端展示用到的前端技术和展示方式,后台代码使用的开发语言,使用的服务器技术,数据持久层所使用的数据库等,在本章最后又介绍了开发使用到的开发工具。
    第三章 系统需求分析3.1 可行性分析3.1.1技术可行性在已有技术方面,为了统一客户端,消除因版本升级和维护带来的复杂性,因此采用成熟的B/S架构在项目的实现上完全可行,在开发语言和框架方面,java和j2ee体系的强大,可以让开发人员精心的构建web项目,以及一系列的开源框架,都为项目的可行性提供了强大的依据,在服务器方面,使用开源服务器Tomcat,足以支持该小型项目的正常使用,而不断发展的前端技术和前端框架可以制作精美的前台页面,提高用户的体验和交互,这在项目的页面展示技术上完全可行,强大的关系型数据库为项目数据的持久化提供强有力的后援。综上所述,日趋成熟化的java和j2ee体系、完全开源的java框架和服务器、功能强大的关系型数据库、运用Web前端技术提供用户交互页面,所以该项目在技术方面完全可行。
    3.1.2 经济可行性在互联网发展到信息时代的今天,单一获取信息的方式已经不能满足人们,人们每天接触的信息越来越多,而获取信息的形式也越来越多,但大多数获取信息的方式,留给用户交互的方式并不多,大都数情况下,人们只能被动获取,而很难找到自己的喜好和需求来获取信息,而本微博项目可以让人们获取实时热点信息和他们所关注的信息,实时与微博信息交互。而使用大量成熟技术与开源框架下,也使得小项目的开发更加简单,经济,高效,因微博而兴起的微博效应也能带来一定的经济效益。
    3.1.3 操作可行性微博系统使用B/S架构,用户不需要下载客户端,只需要用户有浏览器,就可以在浏览器上登陆微博系统,微博系统的界面颜值高,用户交互性高,用户操作简单方便,只需要了解基本的计算机操作就能使用,用户体验性高。因此在系统操作上完全可行。
    3.2 需求分析3.2.1 系统总体需求该微博系统主要由前台用户模块和后台管理模块组成,当用户进入首页时可以选择登陆或不登陆,登陆时可以使用已有账号登陆或注册新账号。用户未登陆时,在首页显示最近更新的热门微博,而登陆后的用户可以在首页看到 自己关注用户最近更新的微博。
    未登陆用户只能搜索查看微博信息和访问用户主页,登陆用户可以登陆系统后修改自己的基本信息例如签名、性别等,在验证用户信息后还可以修改密码和密保信息。以及修改用户头像和密码,编辑自己的个人主页,对微博进行点赞、评论、收藏等功能,还可以关注/取消关注用户,拉黑用户、私聊用户等操作。
    后台管理员可以查看系统所有的数据,包括用户、微博、评论、海螺、回答的总量,最近一个的数据库,最近一周的数据量。具体所具有的功能包括管理不良微博信息与不良账号,对微博、微博评论、海螺、回答等信息的删除和恢复功能,对不良账号的封禁等操作。
    微博查找模块:用户可以输入关键字来查找相对应的微博或查找用户。
    微博发布模块:用户点击发布,在内容中添加自己想要发送的内容,可以选择表情,也可以插入图片,但对输入字符数有着限制,同时还可以插入一张图片。
    微博评论模块:用户可以查看微博的评论,发布微博评论等。
    神奇海螺模块:用户可以发布一个神奇海螺,海螺主要用来记载用户提出的各种问题,由其他用户来查看并回答问题,当回复者的答案被提问者采纳后,回答者可以增加自己的海螺积分,不同的海螺积分有着不同的海螺称号。
    积分模块:用户每天登陆,发微博,做任务等方式可以增加自己的积分,不同的积分拥有不同的称号,神奇海螺模块的积分有着不同的称号,称号显示在用户名称的后面。
    好友模块:用户可以查看系统中其他的用户,找到自己喜爱的用户然后关注他们,关注后可以在好友模块中查看自己关注的用户,以及好友最近发布的微博等信息,也可以私信好友,发送私信信息给好友。
    3.2.2 用例图需求1. 当用户进入系统时候,可以选择登陆或注册用户,如果忘记了密码还可以通过密保问题来重置密码。

    2. 当用户登陆后,可以管理用户的个人基本信息,修改用户基础信息,修改用户密保信息。修改用户头像等功能。

    3. 用户微博管理系统,当用户登陆进去系统时,可以在首页发布微博,通过关键字搜索微博内容中关键字的微博信息。产看微博,包括查看个人微博、好友微博、推荐微博。

    4. 评论管理,评论管理依赖于微博模块,用户可以查看微博的评论,对微博信息发布评论,以及删除自己的评论。

    5. 海螺管理,用户登陆后可以在海螺模块发布海螺问题,发布问题时可以选择悬赏的积分数目,同时减少自己的积分数,用户可以参加回答他人的海螺问题,当回答的答案被采纳时,就可以获得用户悬赏的积分数。

    6. 好友管理,当用户登陆时可以关注系统推荐好友,也可以自己搜索用户,查看用户的主页面,添加关注或取消关注用户,还可以给用户发送私人信息,或者拉黑用户。

    7. 消息通知,当用户的关注,微博评论,点赞,收藏时调用消息通知。

    8. 管理员:管理员登陆系统,可以管理微博用户,对不良用户进行封禁和注销账户的操作,也可以恢复用户的状态,同时也可以对微博信息进行管理,如删除微博,恢复被管理员删除的微博信息等,对微博评论的删除和恢复等操作。

    第四章 系统功能设计4.1 系统类分析4.1.1 实体类实体类主要用来传递数据,主要包括User、Friend、PointAction、PointRecord、Weibo、WeiboCollect、WeiboComm、WeiboLike、WeiboTrans等,用户类中包括用户的基本信息,微博类中包括微博的基本信息同时包含实体用户类,好友类包括好友编号,好友创建时间与关注双方的用户类等,私信类包含私信双方的用户类与私信的基本属性,微博收藏类包含收藏的微博类与用户id等收藏属性,微博评论类包含微博类和用户类以及评论内容等属性,微博转发类包含用户类和微博类以及转发时间等属性,微博点赞类包含微博类和用户类以及点赞时间等属性。实体类之间的类关联关系如图所示。

    4.1.2 控制器类在controller层包含MainController、WeiboController、FrendController、AdminController四个JAVA类,在SpringMVC框架中主要用来接收浏览发送给服务器的请求和数据处理并控制请求的转发,将从Service层中获取的数据响应给浏览器端。MainController主要用发来接收来自用户相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    WeiboController主要用发来接收来自微博相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图4所示。

    FriendController主要用发来接收来自好友相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    AdminController主要用发来接收来自管理员相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    4.1.3 业务逻辑类在Service层中主要包含四个Service接口和他们的实现类,包括IUserService用来处理用户业务例如用户注册、登陆、修改个人信息等,如图4.6所示。IWeiboService用来处理微博相关的业务例如查找微博,发布微博,删除微博以及对微博的相关操作例如点赞、评论等功能的业务实现,如图4.7所示。IFriendService用来处理和用户好友相关的业务例如点赞、取消点赞、私信、拉黑等功能的业务实现,如图4.8所示。IAdmoinService用来处理和管理员相关的业务例如管理员登陆、图表展示、用户管理、微博管理、海螺管理功能的业务实现,如图4.8所示。

    图4.6用户业务处理类图

    图4.7微博业务处理类图

    图4.8微博控制器类图
    4.1.4 数据库交互类由于系统采用了MyBatis持久化框架,开发人员不需要关注和数据库之间具体的JDBC代码,而只需要处理业务逻辑,因此只需要在Dao层接口中声明方法而不需要写接口的实现类来实现方法,则是通过配置对应的配置文件,在配置文件中编写对应接口方法中的SQL语句和数据库交互。
    4.2 关键业务设计4.2.1 登陆系统
    图4.9系统登录顺序图
    用户进入主页后,可以在左边选择注册用户,或者在右边登陆系统,在注册模块,用户输入用户邮箱,密码,昵称等信息,当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台,查找用户邮箱是否已经被注册,如果被注册则在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,保存用户注册信息。
    登陆功能:当用户输入邮箱地址和密码后,如果点击下次自动登陆密码在点击登陆后,判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的 积分,然后跳转至微博首页。
    4.2.1 用户信息系统
    图4.10用户修改信息顺序图
    用户基本信息:在系统的首页中点击个人账号设置后,跳转到修改用户基本信息页面,用户可以修改这些基本信息,并将修改后的信息保存在数据库中。
    修改密码:在用户个人资料页面点击修改密码,跳转修改密码页面,用户可以输入用户的当前密码,系统判断密码是否正确,如果密码不正确,显示当面密码错误,如果输入密码正确,用户则可以输入新密码,点击修改后将修改后的密码更新到数据库中。
    修改用户头像:点击用户个人资料中修改头像,跳转至修改头像页面,用户选择头像文件,点击上传,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,点击修改后将修改后的头像地址保存在数据库中。
    修改密保:用户先要根据之前设置的密保问题来填写答案,如果密保答案错误,提示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改保存用户新的密保问题和答案。
    4.2.2微博模块
    图4.11系统登录顺序图
    发布微博:用户可以在首页发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,最后将微博信息保存在数据库中。
    搜索微博:用户可以在首页的搜索框中输入微博中提到的内容来搜索,系统在数据库中通过迷糊查询查询相关的微博信息。在页面中将搜索到的页面展示出来,并且将关键字标红显示。
    微博操作:用户可以查看个人微博、好友微博、收藏微博等信息,对微博的操作有点赞,转发,收藏,评论,删除等。
    4.2.3 好友模块关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,或者是通过页面中的搜索功能来搜索用户,对搜索到的用户进行关注。关注用户后个人关注+1,同时被关注用户粉丝数+1。
    取消关注:和关注功能类型,在导航栏中点击我的好友,在我的好友首页中查看我的关注好友,就能查看到我所关注的所有好友和好友数以及他们的个人信息,通过点击对应的取消关注就可以取消关注该用户,取消关注用户后个人关注数-1,同时被关注用户粉丝数-1。
    拉黑用户:即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。
    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值展示在前台页面中显示。
    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会将私信信息发送给对应用户。同时增加提示该用户的未读私信数。
    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,并将查询到的用户展示在页面中,同时将用户输入的关键字标红显示。
    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。
    4.2.4 海螺模块发布:用户通过点击导航栏中的神奇海螺进入海螺主页,在海螺页面的上面是发布海螺的问题框,下面的可以插入的表情按钮和问题的悬赏积分数,中间的导航栏可以选择查看最新发布、已解决、待解决、我的海螺不同的筛选条件。
    查看海螺问题:用户点击任意海螺问题,系统跳转到展示海螺的具体信息的页面,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等。
    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息等,点击回复后将回复信息保存在数据库中。
    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,点击采纳后修改海螺状态,被采纳的答案变为采纳答案,增加采纳者的海螺积分。
    4.2.5 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面。当管理员输入账号信息和密码信息后,如果用户名和密码正确就跳转到管理员首页。如果错误则给出提示。
    管理员首页:在管理员首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。
    用户管理首页:在页面上方显示导航栏,页面内容中显示所有用户的编号、邮箱、昵称、注册日期、上次登陆日期以及用户的状态,由于考虑到用户数量多以以及为了方便查找,因此在页面中一次显示10条用户数据,同时添加用户搜索功能,用户能够在输入用户昵称的关键字后来搜索用户,并且支持迷糊查询。 搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵 称为输入关键字的那部分显示为红色。
    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作。
    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博。
    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来。
    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作。
    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作。
    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以能够在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。
    4.3 数据库设计4.3.1 概述微博系统数据库表主要包括:

    1. 用户表:用来保存用户的个人信息,例如编号、昵称、姓名、密码。邮箱等信息,以用户编号作为主键,如表4-1
    2. 微博关注表:用来保存用户的关注用户信息,以关注ID作为主键,如表4-2所示
    3. 微博表:用来保存微博信息,例如微博发布时间,微博内容,微博点赞,转发,收藏数等,以微博编号作为主键,如表4-3所示
    4. 微博收藏表:用来保存用户收藏的微博信息,如微博编号,收藏时间等,以收藏编号作为主键,如表4-4所示
    5. 评论表:保存微博的评论消息,例如评论人编号、评论日期、评论内容等,以评论编号作为主键,如表4-5所示
    6. 积分表:用来保存用户获取积分的方式,例如通过每天的登陆发布微博等获取积分,以积分编号作为主键, 如表4-6所示
    7. 点赞表:用来保存微博的点赞信息,主要包括点赞人编号,点赞编号,点赞微博编号等信息。以点赞编号作为主键,如表4-7所示
    8. 积分流水表:用来保存用户获取积分的信息,主要包括积分编号、用户编号、获取日期等信息,以积分编号作为主键,如表4-8所示
    9. 转发表:用来保存用户转发的信息,主要有转发编号、转发人、微博编号等信息,以转发编号作为主键,如表4-9所示

    4.3.2 概念设计一个用户可以发布多条微博,因此用户表和微博表之间存在一对多的关系如图4.12所示。

    一条微博可以对应多个点赞、转发、收藏和评论,因此微博表和收藏、点赞、转发、收藏表之间存在一对多的对应关系,如图4.13所示。

    一个用户可以发布多个海螺,每个海螺问题可以对应多条评论。如图4.14所示。

    4.3.3 数据库表
    用户表:数据库表名USER_TAB,引用序列名SEQ_USER。


    关注表:数据库表FRIEND_TAB,引用序列名SEQ_FRIEND。


    微博表:数据库表名WEIBO_TAB,引用索引名SEQ_WEIBO。


    收藏表:数据库表名COLLECT_TAB,引用索引名SEQ_COLLECT。


    评论表:数据库表名COMM_TAB,引用索引名SEQ_COMM。


    积分表:数据库表名INTEGRAL_TAB,引用索引名SEQ_INTEGRAL。


    点赞表:数据库表名LIKE_TAB,引用索引名SEQ_LIKE。


    积分流水表:数据库表名RECORD_TAB,引用索引名SEQ_RECORD。


    转发表:数据库表名TRANS_TAB,引用索引名SEQ_TRANS。

    第五章 系统功能实现5.1 登陆系统在系统登陆注册页面当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台控制器,调用Service层中对应的方法,是Service方法中调用Dao层接口查找用户邮箱是否已经被注册,如果被注册则通过前台javaScript显示在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,将会把用户输入的注册信息通过浏览器发送请求到后台控制器中,控制器控制请求的转发页面和将用户注册信息传给Service,在Service中初始化用户的一些基本信息,例如默认头像、默认状态、初始化用户积分等操作,组装用户数据源,调用Dao层方法保存用户注册信息如图5.1所示。核心代码如下:

    登陆功能:如果点击下次自动登陆密码在点击登陆后,先将用户名和密码信息传递到Service层做业务处理,再调用Dao层接口判断邮箱地址和密码信息是否正确,如果正确并且点击了下次登陆,就将邮箱名和密码保存在浏览器Cookie中,将登陆用户保存在Session中,在处理用户登陆的Service中判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的积分,修改用户数据库中的积分数,然后跳转至微博首页,如图5.2所示。核心代码如下:



    5.2 用户信息系统用户基本信息:如图5.3所示当用户点击修改后浏览器会提交form表单,发送请求携带用户基本信息到SpringMVC的控制器中接收请求,并把form表单中的数据组装成User对象,作为参数传入Service层中对应的方法进行处理,调用Dao层接口修改用户的基本信息,并修改当前服务器中Session中的User对象信息。核心代码如下:


    修改密码:在用户个人资料页面点击修改密码,将会跳转至如图5-4所示的修改密码页面,用户可以输入用户的当前密码,前台页面通过Ajax发送异步请求,后台控制器接收请求,从数据库中获取用户当前密码是否正确,如果密码不正确,在前台页面中通过javaScript动态提示给用户当面密码错误,如果输入密码正确,用户则可以输入新密码,确认新密码,点击确认修改后浏览器提交form表单,将用户新密码传给后台Service层中对应的方法,在Service的方法中调用Dao层接口更改数据库中的用户密码,更改服务器Session中的用户信息。核心代码如下:


    修改用户头像:点击用户个人资料中修改头像,将会跳转至如图5.5所示的修改头像页面,用户选择头像文件,点击上传,form表单将图片传到后台控制器中,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,并将用户新的头像地址保存在用户信息中,传入Service中调用Dao层接口修改数据库中用户的头像信息。核心代码如下:


    修改密保:当用户点击修改密保页面时浏览器跳转至修改密保页面如图5-6所示,用户先要根据之前设置的密保问题来填写答案,前台页面通过Ajax将用户输入的密保答案传入后台控制器,与数据库中用户的密保问题答案做判断,如果密保答案错误,在页面上通过javaScript展示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改提交form表单后,浏览器发送请求在后台数据库中修改用户的密保问题和答案。核心代码如下:


    5.3 微博模块发布微博:在用户主页如图5.7所示,用户可以发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,在前台页面中,表情使用javaScript动态生成div标签并显示在页面上,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,从Session中获取当前登陆用户,组装微博数据源,将数据源传入Service中,调用Dao层接口将微博信息保存在数据库中。核心代码如下:


    搜索微博:如图5.8所示,在微博首页搜索框输入关键字点击搜索提交form表单,浏览器发送请求将关键字传到后台控制器中,在数据库中通过迷糊查询查询相关的微博信息。在Service层中具体方法中调用Dao层接口获得相关的微博信息,遍历集合,组装微博的基本信息和发布人对象,将微博中表情转换成对应的gif图片,调整微博时间格式为对应格式,修改微博内容中搜索关键字为红色显示,将修改好的数据返回到前台页面展示,如图5.9所示。核心代码如下:


    微博操作:点击微博的点赞,转发和收藏功能类型,通过Ajax将微博id提交请求到后台控制器,从Session中获取登陆用户的信息,传递参数到Service对应的方法中通过对微博不同的操作调用对应的Dao层接口将微博的点赞,转发和收藏信息保存在数据库中。点击评论后通过前台页面的点击事件跳转至JavaScript中通过Ajax发送异步请求到后台控制器中,将微博id传递到Service层中对应的方法中嗲用Dao层接口查找数据库中对应微博编号的评论信息,微博评论的分页通过对应的PageBean类控制,在数据库层通过SQL语句来控制分页要显示的条数,在控制器中传递json数据到前台页面展示。在评论信息的最后面可以发布用户自己的评论,可以添加表情,点击发表将form表单提交到后台控制器中,在后台代码中调用Dao层接口保存用户的评论信息。如图5.10所示。核心代码如下:


    个人微博:如图5.11所示:在页面上方导航栏可以点击进入用户个人主页查看我的微博,在页面中页面上方展示导航栏,下面显示用户的基本信息,中间显示用户所发布过的微博,点击微博插入的图片还可以通过JavaScript将图片放大,在页面的右侧展示系统为用户推荐的好友。点击我的微博按钮后发送的请求会被后台控制器接收,从服务器Session中获取当前登陆用户的用户id,传入Service层,在Service层中调用Dao层接口从数据库中获取当前登陆用户的微博信息组装成List集合,遍历List集合,更改微博数据源的日期格式,调用工具类将微博正文中的表情替换成对应的图片信息,在微博的分页中,使用了Oralce数据库中的伪列来获取对应区间的微博信息,实现数据库层的分页,将所有需要在页面上展示的数据传递给控制器,控制器再将数据封装在Response响应中传递到前台页面。核心代码如下:


    5.4 好友模块查看好友微博:通过点击页面导航栏中我的好友来跳转至好友微博页面,在后台控制器中先从Session中获得当前登陆用户的id值,传递给Service,在Service中调用Dao层方法先在数据库的好友表中查找对应用户的所有好友信息,然后再在数据库微博表中查看这些好友对应的微博信息按照微博发布时间倒序排列,将所有查找到的微博信息和发布用户信息封装成List集合,遍历List集合修改微博日期各式,微博表情字符转换为对应图片名称,将加工过后的集合传递给控制器,控制器将所有的组装好的数据响应到前台页面中,在前台页面中通过C标签遍历List展示微博数据如图5.12所示。核心代码如下:

    关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,点击关注后前台页面通过Ajax技术发送异步请求将被关注用户的用户id发送到后台控制器中,在控制器中获取Session中的登陆用户信息,将登陆用户的id和被关注用户的id传给Service层中对应的方法,在Service层对应的方法中做处理,组装好友信息数据源,调用Dao层接口保存用户的关注信息,同时更新用户的关注数,更新被关注用户的粉丝数,然后在前台页面中通过javaScript将页面文本信息关注修改为已关注。核心代码如下:

    取消关注:和关注功能类型,在导航栏中点击我的好友,在中间点击关注,就能查看到我所关注的所有好友和好友数以及他们的个人信息,如图5.13所示,点击取消关注,通过Ajax将好友id传递到后台控制器中,在控制器中获取请求中的用户信息,将好友id和用户id传入Service层中对应的方法,调用Dao层接口将对应用户id和好友id的好友信息删除掉,同时更改用户的关注数和被关注用户的粉丝数。然后返回响应在前台页面刷新好友列表。

    拉黑用户:和取消关注用户类似,只是在Dao层接口中,不是删除已有的用户记录而是逻辑删除,即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。核心代码如下:


    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,如图5.14所示,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值传递给前台页面,前台页面中所有展示的数值通过EL表达式从服务器发送回来的响应中获取。点击未读私信或者通过图5.13中显示的好友列表中的私信按钮,跳转至用户私信页面。核心代码如下:


    查看私信:如图5.15所示,用户通过点击私信关注好友时,通过后台控制器跳转至我的私信用户页面,页面左侧显示和该用户最近的私信信息,页面右侧显示所有有过私信的用户列表,点击列表中的用户就可以直接私信这个用户,在私信中允许插入表情。在后台代码中,将要发送私信的用户id和从Session中获取的登陆用户id传递到Service层中对应的方法,调用Dao层接口在数据库中查找与该用户相关的所有私信记录同时修改和该用户的所有私信状态为以阅读,将获取的记录组装到集合中,遍历集合调用工具类修改私信的日期格式和文本中的表情格式,同时从数据库中获取和当前登陆用户有过私信记录的所有用户,将组装好的集合返回给控制器,控制器将数据响应给前台页面中,遍历集合中的数据,展示在前台JSP页面中。核心代码如下:


    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会提交form表单,浏览器发送请求到后台控制器中,控制器获取发送用户的id和接收用户的id,调用Service层中的方法,组装数据源为私信信息,设置私信信息为未阅读状态,在Service层中对应的方法中调用Dao层接口将私信信息保存在数据库中。核心代码如下:

    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,当用户输入要查询的用户昵称,浏览器发送请求携带关键字等信息跳转至控制器中特定的方法,在控制器方法内部调用Service中的方法处理逻辑,业务层调用Dao层接口中的查找方法查找用户昵称中包含有关键字的用户,将从数据库获得的对应用户组装成一个集合,遍历集合将用户昵称中包含的关键字改成红色,控制器返回响应跳转到搜索结果页面,遍历集合展示所有查找到的用户信息。如图5.16所示。核心代码如下:


    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。在后天代码中,当用户点击其他用户的头像或名称时,浏览器发送携带用户id的请求到后台控制器中。控制器调用Service中对应的方法,在Service方法中调用Dao层接口从数据库中查看用户的基本信息和用户的微博信息,将所有的数据存放在集合中。返回到控制器中,控制器携带数据返回到前台页面中做展示,如图5.17所示。核心代码如下:


    5.5 海螺模块发布:当用户进入海螺主页时,浏览器页面发送请求到后台控制器中,调用业务层中特定的方法,Service中调用Dao层接口在数据库中查找有关不同筛选条件的海螺问题,并将查找到的问题封装在集合中,通过控制器发送服务器响应,跳转到海螺首页,并循环展示所有的海螺问题。如果用户需要发布问题,在填写了问题描述和所要悬赏的积分数后,点击发布,浏览器提交表单数据到后台控制器中,在Service层中首先判断用户的海螺积分是否大于悬赏积分,如果小于悬赏积分就返回浏览器页面提示用户积分不足。如果积分足够就调用Dao层接口把用户的海螺问题保存在数据库中,同时减少用户的海螺积分修改用户基本信息。核心代码如下:


    查看海螺问题:用户点击海螺首页具体的问题时,浏览器发送请求给后台控制器中调用Service层对应的方法,在Service层中调用Dao层接口通过海螺问题的编号来查看海螺的具体信息,通过发布问题的用户id在数据库中查找对应的发布人信息,服务器返回响应到浏览器中,展示海螺的具体信息,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等,如图5.19所示。核心代码如下:


    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息,点击回复提交表单,浏览器请求携带表单数据到后台控制器中,被控制器中具体的方法接收,获取Session中的用户编号,组装回复信息的数据源,传递参数到Service中,在Service中调用Dao层接口保存海螺的回复信息,如图5.20所示。核心代码如下:


    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,被采纳的答案变为采纳答案,增加采纳者的海螺积分,如图5.21所示,在后台代码中,当用户点击采纳后,浏览器请求携带海螺问题id和问题回复信息被后台控制器中具体的方法接收,在控制器中调用Service层,业务层中调用Dao层接口修改数据库中海螺问题的状态为已解决,修改评论表中被采纳用户的评论状态为被采纳,刷新前台海螺问题页面。核心代码如下:


    5.6 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面如图5.22所示,管理员登陆后,页面发送请求到后台控制器中,后台Controller层接收请求,将用户名和密码作为参数调用业务层中的方法,在Service层中调用Dao层接口和数据库中管理员账号表中查询,如果存在就返回管理员类,如果不存在,则抛出异常,异常层层上抛,在控制器层中接收,并将错误信息保存在方法的返回值中,在页面提示,如果用户名和密码正确,就跳转到Controller中管理员首页的处理方法中,在管理员首页的处理方法中获取需要显示的数据并展示。核心代码如下:


    管理员首页:登陆后,系统会跳转到管理员首页,在首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。后台首先在Controller层中跳转至管理员首页对应的处理方法中,调用Service层中对应的方法获取首页展示数据,并将获取到的数据组装到Map集合中,在服务层中调用Dao层中的方法来获取首页需要展示的用户、微博、评论、回答等数据保存在Map集合中,在控制层中获取到返回值数据并保存在服务器响应中,返回给前台页面使用EL表达式展示数据,如图5.23所示。核心代码如下:


    用户管理首页:后台代码通过调用Service层中对应的方法,Service方法里面则调用Dao层接口和数据库交互,获取数据库中所有的用户信息封装成List集合,返回给Controller层将用户List集合响应给浏览器,在页面中通过c标签遍历循环显示用户信息,页面的分页使用自定义分页类PageBean来保存分页信息,在数据库层做分页一次获取10条数据。核心代码如下:


    搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵称为输入关键字的那部分显示为红色。在后台代码中,点击搜索会将管理员输入的用户昵称关键字传给后台控制器中,在控制器中调用Service层中对应的搜索用户的方法,在Service层中调用Dao层接口在数据库中通过迷糊查询来获取用户。并将获取到的数据层层返回,在Controller中响应给前台页面,然后在页面中做展示,如图5.25所示。核心代码如下:


    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作,具体页面如图5.26所示。在后台代码中则是在Service层中调用Dao层接口,更改用户状态和封禁日期,解除封禁和封禁用户类似,因此不做具体说明。核心代码如下:


    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博,微博管理页面如图5.27所示,在后台代码中,当用户点击导航栏中微博管理时,浏览器会发送相应的请求到SpringMVC框架的Controller中对用的更能处理方法中,在控制器中调用Service层中显示所有微博的方法中,在Service层中再调用Dao层方法获取所有的微博信息,在分页方面没有采用在前端页面中做分页的方法,而是在数据库中通过Oracle的伪列来做分页,一次获取10条数据,最后在Controller中将获取到的微博信息响应给浏览器,浏览器中通过c标签遍历显示微博信息。核心代码如下:


    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来,具体页面如图5.28所示,在后台代码中,控制器中特定的方法接收浏览发送的搜索微博请求,调用Service层中对应的方法,将关键字作为参数传给Dao层接口中,在Dao层接口中查找数据库微博表中微博内容包含该关键字的微博信息,在数据库中则是通过模糊查询来查找对应微博。然后将查找到微博信息封装到List集合中,层层返回到Controller层中对应的方法,在方法中将数据响应给浏览器,浏览器接收响应在页面中通过c标签展示数据。核心代码如下:


    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作,当用户点击删除时,页面会携带着微博编号等参数发送请求给服务器,请求会被控制器中对应的方法所接收,将微博编号作为参数嗲用Service中对应的方法,在方法中调用Dao层接口在修改数据库中对应微博编号的微博状态为已删除,恢复微博和删除类似,只是修改数据库中对应微博信息状态为正常即可。核心代码如下:

    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,如图5.29所示,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作,在后台代码方面,当用户点击导航栏中的海螺管理时,浏览器发送请求,请求在控制器中被对应的方法接收,Service中调用Dao层接口在数据库海螺表中查找所有的海螺信息,保存在List集合中,遍历List集合组装发布人用户信息,修改日期格式等,最后将组装号的List集合返回到Controller中,响应给浏览器跳转海螺管理首页,通过使用c标签遍历显示查找到的海螺信息,因为系统中使用的分页方法一致,因此在这里不再赘述。核心代码如下:


    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以通过在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。具体页面如图5.30所示。在后台代码中,当用户输入关键字点击搜索时,浏览器发送请求到控制器中指定方法接收,在Controller中调用Service层中对应的方法处理业务逻辑,然后在Service层中调用Dao层接口通过模糊查询在数据库海螺表中查找对应的海螺信息,最后在Controller中将查找到的海螺信息响应给浏览器,在页面中通过c标签遍历展示数据。因为系统使用一样的分页方法,因此不做赘述。核心代码如下:


    第六章 总 结时光荏苒,岁月如梭,转眼之间为期半年的毕业设计以及论文的编写终于落下帷幕,回顾毕业设计的每一个阶段都使我受益匪浅,在毕设初期,经过了长达一周的深思熟虑之后我决定将B/S微博系统作为我这次毕业设计和论文的选题,因为平时对微博的接触以及近几年微博的火热程度,使我对微博系统的具体功能和优缺点都有了一个全体的把控,这也使得在之后系统功能设计时能够更加的得心应手,在毕业设计中期时进入代码编写阶段时,我选择了J2EE体系开发Web项目的B/S微博,使用面向对象语言JAVA作为开发语言,使用个人熟练掌握的SSM框架来搭建系统,SSM框架强大的功能减少了大量的冗余代码,使得系统代码的编写更加轻松,提高了系统的开发效率,然而开发一个系统并不是那么简单就能完成,在代码的编写阶段,问题接连出现,但在指导老师的指导下以及自己通过网上查阅资料,最终解决了这些问题,提高了自己代码的编写能力,同时也提高了自我学习能力,这对以后的生活学习和工作中都有着非同寻常的意义,在项目编写完成后的测试阶段,之前在编写阶段没有被发现的系统缺陷逐渐跃出水面,在每一次修复这些问题的时候,我都能感觉到自己的能力在慢慢提高,一个没有缺陷的系统是不存在的,经过对测试用例中的测试方案一一测试,然后修复掉一个又一个的缺陷后,微博系统中大多数可见性高的BUG都被修复了,但是在之后的使用中还会不断的完善系统让它变的更加成熟,而不单单只是作为一份毕设设计而存在。
    在这次毕业设计系统的开发中也让我看到了自身的一些问题,例如前端技术的不熟练导致在前台页面的修改和开发中浪费了大量的时间,微博系统中关键性的用户交互不够美观,用户体验性差,以及在系统类设计时没能正确的把控全局,设计出合理的接口,只能在后期代码编写阶段中不断的去完善。
    总而言之,在对微博系统的开发中,我学习到了很多以前没有注意到和忽略到的东西,也使我认识到了自身的一些缺陷,让我在以后的生活和工作中都能更好的认识自己,提高自己的能力,然后服务于社会,做一个对社会发展有帮助的人。
    参考文献[1] 贾文潇,邓俊杰. be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Java的Web开发技术浅析[J]. 电子测试,2016
    [2] 李传扬. 微博分析系统的设计与实现[D]. 北京邮电大学 2015
    [3] 刘运臣. 网站设计与建设[M]. 清华大学出版社, 2008
    [4] 秦雅华. be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端WEB2.0的微博网站的设计与实现[D]. 北京工业大学 2012
    [5] 陈玲,夏汛. 利用Mybatis的动态SQL实现物理分页[J]. 数字技术与应用. 2011(11)
    [6] 萨师煊,王珊. 数据库系统概论(第三版)[M].北京:高等教育出版社,1998
    [7] be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Java的数据库访问技术研究[J]. 科技资讯. 2009(04)
    [8] 张峰. be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Ajax技术与J2EE框架的Web应用研究与实现[D]. 中国地质大学 2008
    [9] be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Java多线程技术的网络编程[J]. 电脑编程技巧与维护. 2009(22)
    [10] 李威. 一种小型实用即时网络聊天通讯系统的设计[J]. 长江大学学报(自然科学版). 2011(12)
    [11] 钟睿祺. be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端微博嵌入小伙伴阅读网的分析与设计[D]. 华南理工大学 2011
    [12] 王少锋编着.面向对象技术UML教程[M]. 清华大学出版社, 2004
    [13] 徐春绵. 关于网站开发相关问题的探究[J]. 通讯世界. 2015(09)
    [14] 张宇,王映辉,张翔南. be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Spring的MVC框架设计与实现[J]. 计算机工程. 2010(04)
    [15] 胡以谰,张立平. J2EE开发模式的选择[J]. 计算机系统应用. 2002(08)
    [16] 王丽爱. 《Java程序设计》课程网站的设计与实现[J]. 电脑知识与技术. 2016(27)
    [17] 荣艳冬. 关于Mybatis持久层框架的应用研究[J]. 信息安全与技术. 2015(12)
    1 留言 2019-12-20 13:12:40 奖励50点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端ASP.NET的小清新风格的新闻发布系统

    项目展示
    项目开发背景
    ASP.NET课设
    主要功能:登录注册,新闻管理,评论回复新闻,个人信息管理

    项目运行环境
    vs2017
    Sqlserver2012

    项目运行安装
    项目包地址:https://download.csdn.net/download/qq_35268841/11865696
    项目数据库位置由于大家所用数据库不一定为2012,在此将数据库的Sql文件放出,地址如下:
    链接: https://pan.baidu.com/s/170lyOlqnE2B_Eu30WOlpMA 提取码: km8k 复制这段内容后打开百度网盘手机App,操作更方便哦

    配置数据库连接字符串(不会获取连接字符串的自行百度)https://blog.csdn.net/qq_35268841/article/details/102563070

    运行项目
    浏览器显示网页主页(网页不是响应式布局,若网页显示不整齐,缩放浏览器比例即可)
    1 留言 2019-12-07 00:45:51 奖励16点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Web在线考试系统的设计与实现

    1 课题背景与意义1.1课题开发背景当今社会,考试已经是我们必不可少的东西了,从小到大我们已经考过无数次了,以后还要考,不管是国内还是国外的各大厂家,都在不断的推出一系列的考试、认证。又是要我们去考试。我们国家的自考或是成考,以及各省市的各种考试,现在都在朝着信息化的道路前进在走。我们相信在今后这一系列的考试将会走向网络化考试的。这样才是符合信息技术发展的方向。我们要给不同的考试同一个好的解决方案。 这个方案在技术上来讲我们是采用B/S模式。 在windows/Linux平台上,使用IE浏览器,完成抽题、考试、交卷等考试任务。方便,简单的完成各种考试,这也是我们的目的所在。
    考点模块通过网络获取题库,按照题库中的抽题策略,自动给每个考生生成一份试卷,考生在线作答,考试结果数据通过网络回收,系统自动进行判分,生成考试成绩和统计数据。“在线考试系统”是集合现代考试理论、方法和现代信息技术手段的智能化网上考试系统,为学生个性化学习提供“灵活、方便、科学、公平”的“个别化考试服务”,是终结性评价系统。学生可以随时、随地进行课程结业考试。
    1.2 课题开发意义用Browser/Web模式来设计考试系统比较合适,服务器端我们采用SQL SERVER数据库系统和JSP组件来构成考试的应用服务系统;客户端采用浏览器来完成考试全过程,同时可进行远程系统维护和管理。利用网络和数据库技术,结合目前硬件价格普遍下跌与宽带网大力建设的有利优势,应用JAVA Server Page技术,开发了be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端B/S模式多用户在线考试系统这一程序。它运用方便、操作简单,效率很高(同时,它要求计算机配置也很高,尤其是服务器端).be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Web技术的网络考试系统可以借助于遍布全球的因特网进行,因此考试既可以在本地进行,也可以在异地进行,大大拓展了考试的灵活性。试卷可以根据题库中的内容即时生成,可避免考试前的压题;而且可以采用大量标准化试题,从而使用计算机判卷,大大提高阅卷效率;还可以直接把成绩送到数据库中,进行统计、排序等操作。考生通过姓名、准考证号码和口令进行登录,考试答案也存放在服务器中,这样考试的公平性、答案的安全性可以得到有效的保证。因此,采用网络考试方式将是以后考试发展的趋势。
    2 系统需求分析2.1 项目要求本系统作为一个在线的考试系统,要求实现网络考试系统的各项基本功能。从维护和安全的角度看,可以把系统设计成B/S模式的,可以让用户通过浏览器直接访问位于服务器上的考试题以及对系统进行远程维护。
    系统前台主要有考生注册和登录模块、在线考试模块、查询成绩模块以及退出登录等;系统后台主要有考生信息、考题信息、考试成绩信息、考试套题和课程信息等管理模块。
    (1)注册和登录模块考生要进入考试系统,首先需要注册一个学生证号。在注册页中输入考生的基本信息,包括学生证号、学生姓名、密码、密码问题、问题答案、性别和所学专业等。其中为防止注册的学生证号重复,在这里应用了AJAX无刷新检测用户名的技术。登录只需核实注册信息即可。
    (2)在线考试当考生准备考试时,首先需要阅读考试规则,在同意所列出的考试规则的前提下,才能选择专业和考试课程,然后才能进入考试页面开始答题。当考生提交试卷或者到达考试结束时间,系统将自动对考生提交的试卷进行评分,并给出最终成绩。
    (3)考试套题管理考试套题管理主要包括对考试题进行添加、查询、修改和删除操作。
    (4)考试题目管理考试题目管理主要包括对考试题进行添加、查询、修改和删除操作。除此之外,根据实际需要,还可以对数据库中的信息(学生信息、试题)进行维护。
    要求
    操作简单方便、界面简洁美化
    具有实时性,已注册的用户无论身处在何地,通过Internet浏览器,都可登录考试系统进行考试
    系统提供的自动交卷功能使考试到结束时间时,系统自动交卷
    提供考试时间倒计时功能,让考生随时了解考试剩余时间
    考生可以随时查看成绩
    对考生注册信息进行管理
    系统自动交卷、阅卷,保证成绩真实,准确
    系统运行稳定、安全

    2.2 开发方案选择MySql作为后台的数据库,选择myeclipse作为应用程序开发工具,应用JAVA、JSP、JavaScript、Html、Tomcat服务器技术,整个系统完全be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端B/S (Browser/Server)模式进行设计,采用strus框架进行架构。
    2.3开发环境在开发网络在线考试系统时,需要具备下面的软件环境:

    操作系统:Windows8.1
    Web服务器:Tomcat7.0
    Java开发包:JDK1.7
    开发工具:myeclipse2015
    数据库:MySQL及其图形化管理工具SQLyog
    浏览器:火狐游览器

    3 总体开发3.1 开发思想3.1.1 B/S结构开发思想B/S(Browser/Server)结构即浏览器和服务器结构。它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户工作界面是通过WWW浏览器来实现,极少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现,形成所谓三层(3-tier)结构。一个三层架构的应用程序由三部分组成,这三部分各自分布在网络中的不同地方。这三个部分分别是:工作站或表示层接口、事务逻辑、数据库以及与其相关的程序设计。在一个典型的三层架构应用程序中,应用程序的用户工作站包括提供图形用户界面(GUI)的程序设计和具体的应用程序入口表格或交互式窗口。
    以目前的技术看,局域网建立B/S结构的网络应用,并通过Internet/Intranet模式下数据库应用,相对易于把握、成本也是较低的。它是一次性到位的开发,能实现不同的人员,从不同的地点,以不同的接入方式(比如LAN, WAN, Internet/Intranet等)访问和操作共同的数据库;它能有效地保护数据平台和管理访问权限,服务器数据库也很安全 。特别是在JAVA这样的跨平台语言出现之后,B/S架构管理软件更是方便、快捷、高效。
    3.1.2 面向对象机制的设计思想所有计算机均由两种元素组成:代码和数据。精确的说,有些程序是围绕着”什么正在发生”而编写,有些则是围绕”谁正在受影响”而编写的。
    第一种编程方式叫做”面向过程的模型”,按这种模型编写的程序以一系列的线性步骤(代码)为特征,可被理解为作用于数据的代码。如 C 等过程化语言。
    第二种编程方式叫做”面向对象的模型”,按这种模型编写的程序围绕着程序的数据(对象)和针对该对象而严格定义的接口来组织程序,它的特点是数据控制代码的访问.通过把控制权转移到数据上,面向对象的模型在组织方式上有:抽象、封装、继承和多态的好处。
    3.1.3 代码分层思想由于采用B/S设计模式分层思想,同时根据软件工程的管理思想及系统分析的设计与分析的思想进行系统的开发,利用Java语言开发Web应用程序,提供String+Hibernate+Spring框架对系统的程序代码结构进行分层。分层的策略如下:

    3.2 系统功能结构设计根据网络在线考试系统的特点,可以将其分为前台和后台两个部分进行设计。前台主要用于考生注册和登录系统、在线考试、查询成绩以及修改个人资料等;后台主要用于管理员对考生信息、课程信息、考题信息和考生成绩信息等进行管理。 网络在线考试系统的前台功能如下图所示:

    网络在线考试系统的后台功能结构如下图所示:

    3.3 业务流程图设计网络在线考试的系统业务流程如下图所示:

    4 数据库设计4.1 数据库概念设计根据对系统所做的需求分析和系统设计,规划出本系统中使用的数据库实体分别为考生档案实体、管理员档案实体、课程档案实体、套题实体、考试题目实体和考生成绩实体。
    4.1.1考生档案实体考生档案实体包括编号、姓名、密码、性别、注册时间、提示问题、问题答案、专业和身份证号属性。考生档案实体的E-R图如图所示:

    4.1.2管理员档案实体管理员档案实体包括编号、管理员名、管理员密码属性。管理员档案实体的E-R图如图所示:

    4.1.3课程档案实体课程档案实体包括课程编号、课程名、添加时间属性。课程档案实体的E-R图如图所示:

    4.1.4考试题目实体考试题目实体包括编号、问题类型、所属课程、所属套题、选项A、选项B、选项C、选项D、添加时间、正确答案和备注等属性。考试题目实体的E-R图如图所示:

    4.1.5考生成绩实体考生成绩实体包括编号、准考证号、所属课程、单选题分数、多选题分数、合计分数、添加时间属性。考生成绩实体的E-R图如图所示:

    4.2 数据库逻辑设计4.2.1 tb_manager(管理员信息表)管理员信息表用来保存管理员信息,该表的结构如表所示:

    4.2.2 tb_Student(考生信息表)考生信息表用来保存考生信息,该表的结构如表所示:

    4.2.3 tb_stuResult(考生成绩信息表)考生成绩信息表用来保存考生成绩,该表中的所属课程字段whichLesson与tb_Lesson表中的Name字段相关联,并且设置为级联更新。考生成绩信息表的结构如表所示:

    4.2.4 tb_TaoTi(套题信息表)套题信息表用来保存套题信息,该表中保存着所属套题ID,套题名称,套题所属课程以及套题的添加时间信息。该表的结构如表所示:

    4.2.5 tb_Lesson(课程信息表)课程信息表用来保存课程信息,该表中保存着所属课程的ID,课程名以及课程的添加时间信息。该表的结构如表所示:

    4.2.6 tb_Questions(考试题目信息表)考试题目信息表用来保存考试题目信息。考试题目信息表的结构如表所示:

    4.3 数据表关系设计本系统设计了如图所示的数据表之间的关系,该关系实际上也反映了系统中各个实体之间的关系。

    5 详细设计5.1前台首页模块设计考生通过“考生登录”模块的验证后,可以登录到网络在线考试的前台首页,如图所示。前台首页主要用于实现前台功能导航,在该页面中只包括在线考试、成绩查询、修改个人资料和退出4个导航链接。
    由于本系统的前台首页主要用于进行系统导航,所以在实现时,采用了为图像设置热点的方法,这样可以增加页面的灵活度,使页面不至于太枯燥。下面将对如何设置图像的热点进行详细介绍。为图像设置热点,也可以称作图像映射,是指一幅图像可以建立多个超链接,即在图像上定义多个区域,每个区域链接到不同的地址,这样的区域称为热点。 图像映射有服务器端映射(Server-side-Image Map)和客户端映射(Client-side-Image Map)两种。目前使用最多的是客户端映射,因为客户端映射使图像上对应的坐标以及超链接的URL地址都在浏览器读入,省去和服务器之间互传坐标和URL的时间。

    5.2 考生信息模块设计考生信息模块主要包括考生注册、考生登录、修改个人资料以及找回密码等四个功能。考生首先要注册成为网站用户,然后才能被授权登录网站进行一系列操作的权限;登录后考生还可以修改个人的注册资料。如果考生忘记了登录密码,还可以通过网站提供的找回密码功能快速找回密码。考生信息注册模块的系统如图所示:

    考生信息模块的Action实现类Student继承了Action类。在该类中,首先需要在该类的构造方法中分别实例化考生信息模块的StudentDAO类。Action实现类的主要方法是execute(),该方法会被自动执行,这个方法本身没有具体的事务,它是根据HttpServletRequest的getParameter()方法获取的action参数值执行相应方法的。
    5.3 在线考试模块设计在线考试模块的主要功能是允许考生在网站上针对指定的课程进行考试。在该模块中,考生首先需要阅读考试规则,在同意所列出的考试规则后,才能选择考试,在选择考试课程后,系统将随机抽取试题,然后进入考试页面进行答题,当考生提交试卷或者到达考试结束时间时,系统将自动对考生提交的试卷进行评分,并给出最终考试成绩。在线考试模块的系统流程如图所示:

    考生登录到网络在线考试的前台首页后,单击“在线考试”超链接,将进入到考试规则页面,在该页面中单击“同意”按钮,即可进入到选择考试课程页面,在该页面中将以下拉列表框的形式显示需要参加考试的课程.在该页面中,单击“开始考试”按钮,将关闭当前窗口,并打开新的窗口显示试题,如图所示:

    5.4 考试题目管理模块设计网络在线考试系统的后台首页是管理员对网站信息进行管理的首页面。在该页面中,管理员可以清楚地了解网站后台管理系统包含的基本操作。

    管理员信息管理:主要包括管理员信息列表、添加管理员、修改管理员和删除管理员
    考生信息管理:主要包括查看注册考生信息列表和删除已注册的考生信息
    考生成绩查询:主要用于根据准考证号、考试课程或考试时间模糊查询考生成绩
    课程信息管理:主要包括查看课程列表、添加课程信息和删除课程信息
    套题信息管理:主要包括查看套题信息列表、添加套题信息、修改套题信息和删除套题信息
    考试题目管理:主要包括查看考试题目列表、添加考试题目、修改考试题目和删除考试题目
    退出管理:主要用于退出后台管理系统

    为了方便管理员管理,在网络在线考试系统的后台首页中显示考生成绩查询页面,其运行结果如图所示:

    管理员登录系统后,单击“考试题目管理”超链接,进入到查看考试题目列表页面,在该页面中单击“添加考试题目”超链接,进入到添加考试题目页面。在该页面的“属性课程”下拉列表框中选择“计算机专业英语”,在“所属套题”下拉列表框中将显示该课程对应的套题名称。添加考试题目页面的运行结果如图所示:

    6 软件测试6.1 软件开发技术概述Ajax技术是Asynchronous JavaScript and XML的缩写,意思是异步的JavaScript 和XML。Ajax并不是一门新的语言或技术,它是JavaScript、XML、CSS、DOM等多种已有技术的组合,它可以实现客户端的异步请求操作。这样可以实现在不需要刷新页面的情况下与服务器进行通信的效果,从而减少了用户的等待时间。
    6.2通过Ajax技术实现计时与显示剩余时间在通过Ajax技术实现计时与显示剩余时间,首先需要创建一个封装Ajax必须实现的功能的对象AjaxRequest,并将其代码保存为AjaxRequest.js,然后在开始考试页面中包含该文件,具体代码如下: <script language=“javascript” src=“..//JS/AjaxRequest.js”></script> 由于通过Ajax技术实现计时与显示剩余时间表的方法类似,下面以实现自动计时为例进行介绍。 编写调用AjaxRequest对象的函数、错误处理函数和返回值处理函数。
    计时方法showStartTime()中,首先需要获取保存在Session中的考试开始时间,并将其转化为对应的毫秒数,然后获取当前时间的毫秒数;再应用这两个时间生成两位的小时数、分钟数和秒数,并组合为新的时间;最后将其保存到showStartTime参数中,并转到输出计时时间的页面。
    参考文献[1] 刘东祥.动态网页JSP技术探究[J].时代教育,2010,(10):14-17.
    [2] 何文辉.be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端JSP的动态网站开发技术[J].吉林省教育学院学报,2012,(8):18-20.
    [3] 郭利周,于长虹,郭晓萍.be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端的网上考试安全体系的设计与构建[J].洛阳师范学院学 报,2013,(5):25-28.
    [4] 张洪伟.Tomcat Web开发及整合应用[M].北京.清华大学出版社. 2010.8:10-230
    [5] 周玫,袁振武.浅谈在线考试系统[J].科技广场,2008,(7):11-14.
    [6] 覃远霞.在线考试系统的设计与运用[J].应用科学,2010,(1):34-36.
    [7] 四维科技,杨易编着.JSP网络编程技术与案例[M].北京:人民邮电出版社,2006.
    [8] 范云之.be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Web数据库在线考试系统的设计与实现研究[J].商丘师范学院学报第22卷第5期 2006.10:1-20
    [9] 刘中兵,李伯华,邹晨编着.JSP数据库项目案例导航[M].北京:清华大学出版社,2013.
    [10] 覃远霞.在线考试系统的设计与运用[J].应用科学,2013,(1):34-36.
    [11] Bruce Eckel.Java编程思想[M].北京.机械工业出版社. 2008.9:30-280 [12] (美)舒尔第.Java2-The complete reference[M].北京.电子工业出版社. 2006.1:20-100
    [13] (美)Marty Hall.Servlet与JSP权威指南[M].北京v机械工业出版社. 2008.10:30-350
    [14] (美)Marty Hall.JavaScript高级程序设计[M].北京.人民邮电出版社. 2009.11:50-200
    [15](美)David Flanagan.JavaScript权威指南[M].北京.机械工业出版社. 2013.1:10-200
    附录:数据库源程序CREATE DATABASE db_exam;USE `db_exam`;CREATE TABLE `tb_lesson` ( `ID` INT(11) NOT NULL AUTO_INCREMENT, `Name` VARCHAR(60) DEFAULT NULL, `JoinTime` DATETIME DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=INNODB AUTO_INCREMENT=34 DEFAULT CHARSET=utf-8;INSERT INTO `tb_lesson`(`ID`,`Name`,`JoinTime`) VALUES (4,'数据库原理','2015-12-01 00:00:00'),(5,'计算机文化基础','2015-12-01 00:00:00'),(8,'计算机专业英语','2015-12-01 00:00:00'),(29,'嵌入式系统','2015-12-02 00:00:00'),(31,'物联网体系结构','2015-12-05 00:00:00'),(33,'接口与通信技术','2015-12-02 10:29:10');CREATE TABLE `tb_manager` ( `ID` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(30) DEFAULT NULL, `PWD` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;INSERT INTO `tb_manager`(`ID`,`name`,`PWD`) VALUES (1,'admin','admin'),(2,'tf111','tf111');CREATE TABLE `tb_questions` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `subject` VARCHAR(50) DEFAULT NULL, `type` CHAR(6) DEFAULT NULL, `joinTime` DATETIME DEFAULT NULL, `lessonId` INT(11) DEFAULT NULL, `taoTiId` INT(11) DEFAULT NULL, `optionA` VARCHAR(50) DEFAULT NULL, `optionB` VARCHAR(50) DEFAULT NULL, `optionC` VARCHAR(50) DEFAULT NULL, `optionD` VARCHAR(50) DEFAULT NULL, `answer` VARCHAR(10) DEFAULT NULL, `note` VARCHAR(50) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=52 DEFAULT CHARSET=utf8;INSERT INTO `tb_questions`(`id`,`subject`,`type`,`joinTime`,`lessonId`,`taoTiId`,`optionA`,`optionB`,`optionC`,`optionD`,`answer`,`note`) VALUES (37,'数据库原理的老师是谁?','单选题','2015-12-01 01:00:00',5,10,'常赞杰','陈利平','姜平','以上都不是','B','空'),(39,'网络营销的发展经历几个阶段?','单选题','2015-12-01 00:00:00',29,17,'2个','3个','5个','6个','C','空'),(40,'Internet提供的基本服务有哪些?','多选题','2015-12-01 00:00:00',29,17,'E-mail','FTP','Telnet','WWW','A,B,C,D','空'),(48,'EPROM代表什么?','单选题','2015-12-01 00:00:00',8,19,'可编程存储器','可擦可编程存储器','只读存储器','可擦可编程只读存储器','D',''),(49,'对于WWW的正确解释有哪些?','多选题','2015-12-01 00:00:00',8,19,'全球网','万维网','局域网','World Wide Web的缩写','A,B,D','');CREATE TABLE `tb_student` ( `ID` VARCHAR(16) DEFAULT NULL, `name` VARCHAR(20) DEFAULT NULL, `pwd` VARCHAR(20) DEFAULT NULL, `sex` VARCHAR(2) DEFAULT NULL, `joinTime` DATETIME DEFAULT NULL, `question` VARCHAR(50) DEFAULT NULL, `answer` VARCHAR(50) DEFAULT NULL, `profession` VARCHAR(30) DEFAULT NULL, `cardNo` VARCHAR(18) DEFAULT NULL) ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO `tb_student`(`ID`,`name`,`pwd`,`sex`,`joinTime`,`question`,`answer`,`profession`,`cardNo`) VALUES ('CN20151201000001','王大锤','111','男','2015-12-01 00:00:00','birthday','717','广告学','220198302********'),('CN20151201000002','何小花','111','女','2015-12-01 00:00:00','birthday','1','计算机应用软件','220198007********'),('CN20151225000005','戴小超','111111','女','2015-12-01 00:00:00','我最喜欢的颜色','蓝灰色','计算机应用软件','220104************'),('CN20151229000006','熊时雨','000','男','2015-12-01 00:00:00','你好','你好','公司管理','20020'),('CN20151229000007','王明','111111','男','2015-12-01 00:00:00','你好','你好','编程','52200');CREATE TABLE `tb_sturesult` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `stuId` VARCHAR(16) DEFAULT NULL, `whichLesson` VARCHAR(60) DEFAULT NULL, `resSingle` INT(11) DEFAULT NULL, `resMore` INT(11) DEFAULT NULL, `resTotal` INT(11) DEFAULT NULL, `joinTime` DATETIME DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;INSERT INTO `tb_sturesult`(`id`,`stuId`,`whichLesson`,`resSingle`,`resMore`,`resTotal`,`joinTime`) VALUES (1,'CN20151201000002','计算机专业英语',50,30,80,'2015-12-01 00:00:00'),(2,'CN20151201000001','物联网体系结构',0,20,20,'2015-12-01 00:00:00'),(4,'CN20151201000001','数据库原理',20,30,50,'2013-01-01 00:00:00'),(12,'CN20151201000001','计算机专业英语',40,60,100,'2015-12-01 00:00:00'),(14,'CN20151225000005','嵌入式系统',40,0,40,'2015-12-01 00:00:00'),(29,'CN20151201000002','接口与通信技术',40,60,100,'2015-12-01 00:00:00'),(30,'CN20151229000006','数据库原理',40,60,100,'2015-12-01 00:00:00'),(37,'CN20151229000007','计算机文化基础',0,0,0,'2015-12-01 00:00:00'),(38,'CN20151229000007','数据库原理',40,60,100,'2015-12-01 00:00:00'),(39,'CN20151229000006','嵌入式系统',0,0,0,'2015-12-01 00:00:00'),(40,'CN20151201000001','数据库原理',0,0,NULL,NULL),(41,'CN20151201000002','接口与通信技术',0,0,NULL,'2015-12-02 11:43:15'),(42,'CN20151201000002','计算机文化基础',40,0,40,'2015-12-02 13:10:12');CREATE TABLE `tb_taoti` ( `ID` INT(11) NOT NULL AUTO_INCREMENT, `Name` VARCHAR(50) DEFAULT NULL, `LessonID` INT(11) DEFAULT NULL, `JoinTime` DATETIME DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=INNODB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;INSERT INTO `tb_taoti`(`ID`,`Name`,`LessonID`,`JoinTime`) VALUES (10,'2015数据库期末考试',5,'2015-01-01 00:00:00'),(17,'2015年嵌入式期末考试题',29,'2015-12-01 00:00:00'),(19,'2015年物联网体系结构考试题',8,'2015-12-01 00:00:00'),(20,'接口与通信期末考试题',31,'2015-12-01 00:00:00');
    1 留言 2019-12-20 08:56:17 奖励45点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Java的图书购物商城

    一、功能
    登录用户
    管理员登录和用户登录两种
    注册功能
    有购买书籍功能
    查询功能:按书名查询,按ID名查询
    删除书籍
    显示用户的信息

    二、注册和登录模块的设计与实现2.1 注册和登录界面2.1.1 注册用户在进行注册时,需要通过该界面输入帐号、密码和用户身份,然后点击“点我注册”按钮进行注册,注册界面设计如图1.1所示。

    2.1.2 登录用户在进行系统登录时,需要通过该界面输入帐号、密码和用户身份,然后点击“登录”按钮进行登录,登录界面设计如图1.2所示。

    2.2 该模块涉及到的文件和类通过详细的分析,该模块涉及到的文件和类在工程中的组织如图所示。
    2.2.1 持久层用文件users.txt持久存储用户的信息,文件中以u00001:普通用户:pw00001:张三:0:上海 的形式存储,其中u00001为id,普通用户为用户类型,pw00001为用户密码,张三为用户真实姓名,0代表用户性别为男,上海为用户所在城市。所有用户的信息均以这样的格式存储,且每个用户的信息在文件中占一行。
    为了方便,在类DatabaseConfig中使用静态常量描述了文件users.txt的详细路径。
    2.2.3 文件操作层(Dao层)该层涉及到接口IUserDao和实现该接口的类UserDaoImpl,主要用来完成对文件user.txt的读和写操作。
    public Map<String, User> getUsers();
    该方法从文件中读出用户的信息并使用Map集合返回结果集。Map中key为用户id, value为使用User封装的用户信息。
    public void addUser(User u)
    该方法是将封装到User中的用户信息写入文件。
    类UserDaoImpl的核心代码如下:
    public class UserDaoImpl implements IUserDao{ @Override public void addUser(User u) { //使用缓冲流,一个用户信息占一行 File f = new File(DataBaseConfig.USER_FILE_PATH); FileWriter fw = null; BufferedWriter bw = null; try { fw= new FileWriter(f, true); bw = new BufferedWriter(fw); bw.write(u.toString()); bw.newLine(); } catch (IOException e) { … } finally { … } } @Override public Map<String, User> getUsers() { Map<String, User> users = new HashMap<String, User>(); File f = new File(DataBaseConfig.USER_FILE_PATH); FileReader fr = null; BufferedReader br = null; try { fr = new FileReader(f); br = new BufferedReader(fr); String s; String[] userInfo; User u; while((s=br.readLine()) != null){ userInfo = s.split(":"); u = new User(userInfo); users.put(userInfo[0], u); } } catch (FileNotFoundException e) { … } catch (IOException e) { … } finally { … } return users; }}
    详细请查看源代码
    个人博客(后续有时间的话放在博客上面)
    1 留言 2019-12-17 16:07:28 奖励10点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Crypto++库的RSA非对称加密实现对数据的加解密

    背景写了一个be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Crypto++加密库中RSA非对称加密算法实现的对数据加密和解密的一个小程序,Crypto++加密库就不详细介绍了,这个库提供了很多知名的加解密算法,直接调用就好了,使用起来还是比较方便的。
    写这篇文章,就是分享自己的学习心得。自己的密码学部分的知识学得不怎么好,还好有Crypto++开源库可以使用,弥补了对加解密部分的不足。现在,向大家分享使用Crypto++中的RSA非对称加密算法实现对数据的加密解密方面的知识。
    程序编译设置注意事项首先,先要下载Crypto++库的开源代码,然后,自己编译得到Crypto++的库文件。下载链接还有具体的编译步骤,可以参考这个平台上其他用户写的分享文章“使用VS2013编译Crypto++加密库”,里面有详细介绍。
    在导入Crypto++的库文件到自己的工程项目的时候,要对自己的工程项目进行编译设置。主要一点就是:项目工程的属性中的“运行库”设置,要与编译Crypto++的库文件时设置的“运行库”选项要对应一致,否则程序会编译不过的。也就是要检查LIB库工程和本测试工程的:属性 —> C/C++ —> 代码生成 —> 运行库 是否统一。
    如果编译出错,报告XX重复定义等错误,同样,要检查LIB库工程和本测试工程的:属性 —> C/C++ —> 代码生成 —> 运行库 是否统一。
    程序设计原理1. 产生公钥和私钥文件原理RSA是非对称加密算法,所以它的加密和解密的密钥是不同的,它有自己的公钥和私钥。在正常的使用过程中,公钥一般是用来加密数据,私钥用来解密数据。反之,也可以。公钥可以公开,但是私钥不可以公开。
    对于Crypto++库产生公私钥的原理如下:

    首先用类RandomPool的方法Put()产生种子seed的byte型伪随机
    RSAES_OAEP_SHA_Decryptor是一个解密的公钥密码系统在文件rsa.h 有如下定义:

    typedef RSAES<OAEP<SHA>>::Decryptor RSAES_OAEP_SHA_Decryptor; 就是在这个类用前面产生的伪随机数和密钥长度keyLength生解密的密钥
    接着,通过类FileSink打开文件szPrivateKeyFileName实行序列化操作,用HexEncoder把它转换为十六进制
    最后,用DEREncode把上面处理好的密码对象写入文件

    这样就得到私钥密码的密钥文件了。产生公钥文件的方法和产生私钥密码文件不同的地方就是使用了RSAES_OAEP_SHA_Encryptor是一个加密的公钥密码系统, 在文件rsa.h 有如下定义:
    typedef RSAES<OAEP<SHA>>::Encryptor RSAES_OAEP_SHA_Encryptor; 是用上面产生的密钥密码系统priv来生成相应公钥。
    2. 使用RSA公钥加密数据实现原理RSA公钥加密可以分成以下两种形式:一种是公钥存储在文件中,加密时,从文件获取密钥;另一种是公钥存储在程序中,直接传递存储密钥的地址。虽然,这两种形式只是公钥传递的形式上的区别,RSA加密的原理还是一样的。但,从编程的角度,把这两个进行区分。
    先介绍公钥存储在文件中的方式,那么RSA公钥加密的实现原理是:

    首先用类RandomPool在种子seed下用方法Put()产生伪随机数,Seed可以任取
    用类FileSource对公钥文件pubFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型
    然后用FileSource的对象pubFile 实例化公钥密码系统RSAES_OAEP_SHA_Encryptor生成对象pub
    用类StringSink 把outstr添加到一个String对象,接着用HexEncoder把这个对象转换为十六进制
    然后用伪随机数randPool、公钥密码系统pub和十六进制的String对象实例化一个公钥密码加密的过滤器,再用这个过滤器对字符串message进行加密把结果放到十六进制的字符串result里,这样就完成了对字符串的加密

    那么,对于另一种公钥存储在程序中的方式,RSA公钥加密的原理和上面的区别,只是在第 2 步的区别,也就是用类StringSource对公钥文件pubFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型。
    3. 使用RSA私钥钥解密数据实现原理同样,对应上述的公钥加密,私钥解密同样区分两种私钥获取的形式。
    先介绍私钥存储在文件中的方式,那么RSA私钥解密的实现原理的基本流程跟加密的基本流程差不多,就使用了几个不同的类,但是这些类跟加密函数的对应类的功能是相对的,很容易理解。

    用类FileSource对私钥文件privFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型
    然后用FileSource的对象privFile 实例化公钥密码系统RSAES_OAEP_SHA_Decryptor生成对象priv
    用类StringSink 把outstr添加到一个String对象,接着用HexEncoder把这个对象转换为十六进制
    然后用伪随机数randPool、私钥密码系统prov和十六进制的String对象实例化一个私钥密码解密的过滤器,再用这个过滤器对字符串message进行解密把结果放到十六进制的字符串result里,这样就完成了对字符串的解密

    和加密一样,对于另一种私钥存储在程序中的方式,RSA私钥解密的原理和上面的区别,只是在第 1 步的区别,也就是用类StringSource对公钥文件provFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型。
    编程实现1. 导入Crypt++库文件//*************************************************// crypt++加密库的头文件和静态库//*************************************************#include "crypt\\include\\rsa.h"#include "crypt\\include\\randpool.h"#include "crypt\\include\\hex.h"#include "crypt\\include\\files.h"using namespace CryptoPP; // 命名空间#ifdef _DEBUG #ifdef _WIN64 #pragma comment(lib, "crypt\\lib\\x64\\debug\\cryptlib.lib") #else #pragma comment(lib, "crypt\\lib\\x86\\debug\\cryptlib.lib") #endif#else #ifdef _WIN64 #pragma comment(lib, "crypt\\lib\\x64\\release\\cryptlib.lib") #else #pragma comment(lib, "crypt\\lib\\x86\\release\\cryptlib.lib") #endif#endif//*************************************************
    2. RSA产生公私钥实现BOOL GenerateRSAKey(DWORD dwRSAKeyLength, char *pszPrivateKeyFileName, char *pszPublicKeyFileName, BYTE *pSeed, DWORD dwSeedLength){ RandomPool randPool; randPool.Put(pSeed, dwSeedLength); // 生成RSA私钥 RSAES_OAEP_SHA_Decryptor priv(randPool, dwRSAKeyLength); HexEncoder privFile(new FileSink(pszPrivateKeyFileName)); // 打开文件实行序列化操作 priv.DEREncode(privFile); privFile.MessageEnd(); // 生成RSA公钥 RSAES_OAEP_SHA_Encryptor pub(priv); HexEncoder pubFile(new FileSink(pszPublicKeyFileName)); // 打开文件实行序列化操作 pub.DEREncode(pubFile); // 写密码对象pub到文件对象pubFile里 pubFile.MessageEnd(); return TRUE;}
    3. RSA公钥加密实现1> 公钥存储在文件实现string RSA_Encrypt_ByFile(char *pszOriginaString, char *pszPublicKeyFileName, BYTE *pSeed, DWORD dwSeedLength){ RandomPool randPool; randPool.Put(pSeed, dwSeedLength); FileSource pubFile(pszPublicKeyFileName, TRUE, new HexDecoder); RSAES_OAEP_SHA_Encryptor pub(pubFile); // 加密 string strEncryptString; StringSource(pszOriginaString, TRUE, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(strEncryptString)))); return strEncryptString;}
    2> 公钥存储在程序中string RSA_Encrypt_ByMem(char *pszOriginaString, char *pszMemPublicKey, BYTE *pSeed, DWORD dwSeedLength){ RandomPool randPool; randPool.Put(pSeed, dwSeedLength); StringSource pubStr(pszMemPublicKey, TRUE, new HexDecoder); RSAES_OAEP_SHA_Encryptor pub(pubStr); // 加密 string strEncryptString; StringSource(pszOriginaString, TRUE, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(strEncryptString)))); return strEncryptString;}
    4. RSA私钥解密实现1> 公钥存储在文件实现string RSA_Decrypt_ByFile(char *pszEncryptString, char *pszPrivateKeyFileName){ FileSource privFile(pszPrivateKeyFileName, TRUE, new HexDecoder); RSAES_OAEP_SHA_Decryptor priv(privFile); string strDecryptString; StringSource(pszEncryptString, TRUE, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(strDecryptString)))); return strDecryptString;}
    2> 公钥存储在程序中string RSA_Decrypt_ByMem(char *pszEncryptString, char *pszMemPrivateKey){ StringSource privStr(pszMemPrivateKey, TRUE, new HexDecoder); RSAES_OAEP_SHA_Decryptor priv(privStr); string strDecryptString; StringSource(pszEncryptString, TRUE, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(strDecryptString)))); return strDecryptString;}
    程序测试在main函数中调用上面封装好的函数进行测试,main函数为:
    char g_szPubKey[] = "30819D300D06092A864886F70D010101050003818B0030818702818100F0CE882D7CCB990323A6DB1B775EBE8F2910BFE75B4B580EF8C5089BB25FEDEEABCE2BBD2AC64A138E47F96A6C39152FE98067C0B4F5DC28F8D9394325ADB12A90A9598FF7A2A7211DEF974FC8A005D0CBCDE059FB8F7F9D214C5BAC2532CEB8EC4041AEAB19E80B8C4020F4A50102F9E738647E2384EA2FCD30C3681559CF6F020111";char g_szPrivKey[] = "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100F0CE882D7CCB990323A6DB1B775EBE8F2910BFE75B4B580EF8C5089BB25FEDEEABCE2BBD2AC64A138E47F96A6C39152FE98067C0B4F5DC28F8D9394325ADB12A90A9598FF7A2A7211DEF974FC8A005D0CBCDE059FB8F7F9D214C5BAC2532CEB8EC4041AEAB19E80B8C4020F4A50102F9E738647E2384EA2FCD30C3681559CF6F020111028180210D49E8203005F15F3F0F03C5170B18AB4892CF70EC39434F52426FB91C39C162E0100AE7C0DCFDAA1DF50E9B67351AA7942251AA68051EB8BE7145739A599220030CF5E35ED4DEA41DD6E955722AE46153339FE7417BD00ADF53B368EAB6E71FAE0F7F394A34C91612B0F11AEC5525DB84DD982E6BF10CE74F177FA51ADC51024100F80296900AF134CCC5AC12C58D741C735F5EE9CBDFB8C1B1EB039BF078E37B09322074193B7B0AE5A60B544DDDB9159294E91744404A2C7CDF96287F5483D691024100F8908925066C3ED9AC8EAFE63A59D56FCBEC354A3DD513489DEDA70E42338CD2AEBDEEF685148123B31A55CA27B2A59CA53E2352DA284F30585A5D6B571245FF02410091E367A0066FC4B4B083565616F901AD4728C5C3384E900E4E021F7E653A849BFF5E6269320C24871661046A09F4670AEE2EC264620D8394BFC1BD781398D891024057BA8AC1C608162EB55F896050D46972C0717C38520EF7BF46CC5914175D7CFF107F4547F2BBF157E4DC1E47594E1C55677F57C2E395C19897A76C44009D09A5024100BBB92D3E8776B52FA20303E39FE8AE862637BB75880D82C6580C3217445C4A95BFB6E94120AD62AADC313418A350FF21B0ED861848626CC0F55936F750B44FC4";int _tmain(int argc, _TCHAR* argv[]){ char szPrivateFile[] = "privatefile"; char szPublicFile[] = "publicfile"; char szSeed[] = "WriteBugWriteBug"; char szOriginalString[] = "WRITE-BUG技术共享平台 - 一个专注校园计算机技术交流共享的平台"; /* 密钥在文件方式 */ // 生成RSA公私密钥对 GenerateRSAKey(1024, szPrivateFile, szPublicFile, (BYTE *)szSeed, ::lstrlen(szSeed)); // RSA公钥加密字符串 string strEncryptString = RSA_Encrypt_ByFile(szOriginalString, szPublicFile, (BYTE *)szSeed, ::lstrlen(szSeed)); // RSA私钥解密字符串 string strDecryptString = RSA_Decrypt_ByFile((char *)strEncryptString.c_str(), szPrivateFile); // 显示 printf("原文字符串:\n[%d]%s\n", ::lstrlen(szOriginalString), szOriginalString); printf("密文字符串:\n[%d]%s\n", strEncryptString.length(), strEncryptString.c_str()); printf("解密明文字符串:\n[%d]%s\n", strDecryptString.length(), strDecryptString.c_str()); printf("\n\n"); /* 密钥在内存方式 */ // RSA公钥加密字符串 string strEncryptString_Mem = RSA_Encrypt_ByMem(szOriginalString, g_szPubKey, (BYTE *)szSeed, ::lstrlen(szSeed)); // RSA私钥解密字符串 string strDecryptString_Mem = RSA_Decrypt_ByMem((char *)strEncryptString_Mem.c_str(), g_szPrivKey); // 显示 printf("原文字符串:\n[%d]%s\n", ::lstrlen(szOriginalString), szOriginalString); printf("密文字符串:\n[%d]%s\n", strEncryptString_Mem.length(), strEncryptString_Mem.c_str()); printf("解密明文字符串:\n[%d]%s\n", strDecryptString_Mem.length(), strDecryptString_Mem.c_str()); system("pause"); return 0;}
    测试结果为:

    根据图片显示,数据成功被加密和解密,两种公钥存储形式测试均成功。
    总结使用Crypto++库的好处之一,就是方便易用,即使你不了解具体的加密算法,但你只要知道要使用的加密算法这个名称,依然可以做出使用相应的加密算法进行数据加解密。
    参考参考自《Windows黑客编程技术详解》一书
    3 留言 2018-11-15 08:20:03 奖励6点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端JAVA SWING结合链表的水果超市管理系统

    一、任务介绍1.1 任务描述在水果超市中,有着各种各样的水果,为了便于管理,会将水果信息记录在水果超市管理系统中进行统一管理,通过系统可以方便地实现对水果信息的增删改查操作。其中,水果信息包括水果编号、水果名称、水果单价和计价单位等。本任务要求使用所学GUI知识,编写一个水果超市管理系统,水果超市管理系统共包括系统欢迎界面和超市货物管理界面两个界面,在系统欢迎界面通过单击“进入系统”按钮,进入超市货物管理界面,在货物管理界面就可以对水果信息实现具体的操作。例如:每当有新水果运送到超市时,就需要系统管理人员在系统中增加新水果的信息,如果超市中的水果没有了就删除该水果信息,或者数量价格等需要变更时进行修改,这些操作都可以在管理系统中完成。
    UML类图

    运行结果

    登录界面

    主界面

    添加水果-1

    添加水果-2

    修改水果-1

    修改水果-2

    1.2 任务目标
    学会分析“水果超市管理系统”任务的实现思路
    根据思路独立完成“水果超市管理系统”的源代码编写、编译和运行
    掌握正则表达式来判定数字键或者数据是否合法
    掌握Java异常处理机制
    掌握ArrayLsit集合类的使用
    熟练掌握Swing包(JTextField控件、JButton控件和JTable控件)的使用,以及常用布局方式的使用

    1.3 界面实现思路登入界面: 创建login类实现界面使用BorderLayout布局。实现ActionPerformed接口中的actionPerformed方法,该方法中创建超市货物管理界面,当点击按钮时实现窗口跳转。【通过构造一个JavaJF继承JPanel实现,以及通过Image类对象结合ImageIO类的静态方法read()实现插入图片 image = ImageIO.read( File)】
    管理界面: 根据实验要求,创建FruitManage类实现界面使用GridBagLayout布局。
    1.4 增实现思路通过FruitDAO中的isExist(String)方法遍历链表所有节点,判断输入的表编号是否存在 (返回值为trun),编号存在是不增加结点,且不更新增加JTable中的信息。当编号不存在时(flase),增加链表结点,且更新JTable中的信息,并显示在窗口。
    1.5 改实现思路点击所需要修改的行时在第二行文本框显示该信息,并通过键盘输入修改选定水果的信息,其中若修改水果的编号则为无效操作,即修改的编号不返回。当修改非编号属性时返回修改结果输出显示在表中,并找到链表中该节点且修改该结点的信息。
    1.6 删实现思路点击选中所需要删除的水果的编号将选中的水果编号显示在删除行的文本框中实现,点击删除更新显示表格中的数据,并且删除链表中该节点。
    二、实现代码及运行结果UML

    login.java

    FruitManage.java

    FruitDAO.JAVA

    MyComamdListener.java

    Fruit.java

    Police.java

    三、总结或感悟3.1 错误总结
    采用链表增加结点时,判断出重复项后再次添加该结点添加成功情况
    删除结点后在窗口表格中删除,但链表中得结点依旧存在
    修改节点时将结点编号改成已存在的结点出现修改成功现象
    采用网格布局时组件间无法设置间隔
    无法在登入界面显示图片

    3.2 错误分析及解决
    由于采用链表存放信息,采用循环遍历结点信息,当结点中的信息存在时,跳出提示框,但判断下次输入结点时,遍历未从头开始遍历
    只更新窗口表格信息而未删除链表中的结点
    由编号不可更改的前提下,不将编号输入框信息返回值窗口表格以及链表中
    更换布局(GridBagLayout)设置组件间的间隔
    利用继承JPanel写painnt方法,该方法利用ImageIO类的read方法导入图片

    3.3 总结感悟
    本次实验复习第五章继承的相关知识,第六章的接口的实现,第七章异常处理是一次比较全面的实验。通过复习面向接口编程使程序有更好的封装性
    熟悉掌握了GridBagLayout布局,该布局使组件有更好的可视性
    利用java中的Vector(数据集合)传参至JTable中,具有更加灵活的特性
    学习WindowAdapter适配器的用法,当触发事件接口有很多抽象方法时利用事件适配器重写需要使用的方法即可,而不需要实现接口中所有的方法
    了解MVC结构的使用,利用模型存储数据,利用视图为模型提供数据显示的对象,利用控制器处理用户的交互操作
    0 留言 2019-12-18 09:34:19 奖励25点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端JAVA的简易计算器

    一、任务目标
    学会分析“简易计算器”任务的实现思路
    根据思路独立完成“简易计算器”的源代码编写、编译和运行
    掌握正则表达式来判定数字键或者数据是否合法
    掌握String类常用方法的使用,如:contains方法等
    掌握Java异常处理机制
    熟练掌握Swing包(JTextField控件、JButton控件和控件数组)的使用,以及常用布局方式的使用
    掌握GUI开发过程中如何处理组件上发生的界面事件

    二、实现思路2.1 界面布局实现思路
    根据实验要求,利用GridBagLayout布局将每个组件放在合适的位置,利用GridBagConstraints类中的Insets方法实现组件间隔
    利用数组存放每个组件显示的文本

    2.2 事件处理实现思路设计ComputerListener接口继承按钮触发事件ActionListener接口以增加其抽象方法实现将界面事件传至PoliceListen类(PoliceListen类实现接口ComputerListener)做事件处理。
    2.3 计算功能实现思路
    输入合法机制

    避免第一位为符号,设置判断当第一位按非数字使不处理当第一位为零,第二位也为零,设置判断当第一位为零时输入数字无效避免首位为零,其后出现多个零(即0001),判断该输入的倒数第二位是否为符号,倒数第一位是否为0,在对按钮0。是则不做处理避免输出数字不合法(多个小数点 即6.6.6),利用循环以符号位为分割线,判该数字是否存在已存在小数点,即每个运算符号后的数字至多存在一个小数点排除多符号一起串连(即8+*9+6*/5),点击运算符触发事件并判断前一位是否为符号,是则不做处理
    计算字符串

    判断最后一位是否为运算符,利用String类中的charAt()方法提取最后一位进行判断,是则提示错误,否则运算字符和数字分离,两次利用StringTokenizer类进行字符串提取分析分别得到数字序列和运算符序列将数字序列和运算符序列存放在两个链表中,链表的删除较为便利根据优先级计算,设置两个循环(当符号链表中的数据不为空则继续),第一个循环计算所有得乘除,即符号前后得两个数乘除,结果放在第一个数中删除第一个数和删除符号;第二个循环计算所有的加减,结果放在第一个数中删除第一个数和删除符号

    三、实现代码及运行结果UML图

    实现代码

    运行结果

    四、总结或感悟4.1 错误分析
    输入一个数中多个小数点
    分析器的StringTokenizer的是否错误
    正则表达式的使用错误

    4.2 错误解决
    以运算符为分割,运算符后的数字之多只有一个小数点
    在分析器使用分析字符串中的标点符号时未排除”0”,最后在单步调试中发现错误
    “[+-*/]” 该正则表达式可以匹配”.”,导致浪费大量的时间检查在”.”的触发事件上

    4.3 总结感悟通过本次实验学习到了以下几点:

    该开始入手实验时无从下手,一边考虑如何布局,一边考虑如何输出合法,一边考虑如何实现计算,一心三用,没有主次先后的编程观念。浪费比较多的时间;而后有了步骤,先将组件间的布局做好,在考虑输入数据的合法,最后在合法的情况下实现计算
    学习到了一种新的布局方式GridBagLayout布局,该布局也是一种类似网格布局,改进GridLayout布局不能改变组件在网格中的大小的,组件间的间隔问题
    本次实验未使用 WindowBuilding插件,主要的原因时想检验以下自己在暑假看课本的成果,以及个人认为在使用插件时容易将代码弄乱。不过在布局方面花费比较多的时间。但也熟悉了几个布局的特点,以及他们的常用方法
    本次实验最大的收获就是熟悉掌握了一个类中实例的对象在利用构造方 法在各个类中的重复使用,以及复习接口的相关知识

    五、代码附录Text.java

    ComputerListener.java

    Win.java

    PoliceListen.java
    1 留言 2019-11-17 20:15:59 奖励15点积分
  • MySql高性能优化

    一、表的优化1. 定长与变长分离如id int ,占4个字节,char(4)占4个字符长度,也是定长,
    即,每一个单元值占的字节是固定的;
    核心且常用字段,宜建成定长,放在一张表里;
    而varchar,text,blob,这种变长字段,适合单放一张表,用主键与核心表关联起来。
    2. 常用字段和不常用字段要分离需要结合网站具体的业务来分析,分析字段的查询场景,查询频率低的字段,单拆出来。
    3. 在1对多需要关联统计的字段上,添加冗余字段二、列选择类型1. 字段类型优先级整型>date,time>enum,char>varchar>blob,text
    列的特点分析:
    整型:定长,没有国家/地区之分,没有字符集的差异;
    比如:tinyint 1,2,3,4,5 <—> char(1) a,b,c,d,e
    从空间上,都是占1个字节,但是order by排序,前者快,
    原因:后者需要考虑字符集与校对集(就是排序规则)
    time :定长,运算快,节省空间。考虑时区,写SQL时不方便 where >’2005-10-12’;
    enum: 能起约束值的目的,内部用整型来存储,但与char联查时,内部要经过串与值的转化(如,男女,分别用1,0表示);
    char :定长,考虑字符集和校对集;
    varchar:不定长,要考虑字符集的转换与排序时的校对集,速度慢;
    text/blob:无法使用内存临时表(排序等操作只能在磁盘上进行);
    2. 够用就行,不要慷慨原因:大的字段浪费内存,影响速度;
    以年龄为例:tinyint unsigned not null ,可以存储225岁,对于人来说足够用,如果用int的话就浪费了3个字节;
    以varchar(10),varchar(300)存储的内容相同,但在表联查时,varchar(300)要花更多的内存;
    3. 尽量避免用NULL()原因:NULL不利于索引,要用特殊的字节标注;
    在磁盘上占据的空间其实更大;
    1 留言 2019-12-11 21:21:22 奖励11点积分
  • 【Cocos Creator实战教程(5)】——打砖块(物理引擎,碰撞检测) 精华

    1. 知识点
    物理引擎碰撞检测
    2. 步骤2.1 准备工作搭一个游戏背景
    2.2 小球运动再建一个物理层,用来装游戏里的带有物理属性的东西,设置锚点为左下角

    wall:墙//小球碰到就会反弹的那种墙 ground:地面//球碰到地面,这局游戏就结束了 brick_layout:砖块布局//这个单词我们之前讲过了就不讲了 ball:球//就是球 paddle:桨//这里特指那个可以控制移动的白色长方形

    这个wall肯定是要有碰撞属性的,在属性面板,添加一个物理组件 (物理->rigidbody)。
    因为我们的墙有上,左,右三面,所以再添加三个碰撞组件(一个节点可以有多个碰撞组件)。

    编辑一下

    地面同理,小球同理,托盘同理 。(这里把地面和墙分开是为了后面墙和地面可能有不同的逻辑)
    现在已经编辑了几个物理节点的碰撞包围盒,但还没有编辑他们的物理属性(cc.RigidBody)
    先从小球开始,点击ball节点,在属性检查器可以看到

    Cocos Creator从1.5版本开始支持Box2D物理游戏引擎,Box2D是一个优秀的刚体模拟框架,关于Box2D的知识可以去网络上自行了解。
    把第一个参数勾选,代表启用碰撞回调,可以在脚本里写回调函数
    Bullet:高速运动的物体(子弹)开启,避免穿透,这里不用勾选
    type选择Dynamic,

    static:不会受到力的影响,不会受到速度影响,指的是物理引擎,我们依然可以通过移动节点来改变位置 。
    kinematic:不受力的影响,会受到速度影响 。
    dynamic:受力影响,受速度影响 。
    animated:和动画结合使用。

    Gravity Scale设置为0(标准是1,数值代表比例),也就是没有重力。
    设置线速度(1000,1000)
    在下面的碰撞组件里,设置Friction (摩擦系数)等于0(没有摩擦力),Restitution(弹性系数)等于1(没有动量损耗)

    因为小球是我们的主角,左右的碰撞都是对球来说的,所以碰撞属性都在小球这一方设置就可以了。
    另外要设置wall,ground,paddle,brick的type为staticbrick的tag为1,ground的tag为2,paddle的tag为3,wall的tag位4
    下面来看脚本
    BrickLayout.js
    cc.Class({ extends: cc.Component, properties: { padding: 0, spacing: 0, cols: 0, brickPrefab: cc.Prefab, bricksNumber: 0, }, init(bricksNumber) { this.node.removeAllChildren(); this.bricksNumber = bricksNumber; for (let i = 0; i < this.bricksNumber; i++) { let brickNode = cc.instantiate(this.brickPrefab); brickNode.parent = this.node; brickNode.x = this.padding + (i % this.cols) * (brickNode.width + this.spacing) + brickNode.width / 2; brickNode.y = -this.padding - Math.floor(i / this.cols) * (brickNode.height + this.spacing) - brickNode.height / 2; } }});
    2.3 添加砖块自己写了一个动态添加砖块的布局脚本,传入需要添加的砖块数量就可以动态加入的布局节点中。
    2.4 结束界面完善好游戏逻辑,我使用了MVC模式编写脚本。

    3. 总结至此,我便给大家简要的介绍了一下物理引擎,更多的功能需要大家自己探索实践。
    列出几个大家常问的问题:
    3.1如何移动刚体?当我们的一个节点上有一个刚体,我们要进行移动。一般我们都会通过节点的setPosition进行移动,但是刚体不会被影响,不管是Static、还是Dynamic还是Kinematic都不会被影响我们可以通过1、瞬时动作cc.place来进行移动而且不会影响刚体原本的运动轨迹2、Action的所有动作。cc.moveBy;cc.moveTo;等等
    3.2 碰撞组件和物理组件有什么不同?碰撞组件没有类型之分,只要相交就会发生碰撞事件,如果不对碰撞进行处理,那就没有任何影响。物理碰撞组件分类型,因为他们先会绑定刚体。如果刚体类型不同则会有不同的效果。和Dynamtic类型刚体绑定的PhysicsBoxCollider会受重力影响,可以设置速度和Static类型刚体绑定的物理组件,不会受重力影响,不可以设置速度,可以通过设置位置让其移动和Kinematic类型刚体绑定的物理组件,不受重力影响,可以设置速度
    例如,本文中我们就是用了物理碰撞组件,所以刚体类型选择上要有一定技巧。
    在现实开发情况下,拾取道具和横像动作例如进攻多用碰撞组件 ,而竖向动作例如弹跳多用物理碰撞组件。
    3.3 三种物理组件有什么不同?绑定了Dynamic(运动)类型的物理组件不能穿透绑定了Static(静态)类型的物理组件绑定了Dynamic类型的物理组件不能穿透绑定了Kinematic类型的物理组件Static和Kinematic不会触发碰撞事件,Static和Static;Kinematic和Kinematic不会触发碰撞事件;
    所以,因为我们不能将ball选为kinematic,尽管此游戏我们忽略了重力。
    3.4 物理组件如何进行碰撞回调?首先RigidBody要开启碰撞监听然后当前节点下有如下函数
    在函数碰撞体刚开始接触时调用一次onBeginContatct:function(contact,selfCollider,otherCollider){}在两个碰撞体结束接触时被调用一次onEndContact:fucntion(contact,setCollider,otherCollider){}每次要处理碰撞体接触逻辑是被调用onPreSolve:function(contact,selfCollider,otherCollider){}每次处理完碰撞体接触时被调用onPostSolve:fucntion(contact,selfCollider,otherCollider){}
    3.5 碰撞组件的回调var manager = cc.director.getCollisionManager();manager.enabled = true;脚本里面先开启碰撞监听,因为默认是关闭然后有以下函数://当碰撞产生时调用onCollisionEnter:function(other,self){}//在碰撞产生后,在碰撞结束前,每次计算完碰撞结果后调用onCollisionStay:function(other,self){}//当碰撞结束后调用onCollisionExit;function(other,self){}
    部分素材来源于网络,欢迎提问
    5 留言 2018-11-25 22:52:54 奖励25点积分
  • 炼数成金数据分析课程---16、机器学习中的分类算法 精华

    一、决策树原理决策树是用样本的属性作为结点,用属性的取值作为分支的树结构。
    决策树的根结点是所有样本中信息量最大的属性。树的中间结点是该结点为根的子树所包含的样本子集中信息量最大的属性。决策树的叶结点是样本的类别值。决策树是一种知识表示形式,它是对所有样本数据的高度概括决策树能准确地识别所有样本的类别,也能有效地识别新样本的类别。
    决策树算法ID3的基本思想:
    首先找出最有判别力的属性,把样例分成多个子集,每个子集又选择最有判别力的属性进行划分,一直进行到所有子集仅包含同一类型的数据为止。最后得到一棵决策树。
    J.R.Quinlan的工作主要是引进了信息论中的信息增益,他将其称为信息增益(information gain),作为属性判别能力的度量,设计了构造决策树的递归算法。
    举例子比较容易理解:
    对于气候分类问题,属性为:

    天气(A1) 取值为: 晴,多云,雨
    气温(A2) 取值为: 冷 ,适中,热
    湿度(A3) 取值为: 高 ,正常
    风 (A4) 取值为: 有风, 无风

    每个样例属于不同的类别,此例仅有两个类别,分别为P,N。P类和N类的样例分别称为正例和反例。将一些已知的正例和反例放在一起便得到训练集。
    决策树叶子为类别名,即P 或者N。其它结点由样例的属性组成,每个属性的不同取值对应一分枝。
    若要对一样例分类,从树根开始进行测试,按属性的取值分枝向下进入下层结点,对该结点进行测试,过程一直进行到叶结点,样例被判为属于该叶结点所标记的类别。
    某天早晨气候描述为:

    天气:多云
    气温:冷
    湿度:正常
    风: 无风

    它属于哪类气候呢?——————-从图中可判别该样例的类别为P类。
    ID3就是要从表的训练集构造图这样的决策树。实际上,能正确分类训练集的决策树不止一棵。Quinlan的ID3算法能得出结点最少的决策树。
    ID3算法:

    对当前例子集合,计算各属性的信息增益
    选择信息增益最大的属性Ak
    把在Ak处取值相同的例子归于同一子集,Ak取几个值就得几个子集
    对既含正例又含反例的子集,递归调用建树算法
    若子集仅含正例或反例,对应分枝标上P或N,返回调用处

    一般只要涉及到树的情况,经常会要用到递归。
    对于气候分类问题进行具体计算有:
    ⒈ 信息熵的计算: 其中S是样例的集合, P(ui)是类别i出现概率:
    |S|表示例子集S的总数,|ui|表示类别ui的例子数。对9个正例和5个反例有:
    P(u1)=9/14P(u2)=5/14H(S)=(9/14)log(14/9)+(5/14)log(14/5)=0.94bit⒉ 信息增益的计算:
    其中A是属性,Value(A)是属性A取值的集合,v是A的某一属性值,Sv是S中A的值为v的样例集合,| Sv |为Sv中所含样例数。
    以属性A1为例,根据信息增益的计算公式,属性A1的信息增益为
    S=[9+,5-] //原样例集中共有14个样例,9个正例,5个反例S晴=[2+,3-]//属性A1取值晴的样例共5个,2正,3反S多云=[4+,0-] //属性A1取值多云的样例共4个,4正,0反S雨=[3+,2-] //属性A1取值晴的样例共5个,3正,2反3.结果为
    属性A1的信息增益最大,所以被选为根结点。
    4.建决策树的根和叶子
    ID3算法将选择信息增益最大的属性天气作为树根,在14个例子中对天气的3个取值进行分枝,3 个分枝对应3 个子集,分别是:
    其中S2中的例子全属于P类,因此对应分枝标记为P,其余两个子集既含有正例又含有反例,将递归调用建树算法。
    5.递归建树
    分别对S1和S3子集递归调用ID3算法,在每个子集中对各属性求信息增益.
    (1)对S1,湿度属性信息增益最大,以它为该分枝的根结点,再向下分枝。湿度取高的例子全为N类,该分枝标记N。取值正常的例子全为P类,该分枝标记P。
    (2)对S3,风属性信息增益最大,则以它为该分枝根结点。再向下分枝,风取有风时全为N类,该分枝标记N。取无风时全为P类,该分枝标记P。
    二、PYTHON实现决策树算法分类本代码为machine learning in action 第三章例子,亲测无误。
    1、计算给定数据shangnon数据的函数:
    def calcShannonEnt(dataSet): #calculate the shannon value numEntries = len(dataSet) labelCounts = {} for featVec in dataSet: #create the dictionary for all of the data currentLabel = featVec[-1] if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 labelCounts[currentLabel] += 1 shannonEnt = 0.0 for key in labelCounts: prob = float(labelCounts[key])/numEntries shannonEnt -= prob*log(prob,2) #get the log value return shannonEnt

    创建数据的函数
    def createDataSet(): dataSet = [[1,1,'yes'], [1,1, 'yes'], [1,0,'no'], [0,1,'no'], [0,1,'no']] labels = ['no surfacing','flippers'] return dataSet, labels
    3.划分数据集,按照给定的特征划分数据集
    def splitDataSet(dataSet, axis, value): retDataSet = [] for featVec in dataSet: if featVec[axis] == value: #abstract the fature reducedFeatVec = featVec[:axis] reducedFeatVec.extend(featVec[axis+1:]) retDataSet.append(reducedFeatVec) return retDataSet
    4.选择最好的数据集划分方式
    def chooseBestFeatureToSplit(dataSet): numFeatures = len(dataSet[0])-1 baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0; bestFeature = -1 for i in range(numFeatures): featList = [example[i] for example in dataSet] uniqueVals = set(featList) newEntropy = 0.0 for value in uniqueVals: subDataSet = splitDataSet(dataSet, i , value) prob = len(subDataSet)/float(len(dataSet)) newEntropy +=prob * calcShannonEnt(subDataSet) infoGain = baseEntropy - newEntropy if(infoGain > bestInfoGain): bestInfoGain = infoGain bestFeature = i return bestFeature
    5.递归创建树
    用于找出出现次数最多的分类名称的函数。
    def majorityCnt(classList): classCount = {} for vote in classList: if vote not in classCount.keys(): classCount[vote] = 0 classCount[vote] += 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
    用于创建树的函数代码
    def createTree(dataSet, labels): classList = [example[-1] for example in dataSet] # the type is the same, so stop classify if classList.count(classList[0]) == len(classList): return classList[0] # traversal all the features and choose the most frequent feature if (len(dataSet[0]) == 1): return majorityCnt(classList) bestFeat = chooseBestFeatureToSplit(dataSet) bestFeatLabel = labels[bestFeat] myTree = {bestFeatLabel:{}} del(labels[bestFeat]) #get the list which attain the whole properties featValues = [example[bestFeat] for example in dataSet] uniqueVals = set(featValues) for value in uniqueVals: subLabels = labels[:] myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels) return myTree
    然后是在python 名利提示符号输入如下命令:
    myDat, labels = trees.createDataSet()myTree = trees.createTree(myDat,labels)print myTree
    结果是:
    {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}6.实用决策树进行分类的函数
    def classify(inputTree, featLabels, testVec): firstStr = inputTree.keys()[0] secondDict = inputTree[firstStr] featIndex = featLabels.index(firstStr) for key in secondDict.keys(): if testVec[featIndex] == key: if type(secondDict[key]).__name__ == 'dict': classLabel = classify(secondDict[key], featLabels, testVec) else: classLabel = secondDict[key] return classLabel
    在Python命令提示符,输入:
    trees.classify(myTree,labels,[1,0])
    得到结果:
    'no'Congratulation. Oh yeah. You did it.!!!
    1 留言 2019-11-28 10:56:59 奖励15点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端WinInet的HTTPS文件下载实现 精华

    背景如果你之前写过be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端WinInet库的HTTP下载文件,那么你在看完本文之后,就会发觉,这是和HTTP文件下载的代码几乎是一模一样的,就是有几个地方的区别而已。但是,本文不是对HTTP和HTTPS在WinInet库中的区别进行总结的,总结就另外写。
    本文就是be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端WinInet网络库,实现通过HTTPS传输协议下载文件功能的小程序。现在,就把开发过程的思路和编程分享给大家。
    主要函数介绍介绍HTTPS下载文件使用到的主要的WinInet库中的API函数。
    1. InternetOpen介绍
    函数声明
    HINTERNET InternetOpen(In LPCTSTR lpszAgent,In DWORD dwAccessType,In LPCTSTR lpszProxyName,In LPCTSTR lpszProxyBypass,In DWORD dwFlags);
    参数lpszAgent指向一个空结束的字符串,该字符串指定调用WinInet函数的应用程序或实体的名称。使用此名称作为用户代理的HTTP协议。dwAccessType指定访问类型,参数可以是下列值之一:



    Value
    Meaning




    INTERNET_OPEN_TYPE_DIRECT
    使用直接连接网络


    INTERNET_OPEN_TYPE_PRECONFIG
    获取代理或直接从注册表中的配置,使用代理连接网络


    INTERNETOPEN_TYPE_PRECONFIG WITH_NO_AUTOPROXY
    获取代理或直接从注册表中的配置,并防止启动Microsoft JScript或Internet设置(INS)文件的使用


    INTERNET_OPEN_TYPE_PROXY
    通过代理的请求,除非代理旁路列表中提供的名称解析绕过代理,在这种情况下,该功能的使用



    lpszProxyName指针指向一个空结束的字符串,该字符串指定的代理服务器的名称,不要使用空字符串;如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY,则此参数应该设置为NULL。
    lpszProxyBypass指向一个空结束的字符串,该字符串指定的可选列表的主机名或IP地址。如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY的 ,参数省略则为NULL。
    dwFlags参数可以是下列值的组合:



    VALUE
    MEANING




    INTERNET_FLAG_ASYNC
    使异步请求处理的后裔从这个函数返回的句柄


    INTERNET_FLAG_FROM_CACHE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND


    INTERNET_FLAG_OFFLINE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND



    返回值成功:返回一个有效的句柄,该句柄将由应用程序传递给接下来的WinInet函数。失败:返回NULL。

    2. InternetConnect介绍
    函数声明
    HINTERNET WINAPI InternetConnect( HINTERNET hInternet, LPCTSTR lpszServerName, INTERNET_PORT nServerPort, LPCTSTR lpszUserName, LPCTSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD dwContext);
    参数说明hInternet:由InternetOpen返回的句柄。lpszServerName:连接的ip或者主机名nServerPort:连接的端口。lpszUserName:用户名,如无置NULL。lpszPassword:密码,如无置NULL。dwService:使用的服务类型,可以使用以下

    INTERNET_SERVICE_FTP = 1:连接到一个 FTP 服务器上INTERNET_SERVICE_GOPHER = 2INTERNET_SERVICE_HTTP = 3:连接到一个 HTTP 服务器上
    dwFlags:文档传输形式及缓存标记。一般置0。dwContext:当使用回叫信号时, 用来识别应用程序的前后关系。返回值成功返回非0。如果返回0。要InternetCloseHandle释放这个句柄。

    3. HttpOpenRequest介绍
    函数声明
    HINTERNET HttpOpenRequest( _In_ HINTERNET hConnect, _In_ LPCTSTR lpszVerb, _In_ LPCTSTR lpszObjectName, _In_ LPCTSTR lpszVersion, _In_ LPCTSTR lpszReferer, _In_ LPCTSTR *lplpszAcceptTypes, _In_ DWORD dwFlags, _In_ DWORD_PTR dwContext);
    参数
    hConnect:由InternetConnect返回的句柄。
    lpszVerb:一个指向某个包含在请求中要用的动词的字符串指针。如果为NULL,则使用“GET”。
    lpszObjectName:一个指向某个包含特殊动词的目标对象的字符串的指针。通常为文件名称、可执行模块或者查找标识符。
    lpszVersion:一个指向以null结尾的字符串的指针,该字符串包含在请求中使用的HTTP版本,Internet Explorer中的设置将覆盖该参数中指定的值。如果此参数为NULL,则该函数使用1.1或1.0的HTTP版本,这取决于Internet Explorer设置的值。
    lpszReferer:一个指向指定了包含着所需的URL (pstrObjectName)的文档地址(URL)的指针。如果为NULL,则不指定HTTP头。
    lplpszAcceptTypes:一个指向某空终止符的字符串的指针,该字符串表示客户接受的内容类型。如果该字符串为NULL,服务器认为客户接受“text/*”类型的文档 (也就是说,只有纯文本文档,并且不是图片或其它二进制文件)。内容类型与CGI变量CONTENT_TYPE相同,该变量确定了要查询的含有相关信息的数据的类型,如HTTP POST和PUT。
    dwFlags:dwFlags的值可以是下面一个或者多个。



    价值
    说明




    INTERNET_FLAG_DONT_CACHE
    不缓存的数据,在本地或在任何网关。 相同的首选值INTERNET_FLAG_NO_CACHE_WRITE。


    INTERNET_FLAG_EXISTING_CONNECT
    如果可能的话,重用现有的连接到每个服务器请求新的请求而产生的InternetOpenUrl创建一个新的会话。 这个标志是有用的,只有对FTP连接,因为FTP是唯一的协议,通常在同一会议执行多个操作。 在Win 32 API的缓存一个单一的Internet连接句柄为每个HINTERNET处理产生的InternetOpen。


    INTERNET_FLAG -超链接
    强制重载如果没有到期的时间也没有最后修改时间从服务器在决定是否加载该项目从网络返回。


    INTERNET_FLAG_IGNORE_CERT_CN_INVALID
    禁用的Win32上网功能的SSL /厘为基础的打击是从给定的请求服务器返回的主机名称证书检查。 Win32的上网功能用来对付证书由匹配主机名和HTTP请求一个简单的通配符规则比较简单的检查。


    INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
    禁用的Win32上网功能的SSL /厘为基础的HTTP请求适当的日期,证书的有效性检查。


    INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP
    禁用的Win32上网功能能够探测到这种特殊类型的重定向。 当使用此标志,透明的Win32上网功能允许对HTTP重定向的URL从HTTPS。


    INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
    禁用的Win32上网功能能够探测到这种特殊类型的重定向。 当使用此标志,透明的Win32上网功能允许从HTTP重定向到HTTPS网址。


    INTERNET_FLAG_KEEP_CONNECTION
    使用保持活动语义,如果有的话,给HTTP请求连接。 这个标志是必需的微软网络(MSN),NT LAN管理器(NTLM)和其他类型的身份验证。


    INTERNET_FLAG_MAKE_PERSISTENT
    不再支持。


    INTERNET_FLAG_MUST_CACHE_REQUEST
    导致一个临时文件如果要创建的文件不能被缓存。 相同的首选值INTERNET_FLAG_NEED_FILE。


    INTERNET_FLAG_NEED_FILE
    导致一个临时文件如果要创建的文件不能被缓存。


    INTERNET_FLAG_NO_AUTH
    不尝试HTTP请求身份验证自动。


    INTERNET_FLAG_NO_AUTO_REDIRECT
    不自动处理HTTP请求重定向只。


    INTERNET_FLAG_NO_CACHE_WRITE
    不缓存的数据,在本地或在任何网关。


    INTERNET_FLAG_NO_COOKIES
    不会自动添加Cookie标头的请求,并不会自动添加返回的Cookie的HTTP请求的Cookie数据库。


    INTERNET_FLAG_NO_UI
    禁用cookie的对话框。


    INTERNET_FLAG_PASSIVE
    使用被动FTP语义FTP文件和目录。


    INTERNET_FLAG_RAW_DATA
    返回一个数据WIN32_FIND_DATA结构时,FTP目录检索信息。 如果这个标志,或者未指定代理的电话是通过一个CERN,InternetOpenUrl返回的HTML版本的目录。


    INTERNET_FLAG_PRAGMA_NOCACHE
    强制要求被解决的原始服务器,即使在代理缓存的副本存在。


    INTERNET_FLAG_READ_PREFETCH
    该标志目前已停用。


    INTERNET_FLAG_RELOAD
    从导线获取数据,即使是一个本地缓存。


    INTERNET_FLAG_RESYNCHRONIZE
    重整HTTP资源,如果资源已被修改自上一次被下载。 所有的FTP资源增值。


    INTERNET_FLAG_SECURE
    请确保在使用SSL或PCT线交易。 此标志仅适用于HTTP请求。



    dwContext:OpenRequest操作的上下文标识符。

    4. InternetReadFile介绍
    函数声明
    BOOL InternetReadFile( __in HINTERNET hFile,__out LPVOID lpBuffer,__in DWORD dwNumberOfBytesToRead,__out LPDWORD lpdwNumberOfBytesRead);
    参数

    hFile[in]
    由InternetOpenUrl,FtpOpenFile, 或HttpOpenRequest函数返回的句柄.
    lpBuffer[out]
    缓冲器指针
    dwNumberOfBytesToRead[in]
    欲读数据的字节量。
    lpdwNumberOfBytesRead[out]
    接收读取字节量的变量。该函数在做任何工作或错误检查之前都设置该值为零

    返回值成功:返回TRUE,失败,返回FALSE

    程序设计原理该部分讲解下程序设计的原理以及实现的流程,让大家有个宏观的认识。原理是:

    首先,使用 InternetCrackUrl 函数分解URL,从URL中提取网站的域名、路径以及URL的附加信息等。关于 InternetCrackUrl 分解URL的介绍和实现,可以参考 “URL分解之InternetCrackUrl” 这篇文章
    使用 InternetOpen 建立会话,获取会话句柄
    使用 InternetConnect 与网站建立连接,获取连接句柄
    设置HTTPS的访问标志,使用 HttpOpenRequest 打开HTTP的“GET”请求
    使用 HttpSendRequest 发送访问请求,同时根据出错返回的错误码,来判断是否设置忽略未知的证书颁发机构,以确保能正常访问HTTPS网站
    根据返回的Response Header的数据中,获取将要接收数据的长度
    使用 InternetReadFile 接收数据
    关闭句柄,释放资源

    其中,上面的 8 个步骤中,要注意第 4 步访问标志的标志设置;注意第 5 步的忽略未知的证书颁发机构的设置;同时,还要注意的就是第 6 步,获取返回的数据长度,是从响应信息头中的获取“Content-Length: ”(注意有个空格)这个字段的数据。
    编程实现1. 导入WinInet库#include <WinInet.h>#pragma comment(lib, "WinInet.lib")
    365bet英文 2. HTTPS文件下载编程实现// 数据下载// 输入:下载数据的URL路径// 输出:下载数据内容、下载数据内容长度BOOL Https_Download(char *pszDownloadUrl, BYTE **ppDownloadData, DWORD *pdwDownloadDataSize){ // INTERNET_SCHEME_HTTPS、INTERNET_SCHEME_HTTP、INTERNET_SCHEME_FTP等 char szScheme[MAX_PATH] = {0}; char szHostName[MAX_PATH] = { 0 }; char szUserName[MAX_PATH] = { 0 }; char szPassword[MAX_PATH] = { 0 }; char szUrlPath[MAX_PATH] = { 0 }; char szExtraInfo[MAX_PATH] = { 0 }; ::RtlZeroMemory(szScheme, MAX_PATH); ::RtlZeroMemory(szHostName, MAX_PATH); ::RtlZeroMemory(szUserName, MAX_PATH); ::RtlZeroMemory(szPassword, MAX_PATH); ::RtlZeroMemory(szUrlPath, MAX_PATH); ::RtlZeroMemory(szExtraInfo, MAX_PATH); // 分解URL if (FALSE == Https_UrlCrack(pszDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH)) { return FALSE; } // 数据下载 HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hRequest = NULL; DWORD dwOpenRequestFlags = 0; BOOL bRet = FALSE; unsigned char *pResponseHeaderIInfo = NULL; DWORD dwResponseHeaderIInfoSize = 2048; BYTE *pBuf = NULL; DWORD dwBufSize = 64*1024; BYTE *pDownloadData = NULL; DWORD dwDownloadDataSize = 0; DWORD dwRet = 0; DWORD dwOffset = 0; do { // 建立会话 hInternet = ::InternetOpen("WinInetGet/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { Https_ShowError("InternetOpen"); break; } // 建立连接(与HTTP的区别 -- 端口) hConnect = ::InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTPS_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0); if (NULL == hConnect) { Https_ShowError("InternetConnect"); break; } // 打开并发送HTTPS请求(与HTTP的区别--标志) dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | // HTTPS SETTING INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_RELOAD; if (0 < ::lstrlen(szExtraInfo)) { // 注意此处的连接 ::lstrcat(szUrlPath, szExtraInfo); } hRequest = ::HttpOpenRequest(hConnect, "GET", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0); if (NULL == hRequest) { Https_ShowError("HttpOpenRequest"); break; } // 发送请求(与HTTP的区别--对无效的证书颁发机构的处理) bRet = ::HttpSendRequest(hRequest, NULL, 0, NULL, 0); if (FALSE == bRet) { if (ERROR_INTERNET_INVALID_CA == ::GetLastError()) { DWORD dwFlags = 0; DWORD dwBufferSize = sizeof(dwFlags); // 获取INTERNET_OPTION_SECURITY_FLAGS标志 bRet = ::InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwBufferSize); if (bRet) { // 设置INTERNET_OPTION_SECURITY_FLAGS标志 // 忽略未知的证书颁发机构 dwFlags = dwFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA; bRet = ::InternetSetOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags)); if (bRet) { // // 再次发送请求 bRet = ::HttpSendRequest(hRequest, NULL, 0, NULL, 0); if (FALSE == bRet) { Https_ShowError("HttpSendRequest"); break; } } else { Https_ShowError("InternetSetOption"); break; } } else { Https_ShowError("InternetQueryOption"); break; } } else { Https_ShowError("HttpSendRequest"); break; } } // 接收响应的报文信息头(Get Response Header) pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize]; if (NULL == pResponseHeaderIInfo) { break; } ::RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize); bRet = ::HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL); if (FALSE == bRet) { Https_ShowError("HttpQueryInfo"); break; }#ifdef _DEBUG printf("[HTTPS_Download_ResponseHeaderIInfo]\n\n%s\n\n", pResponseHeaderIInfo);#endif // 从 中字段 "Content-Length: "(注意有个空格) 获取数据长度 bRet = Https_GetContentLength((char *)pResponseHeaderIInfo, &dwDownloadDataSize); if (FALSE == bRet) { break; } // 接收报文主体内容(Get Response Body) pBuf = new BYTE[dwBufSize]; if (NULL == pBuf) { break; } pDownloadData = new BYTE[dwDownloadDataSize]; if (NULL == pDownloadData) { break; } ::RtlZeroMemory(pDownloadData, dwDownloadDataSize); do { ::RtlZeroMemory(pBuf, dwBufSize); bRet = ::InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet); if (FALSE == bRet) { Https_ShowError("InternetReadFile"); break; } ::RtlCopyMemory((pDownloadData + dwOffset), pBuf, dwRet); dwOffset = dwOffset + dwRet; } while (dwDownloadDataSize > dwOffset); // 返回数据 *ppDownloadData = pDownloadData; *pdwDownloadDataSize = dwDownloadDataSize; } while (FALSE); // 关闭 释放 if (NULL != pBuf) { delete[]pBuf; pBuf = NULL; } if (NULL != pResponseHeaderIInfo) { delete[]pResponseHeaderIInfo; pResponseHeaderIInfo = NULL; } if (NULL != hRequest) { ::InternetCloseHandle(hRequest); hRequest = NULL; } if (NULL != hConnect) { ::InternetCloseHandle(hConnect); hConnect = NULL; } if (NULL != hInternet) { ::InternetCloseHandle(hInternet); hInternet = NULL; } return bRet;}
    程序测试在main函数中,调用上述封装好的函数,下载文件进行测试。
    main函数为:
    int _tmain(int argc, _TCHAR* argv[]){ char szHttpsDownloadUrl[] = "https://download.microsoft.com/download/0/2/3/02389126-40A7-46FD-9D83-802454852703/vc_mbcsmfc.exe"; BYTE *pHttpsDownloadData = NULL; DWORD dwHttpsDownloadDataSize = 0; // HTTPS下载 if (FALSE == Https_Download(szHttpsDownloadUrl, &pHttpsDownloadData, &dwHttpsDownloadDataSize)) { return 1; } // 将数据保存成文件 Https_SaveToFile("https_downloadsavefile.exe", pHttpsDownloadData, dwHttpsDownloadDataSize); // 释放内存 delete []pHttpsDownloadData; pHttpsDownloadData = NULL; system("pause"); return 0;}
    测试结果:
    根据返回的Response Header知道,成功下载67453208字节大小的数据。

    查看目录,有65873KB大小的“https_downloadsavefile.zip”文件成功生成,所以,数据下载成功。

    总结be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端 WinInet 库的 HTTPS 下载文件原理并不复杂,但是,因为涉及较多的 API,每个 API 的执行都需要依靠上一个 API 成功执行返回的数据。所以,要仔细检查。如果出错,也要耐心调试,根据返回的错误码,结合程序前后部分的代码,仔细分析原因。
    同时要注意在使用 HttpOpenRequest 和 HttpSendRequest 函数中,对 HTTPS 的标志设置以及忽略未知的证书颁发机构。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2018-12-22 10:15:55 奖励15点积分
  • 利用网页打开本地exe文件

    首次接到“利用网页打开本地exe文件”这个任务时,还真有点摸不着头脑,后来细想,在淘宝上点击卖家的旺旺能开启本地的旺旺,这不就是利用的网页打开本地exe文件吗?
    了解这种实实在在存在的合理需求后,开始调查。经过网上搜索查询,主要归纳为两种实现方式,方式一:利用JS打开本地exe文件。一般浏览器,由于安全问题,都会禁止掉这个特性,这就导致部分浏览器不支持该种方式。方式二:利用浏览器外部协议(URL Procotol)打开本地exe文件。用这种方式实现,任何浏览器都兼容。在实际开发中,当然首选方式二。
    一、利用注册表文件将外部协议写入注册表[HKEY_CLASSES_ROOT\PCTV]@="PCTVProtocol""URL Protocol"="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe\""[HKEY_CLASSES_ROOT\PCTV\DefaultIcon]@="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe,1\"" [HKEY_CLASSES_ROOT\PCTV\shell] [HKEY_CLASSES_ROOT\PCTV\shell\open] [HKEY_CLASSES_ROOT\PCTV\shell\open\command]@="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe\" \"%1\""将以上代码存入reg文件中,双击文件执行即可。在浏览器中输入 “pctv://” 或 “pctv://param1,param2” ,执行后即可打开对应路径下的exe文件。
    二、 在安装exe文件时将外部协议写入注册表在实际部署中,不会让客户安装完程序再手动单击注册表文件将安装路径写入注册表,最容易让人接受的方式就是在安装exe文件时将安装路径写入注册表。利用Inno Setup打包exe文件时,在脚本中加入如下代码即可:
    [Registry] Root:HKCR;Subkey:"PCTV";ValueType:string;ValueName:"URL Protocol";ValueData:"{app}\{#MyAppExeName}";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\DefaultIcon";ValueType:string;ValueData:"{app}\{#MyAppExeName}";Flags:createvalueifdoesntexist uninsdeletekey Root:HKCR;Subkey:"PCTV\shell";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\shell\open";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\shell\open\command";ValueType:string;ValueData:"{app}\{#MyAppExeName} ""%1""";Flags:createvalueifdoesntexist uninsdeletekey这样,在浏览器中输入 “pctv://” 或 “pctv://param1,param2” ,执行后即可打开对应路径下的exe文件。
    以上两种写入注册的方式,允许在外部协议中带参数。
    三、遇到的问题在利用外部协议打开本地exe文件时,通过查看日志记录,看到会出现路径不对的问题。通过查看代码在程序中用Environment.CurrentDirectory获取可执行文件的路径,但是通过浏览器打开exe文件时,Environment.CurrentDirectory获取的是浏览器exe文件的路径,这样在程序中就会报错。解决方法是将Environment.CurrentDirectory修改为Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)即可。
    0 留言 2019-12-06 11:30:53 奖励15点积分
  • PC微信逆向分析のWeTool内部探秘 精华

    作者:zmrbak(赵庆明老师)
    前言先不说微信在社交领域的霸主地位,我们仅从腾讯公司所透露的在研发微信过程中踩过的无数的坑,以及公开的与微信相关的填坑的源码中,我们可以感受到,单从技术上讲,微信是一款非常伟大的产品。然而,伟大的产品,往往会被痴迷于技术的人送进实验室,运用各种可能的工具将其大卸八块,以参悟其“伟大”之所在!。
    WeTool,一款免费的微信社群管理工具,正是一群痴迷于技术的人对于微信这个伟大的产品的研究而得到的成果。在微商界,这个软件真可谓是鼎鼎大名、如雷贯耳。如果你还不知晓这个软件,那么你肯定不是微商界的人。如果你想对你的微信群进行管理,而又不想花钱,也许这个软件就是你最佳的选择。当然,免费软件的套路都是一样的,WeTool“有意地”不满足你的一些特殊需求,如果真的很想要的话,当然是要付费的,那就购买“企业版”吧。
    但是,对于一个对技术有强烈兴趣的人来说,研究WeTool与研究PC微信一样有趣,在这里,我把它们两个一起送进实验室,一窥其中的奥秘!
    微信中的WeTool由于腾讯干预,目前WeTool免费版本已不再公开提供下载。但之前的旧版本仍然可以自动升级到最新版。如果你想获得WeTool这个软件,我想,你应该知道该怎么做了吧。如果你还是不知道,很抱歉,这篇文章对你来说太深奥了。那么我对你的建议是:关掉这个网页吧。
    WeTool在启动的时候,会检查当前计算机上是否安装了版本匹配的PC微信。倘若找不到,或者版本不匹配,WeTool会引导你到它的官网去下载一个版本匹配的PC微信(可能比较旧,但能用)。下载完毕后,还需要你手动去安装一下。
    在WeTool启动的时候,还会检查微信的登录状态,如果微信还未完成登录,WeTool会等待微信登录之后,再开启自己的管理界面。
    这里的问题是:WeTool是如何得知微信是否已经登录了呢?
    在这里,我们使用PCHunter来检查一下微信(WeChat.exe)的进程模块。我们可以看到,在微信的进程中加载了一个特殊的DLL文件(WeHelp.dll),而它的父目录是一个特殊的字符串:“2.6.8.65”,恰好与我们当前运行的微信版本一致。再上一层的目录,“WeToolCore”,很明显,这里的文件是WeTool的一部分。

    恰恰是这个DLL文件帮助WeTool完成了与微信之间的各种互动。也就是说,WeTool通过WeHelp.dll这个文件,可以感知到微信的各种活动,当然也包括微信是否已经登录等等…
    窥探WeTool如果在不经意之间关闭了WeTool,你会发现,你的微信也被关闭了。这又是为什么呢?
    如果你曾经用OD调试过软件,你会发现当你的OD被关闭的时候,被OD所调试的那个软件也被关闭掉了。因此,我们猜想,WeTool对于微信来说,应该使用的是类似于OD之于其他软件相同的原理,那就是“调试”。
    在WeTool管理你的微信的时候,你也会发现,这时候微信无法被OD所附加。其实,还是“调试”。当一个软件已经处于某个调试器的“调试”之下,为了防止出错,调试器会拒绝对这个已处于被调试中的软件的再次调试。这进一步印证了WeTool对于微信的“调试”的事实。
    然而就是这么一个“小小的”设置,就击碎不少“小白”想调试WeTool美梦。
    既然我们找到了WeTool对于微信的关键,那就是文件“WeHelp.dll”。那么,我们就把这个文件请入我们的实验室,让我们把它一点一点地拆开,细细探寻其中的一点一滴的奥秘。
    拆解WeTool在动手拆解之前,我们还是先了解一下WeTool到底向我们的计算机上安装了些什么东东。顺着桌面上的“WeTool 免费版”,我们找到了WeTool安装的目录,安装目录之下22个文件夹和84个文件。当然,让我们比较感兴趣的就是“WeChatVersion”这个文件夹,因为它的名字与微信(WeChat)太让人能联想到一起了。

    双击“WeChatVersion”,我们看到如下结果。恰好是以微信曾经的一个个版本号命名的文件夹。我们猜想,这个文件夹一定与这个版本的微信之间存在中某种联系。目前,我们可以得到最新的微信版本是2.6.8.68(此版本为更新版;从腾讯官网可下载到的版本仅为2.6.8.65),而这里恰好有一个以该版本号命名的文件夹“2.6.8.65”。

    我们双击打开“2.6.8.65”这个文件夹。文章前面所提到的“WeHelp.dll”文件赫然在目。点开其他类似微信版本号的文件夹,同样,每个文件夹中都有这两个文件。唯一的区别就是文件的大小不一样。
    由于我们使用的微信版本是2.6.8.65,那么我们就针对2.6.8.65文件夹下的这个“WeHelp.dll”进行研究。通过二进制对比,我们发现该文件夹下的“WeHelp.dll”文件与微信中加载的“WeHelp.dll” 文件为同一个文件。

    由此,我们得出结论:WeTool为不同版本的微信分别提供了不同的WeHelp.dll文件,在WeTool启动的时候,把WeChatVersion中对应与当前版本微信号的文件夹复制到当前Windows登录用户的应用程序数据文件夹中,然后再将里面的“WeHelp.dll”加载到微信进程的内存中。
    WeHelp解析WeTool为“WeHelp.dll”设置了一道阻止“动态调试”的障碍,这足以让所有的动态调试器,在没有特殊处理前,对它根本无法下手。
    如果能绕道而行,那何必强攻呢?于是我们请出静态分析的利器——IDA PRO 32。注意,这里务必使用32位版本的,因为只有在32位版本中,才可以把汇编代码转换成C语言的伪代码。相比于汇编代码来说,C代码就直观的多了。
    打开IDA,点击按钮“GO”,然后把WeHelp.dll拖入其中,接下来就是十几秒的解析,解析完毕后,界面如下:

    从IDA解析的结果中,让我们很惊奇的是,在“WeHelp.dll”中居然未发现什么加壳啊、加密啊、混淆啊等等这些对于程序版权保护的技术。也许是WeTool太自信了吧!毕竟WeTool是事实上的业界老大,其地位无人可以撼动。
    对于和微信之间交互的这部分功能来说,其实对于一个刚入门的、比较勤奋的逆向新手,只需经过半年到一年时间的练手,这部分功能也是可以完成。对于WeTool来说,其真正的核心价值不在这里,而在于其“正向”的管理逻辑,以及自己后台的Web服务器。在它的管理界面,各种功能实现里逻辑错综复杂,如果你想逆向的话,还不如重写算了,况且它都已经免费给你用了,还有必要逆向吗!!当然,WeTool后台的服务器,你根本就碰不到。
    从IDA解析的结果中,可以看到WeHelp中各个函数、方法,毫无遮拦地完全展示在眼前。而在右侧的窗口中,按下F5,瞬间汇编代码变成了C语言的伪代码。

    对于一个稍稍有一些Window API编程经验的人来说,这些全部都是似曾相识的C代码,只需简单地猜一猜,就能看明白写的是啥。如果还是不懂的话,那就打开Visual Studio,对照着看吧。这里是DllMain,也就是DLL的入口函数。我们还是来创建一个C++的动态链接库(dll)的项目,来对照着看吧:

    fdwReason=1,恰好,DLL_PROCESS_ATTACH=1。一旦DLL被加载,则马上执行DllMain这个函数中的DLL_PROCESS_ATTACH分支。也就是说,当“WeHelp.dll”这个文件被微信加载到进程之后,马上执行一下DllMain函数,DLL_PROCESS_ATTACH分支里面的这两个函数就会马上执行。

    鼠标双击第一个函数(sub_10003040),到里面去看看这个函数里面有啥,如下图,它的返回值来自于一个Windows Api(桃红色字体)——“RegisterWindowMessageW”,查看MSDN后,发现,原来是注册Windows消息。
    这不是我们最想要的,按ESC键,返回。
    鼠标双击下一个函数(sub_100031B0),页面变成这个啦。很明显,在注册一个窗口类。对于一个窗口来说,最重要的就是它的回调函数,因为要在回调函数中,完成对窗口的所有事件处理。这里,lpfnWndProc= sub_10003630,就很明显了,这就是回调函数。

    双击sub_10003630这个函数,窗口切换为如下内容。除了第一条语句的if之外,剩下的if…else if…else if是那么的引人注目。每一个比较判断之后,都调用了一个函数。而判断的依据是传入的参数“lParam”要与一个dword的值比较。
    我们猜测,这些函数大概是WeHelp和微信之间交互相关的函数吧。当然,这只是猜测,我们还要进一步验证才行。

    sub_10003630这个函数,是窗口的回调函数,我们要重点关注。那么,我们先给它改个名字吧。在函数名上点右键,选中“Rename global item”,我们取个名字叫“Fn_WndProc”吧。于是页面就变成了这样:

    虽然在IDA中,“WeHelp.dll”中的函数(方法)全部显示出来了,但是也有40多个呢,我们找个简单一点的来试试。CWeHelp::Logout(void),这个函数没有参数,那么我们就选这个吧。在左侧函数窗口中双击CWeHelp::Logout(void),右侧窗口换成了这个函数的C语言伪代码(如果你显示的还是汇编,请点击汇编代码后按F5)。

    在前面,我们看到,在回调函数中,lParam要与一个dword值进行比较。在这个函数中,我们发现,这里为lParam赋了一个dword类型的值。为了方便记忆,我们把这个dowrd值改个名字吧,因为是Logout函数中用到的数字,那么就叫做“D_Logout”吧。

    接下来,我们要看看”还有谁”在用这个数值。在我们修改后的“D_Logout”上点右键,选择“Jump to xref…”。原来这个数值只有两个地方使用,一个就是当前的“Logout”函数,而另一个却是在”Fn_WndProc”中,那不就是前面的那个回调函数嘛!选中“Fn_WndProc”这一行,点击OK!

    又一次看到了熟悉的if…else if…,还有和“D_Logout”进行比较的分支,而这个分支里面只调用了一个函数sub_10005940,而且不带参数。

    双击函数“sub_10005940”后,发现这个函数很简单。核心语句只有两条,首先调用了sub_100030F0函数,然后得到一个返回值。接下来,为这个返回值加上一个数值“0x3F2BF0”,再转换成一个函数指针,再给一个0作为参数,再调用这个函数指针。最后返回结果。

    我们这里要关注,来自于“sub_100030F0”函数的返回值result到底是什么?
    同样,双击这个函数(sub_100030F0),进去看看呗!原来,调用了一个Windows API函数(GetModuleHandleW),查看MSDN后,发现原来这个函数的功能就是取微信的基址(WeChatWin.dll)。

    那就简单多了!是不是说,如果我们在微信中执行“((int (__stdcall *)(_DWORD))(weChatWinBaseAddress + 0x3F2BF0))(0);”这么一句代码,就可以实现Logout功能呢?
    当然,这只是猜测,我们还需要进一步验证。
    猜测验证打开原来创建的C++的动态链接库项目,把 case分支DLL_PROCESS_ATTACH换成如下内容:
    case DLL_PROCESS_ATTACH: { DWORD weChatWinBaseAddress = (int)GetModuleHandleW(L"WeChatWin.dll"); ((int(__stdcall*)(DWORD))(weChatWinBaseAddress + 0x3F2BF0))(0); }
    注意:把从IDA中拷贝过来的代码中 “_DWORD”中的下划线去掉,就可以编译通过了。

    启动微信,登录微信(这时候,手机微信中会显示“Windows微信已登录”)。
    使用OD附加微信(确保WeTool已经退出,否则OD附加不成功)。
    在OD汇编代码窗口点右键,选择”StrongOD\InjectDll\Remote Thread”,选中刚才Visual Studio中编译成功的那个dll文件。

    一秒钟!OK,神奇的事情发生了:微信提示,你已退出微信!
    同时,手机微信上原来显示的 “Windows微信已登录”,也消失了。
    从这里我们可以确定,微信“真的”是退出了,而不是崩掉了。

    总结其实逆向研究,并不只是靠苦力,更重要的是强烈的好奇心和发散的思维。也许一个瞬间,换一下思维模式,瞬间一切都开朗了。一个人的力量是有限的,融入一个圈子,去借鉴别人的成功经验,同时贡献自己成功经验,你会发现,逆向研究其实是一件非常有趣的事情。当然,我研究的后编写源码都是免费公开的,你可以到GitHub(https://github.com/zmrbak/PcWeChatHooK)上下载,也欢迎和我们一起学习和研究。点击入群:456197310
    后记2019年3月17日前,本人对WeTool进行过一些探索,始终没有取得进展。时隔两个月之后,突然有所发现,于是在2019年5月29日将我的探索成果录制成一个个视频,分享给和我一起研究和探索微信的好朋友。结果,在他们中激起了强烈的反响,有不少的朋友的研究进度有了一个飞跃性的发展,还有几个朋友短短几日之间,就远远超越了我的研究进度。看到这样的场景,让我的内心充满欢喜。当然,还有不少朋友的评价,更是让我开心,现摘录如下:











    源码分享:https://github.com/zmrbak/PcWeChatHooK
    2 留言 2019-09-14 15:14:33 奖励35点积分
  • 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 是无连接的
    2 留言 2019-04-24 10:50:21 奖励15点积分
  • be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端MuPDF库实现PDF文件转换成PNG格式图片 精华

    背景之所以会接触MuPDF是因为,有位群友在Q群里提问,如何将PDF保存为.PNG图片格式。我一看到这个问题,就蒙了,因为我没有接触过类似的项目或程序。但是,作为一群之主的我,还是要给初学者一个答复的,所以便去网上搜索了相关信息,才了解到有MuPDF开源库的存在。
    MuPDF是一种轻量级的PDF,XPS和电子书阅读器。由各种平台的软件库,命令行工具和查看器组成。支持许多文档格式,如PDF,XPS,OpenXPS,CBZ,EPUB和FictionBook 2。
    后来,自己就根据网上搜索到的一些资料,实现了be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端MuPDF库将PDF指定页转换成PNG格式图片的小程序。现在,我就把程序的实现思路和过程写成文档,分享给大家。
    实现思路对于MuPDF库的源码下载以及编译过程,可以参考本站的《使用VS2013编译MuPDF库》这篇文章。
    其实,在MuPDF库中就提供了这一个功能:将PDF指定页转换成PNG格式图片,所以,我们直接编译MuPDF提供的代码就可以了。
    示例程序代码位于MuPDF库源码的“docs”目录下的“example.c”文件,我们只需使用VS2013创建一个空项目,然后把“example.c”文件导入项目中,接着将“include”目录中的头文件以及编译出来的libmupdf.lib、libmupdf-js-none.lib、libthirdparty.lib导入文件中即可。
    其中,“example.c”中主要的函数就是 render 函数,我们主要是把参数传进render函数,就可以把pdf转换成png图片了。
    对于“example.c”的代码可以不用修改,直接编译运行即可。但是,为了方便演示,我们还是对“example.c”中的 main 函数进行修改。
    编码实现以下是“example.c”文件中 render 函数的源码:
    void render(char *filename, int pagenumber, int zoom, int rotation){ // Create a context to hold the exception stack and various caches. fz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); // Open the PDF, XPS or CBZ document. fz_document *doc = fz_open_document(ctx, filename); // Retrieve the number of pages (not used in this example). int pagecount = fz_count_pages(doc); // Load the page we want. Page numbering starts from zero. fz_page *page = fz_load_page(doc, pagenumber - 1); // Calculate a transform to use when rendering. This transform // contains the scale and rotation. Convert zoom percentage to a // scaling factor. Without scaling the resolution is 72 dpi. fz_matrix transform; fz_rotate(&transform, rotation); fz_pre_scale(&transform, zoom / 100.0f, zoom / 100.0f); // Take the page bounds and transform them by the same matrix that // we will use to render the page. fz_rect bounds; fz_bound_page(doc, page, &bounds); fz_transform_rect(&bounds, &transform); // Create a blank pixmap to hold the result of rendering. The // pixmap bounds used here are the same as the transformed page // bounds, so it will contain the entire page. The page coordinate // space has the origin at the top left corner and the x axis // extends to the right and the y axis extends down. fz_irect bbox; fz_round_rect(&bbox, &bounds); fz_pixmap *pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), &bbox); fz_clear_pixmap_with_value(ctx, pix, 0xff); // A page consists of a series of objects (text, line art, images, // gradients). These objects are passed to a device when the // interpreter runs the page. There are several devices, used for // different purposes: // // draw device -- renders objects to a target pixmap. // // text device -- extracts the text in reading order with styling // information. This text can be used to provide text search. // // list device -- records the graphic objects in a list that can // be played back through another device. This is useful if you // need to run the same page through multiple devices, without // the overhead of parsing the page each time. // Create a draw device with the pixmap as its target. // Run the page with the transform. fz_device *dev = fz_new_draw_device(ctx, pix); fz_run_page(doc, page, dev, &transform, NULL); fz_free_device(dev); // Save the pixmap to a file. fz_write_png(ctx, pix, "out.png", 0); // Clean up. fz_drop_pixmap(ctx, pix); fz_free_page(doc, page); fz_close_document(doc); fz_free_context(ctx);}
    程序测试我们修改 main 函数直接调用上述函数接口, main 函数为:
    int main(int argc, char **argv){ // 文件路径 char filename[MAX_PATH] = "C:\\Users\\DemonGan\\Desktop\\test.pdf"; // 转换的页码数 int pagenumber = 1; // 缩放比例 int zoom = 100; // 旋转角度 int rotation = 0; // 处理 render(filename, pagenumber, zoom, rotation); system("pause"); return 0;}
    直接运行程序,目录下有 out.png 图片生成,打开图片查看内容,发现是 test.pdf 的第一页内容,所以转换成功。
    总结这个程序的实现,自己可以不用写代码就可以完成。因为MuPDF已经把接口都封装好了,而且也有示例程序可以直接调用。如果想要把界面做得更有好些,可以把程序写成界面程序,然后直接调用现在的这个 render 函数接口,进行转换即可。
    3 留言 2018-11-06 22:31:04 奖励15点积分
显示 0 到 15 ,共 15 条

热门回复

eject