分类

课内:
不限
类型:
不限 毕业设计 课程设计 小学期 大作业
汇编语言 C语言 C++ JAVA C# JSP PYTHON PHP
数据结构与算法 操作系统 编译原理 数据库 计算机网络 软件工程 VC++程序设计
游戏 PC程序 APP 网站 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019 2020

资源列表

  • 基于PHP和MYSQL数据库实现的失物招领系统

    一、功能描述1.1 系统实现的目的和意义
    目的:在新校区为大家提供一个失物招领的平台。
    意义:现在新校区面积较大,同学们丢失物品后,只通过线下途径进行效率低下且失主与拾物者联系不便,所以想通过这个系统建立线上交流的平台,提高失物找回的效率。

    1.2 系统实现的功能可以发布丢失物品和捡拾物品的信息,浏览以往发布的信息并提供搜索功能。在物品成功归还后可以撤销已经发布的消息。首页,即广场,展示已经发布的物品的消息,按时间排序展示,有多种分类:

    大类:寻失物/寻失主
    小类:地点,丢失时长(一周内,一个月内,三个月内)
    在首页还有搜索功能,输入关键字可以进行搜索,搜索内容包括时间地点物品特点等等
    个人中心,进入时先登陆,然后可以进行丢失物品或捡拾物品或撤销已发布的消息等操作。其中发布消息时要填写表格,内容包括物品图片,物品描述,丢失人姓名联系方式,丢失的时间,地点

    1.3 系统开发的环境

    Mysql:5.5.56
    PHP:5.5.38
    服务器:nginx
    网站原型设计软件 Axure

    二、总体结构2.1 文件清单(按文件系统树型方式排列)文件夹 PATH 列表,卷序列号为 0E94-2604
    C:.│ addmessage.php //添加信息│ delete.php //删除信息│ detail.php //详细信息│ esc.php //用户注销│ findpeople.php //查找失主│ findthing.php //查找失物│ getmessage.php //ajax入口│ guanli.php //用户个人信息管理│ index.php //主页│ join.php //注册页面│ link.php //连接数据库│ logoin.php //登录界面│ people.php //个人信息界面│ readme.txt //帮助文档│ ├─bootstrap //前端界面框架├─css│ style.css //层叠样式表文件│ ├─images //图片│ buct.png //buct图标│ message.png //信息图标│ more.png //详细信息图标│ user.png //用户界面图标│ ├─js│ │ index.js //网站脚本│ │ │ └─jquery│ jquery-3.2.1.min.js //网站脚本库│ └─database findlost.sql //数据库导出文件
    2.2 系统功能结构图下图是招领广场网页所对应的功能结构图,对于初次登录的用户,会检测有无cookie,若有则会直接进入招领广场页面,如果没有则会进入登录页面,在登录页面有登录选项和注册选项。在招领广场中有查找功能,并且有指向详细信息和个人中心的链接。

    下面是个人中心网页所对应的功能结构图,在这个页面中有发布丢失信息,发布找失主信息、管理已经发布的信息等三个功能链接。该页面也可以注销账户或者回到招领广场

    2.3 系统的总流程图(标明每个模块的文件名)
    三、详细设计3.1 数据结构3.1.1 树的遍历,增加删除节点Html是一种超文本标记语言,浏览器会解析html代码生成DOM树,如下表示:

    只有html代码界面并不完美,这就需要为每个html元素设置样式属性,就是css属性,通过下面的一段代码来解释这个事情:

    网页上每个元素都是有样式的,节点之间可能有共同的样式,也可能会有自己独特的样式,浏览器对每个节点进行css样式的渲染时,都首先需要找到这个节点的位置,然后再设置具体的属性,本质上是树的一个深度优先搜索遍历,比如上图中的<div>标签,在进行样式的设定时,首先要从根节点,即html标签节点向下遍历搜素,找到所有div标签的位置,这个位置指向的是一个数组,数组中的div到底哪个才是应该设置属性的目标div呢?这就涉及到另外的一个东西,叫做css选择器,有类选择器,标签选择器,id选择器等,标签选择器就是给这个界面的所有特定标签进行样式的设定,比如说给所有的div设置样式就可以使用标签选择器,上图中用到了类选择器,即class=“”中的值,回到刚才的div数组,浏览器会查找这个div数组中具有特定类名属性的div,把样式渲染上去,不确定到底有几个div有这样的属性,所以浏览器会遍历整个数组,把所有的节点都遍历完,才能结束,否则只能使部分目标div设置好样式。
    用户平时看到的网页有时不是固定的,比如说点击某一个按钮会在下面生成一个新的部分,或者删除一个新的部分,这就涉及到在DOM树上进行增加节点和删除节点的操作了,每个标签都对应于DOM树的一个节点,在进行css样式的渲染时,浏览器会遍历当前html文档生成的树,首先查找到应该删除或者增加元素的位置,然后再进行删除和插入的操作,重构这个DOM树,然后解析出来,这一个过程看上去有好几步,实际上程序运行实在瞬间完成的。
    3.1.2 字符串的相关处理函数用JavaScript处理字符串,与c语言字符串处理不同的是,js处理字符串更加的简单,JavaScript是一种弱类型解释性的脚本,只要声明一个变量,然后把一个字符串的值赋给它就好,想给字符串增加内容只需要在后边直接增加,本系统在运行搜索功能的时候,会把每个条件设置一个具体的语句,然后最终的查询语句是这些语句拼合在一起完成的,如果条件改变,还需要把已经生成的查询语句进行修改,修改由于查询参数变化引起的固定位置的变化,最终的查询语句数据会发给服务端程序,后端程序在进行数据库中的查找,并把结果返回给浏览器,用户就可以看到自己搜索的结果了。
    3.1.3 集合的交并运算接收到前端的查询语句,服务器端程序会执行这个查询语句,每条查询语句会返回一个结果,这个结果是一个集合,多数情况下匹配的结果不会只有一个,部分匹配的记录也会被添加到这个结合中,这个集合被称为结果集,每个条件会返回对应的结果集合,但是用户需要的是满足所有条件的数据,所以对这些结果集要进行交集的运算,最终生成一个返回到浏览器端的结果集—-最终结果集,最终结果集就是用户查找的最终结果,然后前端再把它渲染到界面上。

    3.1.4 索引查找算法因为要存储用户输入的数据,所以,需要有数据库来存用户输入的数据,小组用到的数据库是当下流行的关系型数据库MySQL,MySQL数据库的每一行是一条记录,每一列代表相应的字段,用来存储每个用户的发布的消息。在大多数情况下,不能确定用户输入的数据没有重复的,所以需要给每条消息设置一个唯一的值,这个值只有这条消息是这个值,其他的消息都不同,以此来进行每条消息的区分,只有给每条消息设置一个唯一的标识,在进行数据删除的时候才不会误删其他的消息,这个唯一的标识就叫索引。在用户管理页面的删除功能就是用的这种方法来进行每条消息的删除操作的。通常这个索引(数据库中叫主键)以一个整数来表示,并设置自增的属性。前端用户界面把这个索引发送到后端之后后端程序进行数据库的操作,查找这个索引,以此达到查找到目标数据的目的。用到了一种索引的查找算法。
    3.2 各模块(或算法)流程图(标明函数名)3.2.1 index.php这个网站用PHP语言写的,PHP的语法混合了C、Java、Perl以及PHP创新的语法。PHP具有的优势是能够更快的执行动态网页而且PHP几乎支持所有流行的数据库以及操作系统,这也是本系统使用它的原因。
    失物招领系统是一个在非常具有使用意义的一个系统,在日常生活中几乎每天都能看见朋友圈中找东西的消息。而这个系统看起来简单但是要做到真正实用还需要考虑许多细节。例如用什么关键字去分类和寻找丢失的物品,为了便于搜索,本系统设置了时间、丢失地点的分类,还提供了近几天和范围性地点的模糊搜索。为了准确,首界面的more中可以详细的看到物品的信息。本系统还提供了管理功能让用户方便自由的管理自己发布的信息,并及时做出修改。
    下面简单介绍一下这个网站的结构和各个模块的功能。
    首先进入之后的首界面是由一个名为index.php的文件解析而成的网站。这是整个网站的索引中心,由它指向失物招领系统的其他功能网页。
    初次进入时会检测是否具有网页cookie,如果没有就会进入登陆页面,有的话则会进入首页.
    首页中detail的链接指向由detail.php编写的一个网页,负责输出丢失物品的详细信息,另外还有一个名为个人中心的链接,指向的是一个由people.php解析而成、为用户服务的功能网页。

    3.2.2 people.php下面简单介绍一下个人中心(即people.php解析成网页)。主要功能有三个,分别是找失主,找丢失的物品和管理自己发布的信息。分别对应着三个链接,这三个链接对应的网页分别是由findpeople.php findthing.php和guanli.php写的,并在各个网页实现各自对应功能。

    四、系统实现4.1 index.php用户登陆,招领广场部分代码实现
    <?php require_once("link.php"); if (isset($_POST["user"])&&$_POST["user"]!="") { setcookie("user",$_POST["user"]);}if (isset($_COOKIE["user"])&&$_COOKIE["user"]!="") { print <<<ETO 这里显示的是招领广场的html页面代码ETO; $link=create_connect("findlost"); // $link=mysqli_connect("localhost","root","123456","findlost"); if(!$link) { echo "<script>alert('数据库连接失败');</script>"; } else { $sql="SELECT * FROM findpeople ORDER BY sendtime DESC"; $result=mysqli_query($link,$sql); while ($rows=mysqli_fetch_assoc($result)) { if($rows["class"]==0) { echo :这里写寻物消息 } else { echo "这里写寻失主消息 } } }}else{ print <<<ETO 这里写登陆界面 ETO;}?>
    4.2 people.php这一部分是用户在个人中心的界面。可以进行找东西,找失主消息的发布,也可以进行对自己已经发布消息的一个删除操作。
    <div class="info"> 账号:<?php echo $_COOKIE["user"]; ?> <a href="esc.php">注销</a></div><div class="people" style="margin-top: 100px;"><a href="findthing.php">我丢失了物品 <img src="images/more.png"></a></div><div class="people"><a href="findpeople.php">我拾到了物品 <img src="images/more.png"></a></div><div class="people"><a href="guanli.php">管理我的发布信息 <img src="images/more.png"></a></div>
    4.3 findlost.php发布找东西消息的页面的实现。
    <form name="add" action="addmessage.php" method="post"> <input type="text" name="name" placeholder="输入所失物品名称"><br> <input type="text" name="attribute" placeholder="输入所失物品类别"><br> <input type="text" name="describes" placeholder="输入所失物品描述"><br> <input type="text" name="findaddress" placeholder="输入丢失地点"><br> <input type="text" name="phone" placeholder="输入联系方式"><br> <input type="text" name="photo" placeholder="输入照片"><br> <input type="hidden" name="class" value="1"> <input type="button" value="提交" class="button" onclick="checkdata()"></form>
    4.4 findpeople.php发布寻找失主的消息的界面。
    <form name="add" action="addmessage.php" method="post"> <input type="text" name="name" placeholder="输入所拾物品名称"><br> <input type="text" name="attribute" placeholder="输入所拾物品类别"><br> <input type="text" name="describes" placeholder="输入所拾物品描述"><br> <input type="text" name="findaddress" placeholder="输入捡到地点"><br> <input type="text" name="phone" placeholder="输入联系方式"><br> <input type="text" name="photo" placeholder="输入照片"><br> <input type="hidden" name="class" value="0"> <input type="button" value="提交" class="button" onclick="checkdata()"></form>
    4.5 detaill.php对每一条消息详细信息展示的界面(首页点击more进入)
    $sql="SELECT * FROM `findpeople` WHERE id=$id"; $result=mysqli_query($link,$sql); if(!$result) { echo "没有查到相关内容"; } else { while($rows=mysqli_fetch_assoc($result)) { echo "<div class='container detail'> <div class='row clearfix'> <div class='col-xs-12 column'> <img src='images/user.png'> <div class='text'> <p class='iteam'><span>名称: </span>".$rows['name']."</p> <p class='iteam'><span>类别: </span>".$rows['attribute']."</p> <p class='iteam'><span>描述: </span>".$rows['describes']."</p> <p class='iteam'><span>地址: </span>".$rows['findaddress']."</p> <p class='iteam'><span>时间: </span>".$rows['sendtime']."</p> <p class='iteam'><span>电话: </span>".$rows['phone']."</p>"; if ($rows['class']==0) { echo "<p class='iteam'><span>拾到人:</span>".$rows['finder']."</p>"; } else { echo "<p class='iteam'><span>寻物人:</span>".$rows['finder']."</p>"; } echo " </div> </div> </div> </div>"; } }
    4.6 esc.php这是一个后端程序,用于消除cookie,即注销操作。
    <?php setcookie("user","");echo "<script>window.location.href='index.php'</script>";?>
    4.7 guanli.php对自己发布的消息进行管理(浏览和删除)
    $user=$_COOKIE['user']; $sql="SELECT * FROM findpeople WHERE finder = $user ORDER BY sendtime DESC"; $result=mysqli_query($link,$sql); $i=0; if (mysqli_num_rows($result)==0) { echo "空空如也"; } while ($rows=mysqli_fetch_assoc($result)) { 这里显示数据 }
    4.8 join.php注册界面,没账号,先注册。
    <?php require_once("link.php");if (isset($_POST["user"])){ // $link=mysqli_connect("localhost","root","123456","findlost"); $link=create_connect("findlost"); if(!$link) echo "<script>alert('连接失败');history.back();</script>"; else { $user=$_POST["user"]; $sql="SELECT * FROM `user` WHERE account='$user'"; $result=mysqli_query($link,$sql); if (mysqli_num_rows($result)) { echo "<script>alert('该用户已经存在');history.back();</script>"; } else { $password=$_POST["password"]; $sql2="INSERT INTO user (`account`,`password`) VALUES ('$user','$password')"; $res=mysqli_query($link,$sql2); if(!$res) { echo "<script>alert('注册失败');history.back();</script>"; } else { setcookie("user",$user); echo "<script>alert('注册成功');window.location.href='index.php';</script>"; } } }}
    4.9 addmessage.php发布消息的后端程序,用于把用户的数据写入到数据库,并跳转到消息界面。
    $name=$_POST["name"];$attribute=$_POST["attribute"];$describes=$_POST["describes"];$findaddress=$_POST["findaddress"];$phone=$_POST["phone"];$class=$_POST["class"];$finder=$_COOKIE["user"];$photo="user.png";$sendtime=date("Y-m-d H:i:s");$sql="INSERT INTO `findpeople`(`name`, `attribute`, `describes`, `findaddress`, `sendtime`, `phone`, `finder`, `photo`, `class`) VALUES ('".$name."','".$attribute."','".$describes."','".$findaddress."','".$sendtime."','".$phone."','".$finder."','".$photo."','".$class."')";
    五、程序的过程展示5.1 程序运行过程展示程序登陆界面

    程序主界面(广场界面)

    个人中心界面

    发布“找失物“消息界面:

    发布“找失主“消息界面:

    消息详细信息界面:

    消息分类选择界面:

    时间跨度选择

    拾失地点选择
    8 评论 109 下载 2018-11-09 14:21:08 下载需要13点积分
  • 基于JAVA的文件系统

    一、项目需求在内存中开辟一个空间作为文件存储器,在其上实现一个简单的文件系统。退出这个文件系统时,需要该文件系统的内容保存到磁盘上,以便下次可以将其恢复到内存中来。
    二、具体技术细节
    文件存储空间管理可采取显式链接(如FAT)或者其他方法。(即自选一种方法)
    空闲空间管理可采用位图或者其他方法。如果采用了位图,可将位图和FAT表合二为一
    文件目录采用多级目录结构。至于是否采用索引节点结构,自选。目录项目中应包含:文件名、物理地址、长度等信息。同学可在这里增加一些其他信息

    三、开发工具
    环境: intellij
    语言: Java

    四、文件系统管理方案4.1 存储空间概述
    盘区数量:10
    盘区大小:1024KB

    4.2 存储空间管理方式
    FAT:在本次项目中,我的文件系统管理方案采用了显式链接的方式,在每一个盘区都开了一个文件用来存储当前盘区所有存储块的使用情况。并记录盘区内所有文件所占用内存块的情况
    优点:不存在内外内存碎片,最大程度地利用了盘区空间

    4.3 空闲空间管理方式
    位图:在本次项目中,我的文件系统空闲空间管理方案采用了位图。使用一串0和1来标识内存块的状态。0为未使用,1表示已经被占用。创建文件时从最前方开始检索空闲块,确定使用后将0置为1
    优点:一目了然,易于管理

    4.4 文件目录结构
    多级目录结构
    4.5 FCB4.5.1 概述该文件系统的FCB将直接显示在生成的txt文本中,以文本头的形势呈现。需要编辑内容可以将内容写在FCB下方的划分线内。
    4.5.2 内容
    大小
    文件名
    地址
    最近更新时间

    五、程序操作指南5.1 特殊文件说明5.1.1 BitMap&&Fat.txt
    该文件存储了盘区的存储块的状态,用0表示还未分配,1表示已经分配
    该文件存储了盘区内存在的文件按照链接方式实现的的内存分配情况,列出了对应内存块的位置

    5.1.2 recover.txt该文件是当前盘区的备份文档,在再次启动该应用程序的时候通过读取该文件的信息还原出上次的运行状态,从而实现复盘
    5.2 程序概述5.2.1 界面构成
    搜索框
    文件树
    显示框
    底部盘信息栏

    5.2.1.1 搜索框搜索框位于顶部可以在搜索栏中键入文件名或者文件夹名然后点击“start”来进行检索,如果检索成功将直接打开,失败则弹框提醒
    5.2.1.2 算法采用dfs深度搜索的方式
    // Search a file public boolean searchFile(String fileName, File parent){ File [] files = parent.listFiles(); for (File myFile:files){ if (myFile.getName().equals(fileName)){ try { if(Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); desktop.open(myFile); return true; } } catch (IOException e1) { JOptionPane.showMessageDialog(null, myFile.getPath() + " Sorry, some thing wrong!", "Fail to open", JOptionPane.ERROR_MESSAGE); return true; } } if (myFile.isDirectory() && myFile.canRead()){ if(searchFile(fileName, myFile)){ return true; } } } return false; }
    5.2.1.3 文件树
    文件树位于左侧,呈现文件的目录结构,详情见目录结构
    5.2.1.4 显示框
    显示框位于程序右侧,用于显示文件的名称、路径、类型、大小和最近修改时间
    5.2.1.5 底部盘信息栏
    底部盘信息栏位于程序的底部,用于显示当前选中盘区的名称、已使用空间、未使用空间、文件数量
    5.2.2 菜单项
    create a file(生成一个文件)
    create a dir(生成一个文件夹)
    delete(删除一个文件或文件夹)
    format(格式化)
    rename(重命名)

    5.3 注意事项
    必须先选择一个系统内的文件夹作为模拟工作目录
    文件操作需要先选中一个文件树中的节点右键才能进行操作
    本程序重在模拟,并不是真正地为文件开了这么大的空间
    仅支持生成txt文件,在输入文件名时不需要输入.txt
    相同目录下已经存在该文件重命名将失败
    文本中将自动生成FCB,不支持用户去修改FCB
    非法输入都将导致文件生成失败
    如果存档文件recover.txt被破坏,将无法在再次打开该程序时恢复盘区信息

    六、文件系统实现功能6.1 文件目录文件目录将呈现在程序的左侧,以十个以数字命名的盘为基目录。
    点击文件夹左侧箭头可以将文件夹展开
    当执行创建和删除文件等操作,必须重新展开一次父目录才能刷新

    6.2 创建文件6.2.1 概述选中某一文件后,右键展开功能选项,选择第一项生成文件或第二项就会弹出对话框要求键入文件名和文件大小。文件生成成功之后只要重新展开父目录就可以看到新生的文件。双击程序右侧的显示区的文件就可以打开该文件看到它的FCB
    6.2.2 实现方法通过调用File类的createFile()方法来实现,文件创建成功之后将刷新FAT和位图,如果存储空间不足或是输入非法将导致文件生成失败
    // create a file public boolean createFile(String filePath){ boolean result = false; File file = new File(filePath); if(!file.exists()){ try { result = file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } return result; }
    当执行创建文件操作,必须重新展开一次父目录才能刷新

    6.3 删除文件6.3.1 概述选中某一文件后,右键展开功能选项,选择第三项删除即可删除所选项
    6.3.2 实现方法通过调用File类的delete()删除 针对文件夹需要递归删除,文件删除成功之后将刷新FAT和位图
    // delete a file public boolean deleteFile(String filePath){ boolean result = false; File file = new File(filePath); if(file.exists() && file.isFile()){ result = file.delete(); } return result; }
    当执行删除文件操作,必须重新展开一次父目录才能刷新

    6.4 重命名(更改当前目录)6.4.1 概述选中某一文件后,右键展开功能选项,选择第四项重命名即可对文件或文件夹进行重命名
    6.4.2 实现方法通过调用File类的renameTo()方法进行重命名,如果相同目录下有相同的文件,则重命名将失败
    当执行重命名文件操作,必须重新展开一次父目录才能刷新

    6.5 打开文件双击程序中右侧的显示面板中的文件即可打开对应文件
    6.6 关闭文件打开文件之后点击文件上方的关闭按钮就可以将对应文件关闭
    6.7 写文件打开文件之后在FCB的内容下方输入文本即可
    6.8 读文件双击程序右侧的显示面板中的文件即可打开对应文件进行查看
    6.9 格式化6.9.1 概述选中某一文件夹之后右键选择第四项格式化就可以将该文件夹格式化
    6.9.2 实现方法即递归删除该目录下的所有文件并更新FAT和位图
    七、 数据结构盘区(Block)
    public class Block { // 名称 private int blockName; // 盘区内的文件 private File blockFile; // 位图存储文件 private File blockBitMap; // 恢复文件 private File recover; // 位图存储文件书写器 private FileWriter bitWriter; // 恢复文件书写器 private FileWriter recoverWriter; // 文件数量 private int fileNum; // 盘区已占用空间 private double space; // 内存块数组 public int [][] bitmap = new int[32][32]; // 文件和内存块对应表 private Map<String, int[][] > filesBit = new HashMap<String, int[][]>(); // 文件列表 private ArrayList<File> files = new ArrayList<File>(); public Block(int name, File file, boolean rec) throws IOException { // 初始化 } public File getBlockFile(){ return blockFile; } public void putFCB(File file, double capacity) throws IOException { // 将FCB写入文件 } public void rewriteBitMap() throws IOException { // 重写位图存储文件 } public void rewriteRecoverWriter() throws IOException{ // 重写恢复文件 } public boolean createFile(File file, double capacity) throws IOException { // 在盘区内创建文件 } public boolean deleteFile(File file, double capacity){ // 在盘区内删除文件 } public boolean renameFile(File file, String name, double capacity) throws IOException { // 重命名盘区内的文件 } public int getFileNum() { return fileNum; } public double getSpace() { return space; }}
    3 评论 228 下载 2018-11-03 00:44:21 下载需要13点积分
  • 基于JAVA和SQL SERVER数据库实现的火车票预售系统

    1 系统设计1.1 设计目的乘坐火车是我们生活中几乎不可缺少的一件事儿,每天都会有各种各样的火车班次发布与被预定。针对这个火车票预售的环节我设计了一个火车票预售系统,为购票用户与卖票管理人员之间搭建平台。让我们的用户能够通过该软件对管理人员发布的航班进行预购与查询。另一方面也可以加强我们的管理人员对班次信息与乘客的管理与查询。
    本系统的根本目的是让管理人员能够发布与查询班次信息、查询乘客信息等;用户可以通过该系统对班次进行预购、自己购票记录的查询等。
    1.2 需求分析1.2.1 信息要求该系统主要记录用户、班次、火车、银行卡之间的关系

    用户分为管理员与购票用户

    售票管理员信息:管理员编号、管理员名字、管理员电话购票用户信息:身份证号、电话号码、银行卡号
    班次信息

    班次号、火车号、出发地点、目的地、出发时间、到达时间
    火车信息

    火车号、火车节数、座位数、各种座位票价、火车车速
    银行卡信息:

    银行卡号、余额、持有人身份证号
    身份证信息

    身份证号、姓名、性别、所有者
    车票信息

    车票号、班次号、座位号、乘客身份证号、车票价钱、车厢数

    1.2.2 处理要求
    能够正确、高效、迅速地完成所有操作
    一个管理员可以管理多个班次、一个用户可以多次订购不同时间段的车票

    1.2.3 安全性与完整性需求
    安全性

    该系统需要用户进行账号的注册与登陆通过对不同的用户种类的检测来给予不同的权限与界面用户登陆自己账号后只能查询自己用户名下身份证的购票信息与个人信息用户不可对班次、火车等信息进行修改售票员能对班次信息进行修改与查询,对于用户信息只能查询不能修改
    完整性

    实体完整性

    手机号、班次号、火车号、银行卡号、身份证号、车票号分别为用户、班次、火车、银行卡、身份证、车票的主码
    参照完整性

    班次号中的火车号为火车表的主码、银行卡号中所有者号码为用户的主码、车票中的火车号与班次号分别为火车表与班次表的主码。以上外码要么为空要么是参照表中已有数据

    用户定义完整性

    性别只能是男女、车票钱不能为空、车速不能为空、班次目的地与出发地不能为空、用户类型只能是 0 与 1:0 表示普通用户、1 表示管理员用户。用户名字不能为空

    1.3 开发和运行环境选择
    开发工具

    前台开发语言为 Java后台数据库为 SQL SERVER 2017
    运行环境

    Java 1.8 版本 Windows2000 以上

    2 数据库设计2.1 数据库概念设计本系统中包括六个实体:身份证实体、银行卡实体、用户实体、车票实体、火车实体、班次实体,根据需求需求分析的结果以上六个实体对应的 ER 属性图如下图所示。

    系统整体 E-R 图

    2.2 数据库逻辑设计根据 E-R 图向关系模型的转换原则,一个实体型转换为一个关系模式,实体的属性就是关系的属性。因此按照图 2.7 中的整体 E-R 图,本数据库系统中应包括六张表:身份证、银卡、用户、车票、火车、和班次。






    3 火车票预售系统详细设计3.1 功能概述售票管理员系统主要包含如下几个功能:

    发布与删除班次及其相关信息
    查看班次详细信息
    通过班次号对班次进行详细查询
    通过身份证号码查询乘车人信息

    用户系统主要包含如下几个功能:

    通过地点、时间查询班次信息
    通过班次号查询班次详细信息
    添加和删除信用卡
    添加、修改和删除乘车人信息
    查询得到班次后可以为乘车人购买车票
    查看自己用户的购票记录

    整体功能描述图如下图所示:

    3.2 火车票预售系统详细设计3.2.1 界面设计3.2.1.1 管理员界面设计在使用管理员账号登录过后,会出现管理员主界面。在界面最上方有初始地点选择和时间选择控件,可以通过时间和地点的详细选择来针对性地发布班次信息,在点击发布后会弹出火车号填写对话框,在此处填写火车号以完成班次发布的目的,如下图在发布下方有一个查看按钮,通过在左边列表中选择想查看的班次再点击该按钮即可查看到详细的班次信息如下图所示。


    3.2.1.2 用户系统界面设计在使用用户权限帐号登录过后,会进入相应的用户界面,用户界面分为四个 fragment,第一个 fragment 通过站点和时间来查询班次列表,在查询到班次后可以通过购买按钮对车票进行购买,购买时需要选择乘车人、勾选座位信息和选择支付的信用卡,如下图所示。


    在第二个 fragment 里用户可以通过班次号来查询班次信息与购票,如下图所示,在点击查询后按钮会自动变成购票,后续购票操作和第一个 fragment 一样。


    在第三个 fragment 里用户可以查询到自己所有的购票记录,可以通过查看详细按钮进行查看,还可以通过退票按钮进行退票操作,如下图所示。


    最后是用户信息界面,用户可以在此处看到自己用户名以及邮箱号,如下图所示。用户可以在此界面增、删、改常用乘车人信息,如下图所示。也可以添加和删除信用卡,如下图所示。




    3.2.2 功能实现整体功能是通过两个 Activity 与四个 Fragment 来实现,具体实现功能顺序为下图所示。本系统大量使用 Dialog 来对详细信息填写进行管理。
    功能实现过程中使用到 UI 的类有 17 个,其中三个抽象父类用于限制所有界面的业务处理与逻辑执行顺序,其余 14 个子类用于详细界面以及功能的实现,整体界面代码框架如下图所示。
    注:Dialog 是在 Fragment 中使用,Fragment 是放置到底层 Activity 使用。

    4 系统测试发布班次示意图

    发布班次示意图

    到数据库查询如下图所示:

    购票操作示意图 1

    购票操作示意图 2

    到数据库查询如下图所示:

    退票操作示意图

    到数据库查询如下图所示:

    5 总结三个星期的时间非常快就过去了,这三个星期不敢说自己有多大的进步,获得了多少知识,但起码是了解了项目开发的部分过程。虽说上过数据库等相关的课程,但是没有亲身经历过相关的设计工作细节。这次课设证实提供了一个很好的机会。通过这次的系统设计,我在很多方面都有所提高。综合运用所学知识的理论知识实际训练从而培养和提高学生独立工作的能力,巩固所学的知识,掌握系统程序的编排和运行,使自己的独立思考能力有了显著提高。
    从各种文档的阅读到开始的需求分析、概念结构设计、逻辑结构设计、物理结构设计。亲身体验了一回系统的设计开发过程。很多东西书上写的很清楚,貌似看着也很简单,思路非常清晰。但真正需要自己想办法去设计一个系统的时候才发现其中的难度。经常做到后面突然就发现自己一开始的设计有问题,然后又回去翻工,在各种反复中不断完善自己的想法。
    我们学习并应用了SQL 语言,对数据库的创建、修改、删除方法有了一定的了解,通过导入表和删除表、更改表学会了对于表的一些操作,为了建立一个关系数据库信息管理系统,必须得经过系统调研、需求分析、概念设计、逻辑设计、物理设计、系统调试、维护以及系统评价的一般过程,为毕业设计打下基础。
    很多事情不是想象中的那么简单的,它涉及到的各种实体、属性、数据流程、数据处理等等。很多时候感觉后面的设计根本无法继续,感觉像是被前面做的各种图限制了。在做关系模型转换的时候碰到有些实体即可以认为是实体又可以作为属性,为了避免冗余,尽量按照属性处理了。
    不管做什么,我们都要相信自己,不能畏惧,不能怕遇到困难,什么都需要去尝试,有些你开始认为很难的事在你尝试之后你可能会发现原来并没有你以前觉得的那样,自己也是可以的。如果没有自信,没有目标,没有信心就不可能把事情做好,当其他人都在迷茫的时候,自己一定要坚信目标。有一个这样的感觉就是课程设计学到的东西比一个学期都多。
    参考文献[1] CSDN 论坛、简书论坛、知乎论坛
    [2] 毕广吉.Java 程序设计实例教程[M]. 北京:冶金工业出版社,2007 年
    [3] 肖成金,吕冬梅。 Java 程序开发数据库与框架应用[J]. 科技展望,2017,05:19.
    [4] 吕锋,梅细燕,周晓东;基于 JDBC 的数据库管理及其应用[J];武汉理工大学学报;2002 年 10 期
    [5] 姚永一,SQL Server 数据库实用教程,北京:电子工业出版社,2010.
    [6] 高云,崔艳春,SQL Server 2008 数据库技术实用教程,北京:清华大学出版社,2011
    [7] 何玉洁,梁琦,数据库原理与应用(第二版),北京:机械工业出版社,2011
    [8] 壮志剑,数据库原理与 SQL Server,北京:高等教育出版社,2008
    [9] 杜丁超.计算机软件 Java 编程特点及其技术分析
    [10] 萧仁惠,陈锦辉. JDBC 数据库程序设计[J].北京:中国铁道出版社,2004
    9 评论 269 下载 2018-11-20 18:23:37 下载需要17点积分
  • 基于Java Web的投票管理系统

    摘 要相较于传统投票方式,在线投票是任何一-次商演(时间短、影响范围小)、电视(投入大,效果不明显)或者户外广告(不能快速吸引消费者眼球)在投入产出效率上都难以企及的。在线投票能够迅速提升活动商家的广告价值,降低商家广告成本,提高知名度以及美誉度。网络投票的主要目的就是人群放大:通过网络投票,可以吸引大量的选手及相关亲友的关注,通过以往的数据统计,一名选手参 与投票,会带动60人参与。这样可以最大程度的向客户展现实力,扩大社会影响力。
    系统分为前台和后台两部分,前台主要是对普通用户开放。普通用户将实现匿名投票操作:后台主要作用是管理员实现登录、投票管理、用户管理等操作。依据前台和后台的不同权限需求,在线投票系统主要分为普通用户和管理员两个功能模块。
    普通用户模块将实现用户登录、用户投票以及查看投票结果等功能。管理员模块将实现管理员登录、发布新投票、查看所有投票、及用户管理等功能。
    关键词:网上投票、JSP、MYSQL
    1 概述随着科技的飞速发展,计算机已经广泛的应用于各个领域之中,而且日趋普及。在计算机应用中很重要的一部分就是编程语言,编程语言的出现打开了计算机应用的新篇章。在这些编程语言中JSP占有着重要的地位,JSP 拥有Java编程语言“一-次编写,各处运行”的特点口。在当今的网络应用中JSP无处不在,在线投票系统就是JSP功能强大的-一个最好的例证。
    在线投票系统有很多传统投票方式不可比拟的优势。首先,它可以在很短的时间内,通过互联网将不同时间不同地域的用户投票组合到一起,并进行结果反馈,这是传统的投票方法完全做不到的;其次,在线投票系统的效率要比以往的传统投票方法高很多,在这个寸时如寸金的年代,在线投票系统不但省去了投票与计票环节的人力成本,还可以节省很多时间以用作处理其他更重要的事情:综上所述,在线投票系统有着快捷、经济、方便、省时省力等诸多优点都注定它将是未来投票方式的主流。
    2 课程设计任务及要求2.1 任务设计一个投票系统,实现学生对教师的匿名投票。有管理用户,实现对整个系统的管理。
    2.2 要求用户分为管理员用户和普通用户。
    普通用户提供以下功能:系统首页上显示所有投票选项的列表,显示当前登录用户并提供注销功能,用户可以通过选中某个选项并点击“投票”按钮进行投票操作。用户点击后可在当前一页面上看到各个投票选项目前的投票情况,主要包括各选项所得的票数,并且以柱形显示各选项的得票率,可以让用户从直观上看到各项的得票情况。
    管理员用户提供以下功能:显示当前登录用户并提供注销功能,提供系统管理实现用户的增删查改及重新投票等功能,还能显示用户列表及投票情况表。开发的系统数据库设计要合理,能够实现设计的全部功能,能够运行演示。能够体一个软件的基本功能。
    3 需求分析3.1 功能需求用户能够正常的登录及注销,每个用户在每一次投票中只能匿名投票一次。管理员能够新建账号、删除账号、修改账号及初始话投票。在管理员页面显示投票情况及用户情况等系统详情。
    整个系统要进行session检查,防止非法用户访问受限页面。
    3.2 硬件需求运行windows的硬件平台并装有如下软件:

    Jdk1.8
    tomcat6.0
    eclipse
    sql server 2008

    4 概要设计4.1 总体模块设计4.1.1 普通用户模块
    登录:使用普通用户账号登录到投票界面
    投票:选择投票对象并进行投票
    注销:清空用户session退回到欢迎界面

    其各部分关系图如图1所示。

    4.1.2 管理员用户模块
    登录:使用管理员账号登录到后台管理界面
    注销:清空用户session退回到欢迎界面
    用户管理:管理员可以增加、删除、修改用户账号,不能删除当前登录用户
    初始化投票:管理员可以初始化投票系统,开启下一次投票
    查看所有用户:显示系统中已存在的用户账号及其投票情况
    查看得票数:显示当前的投票情况

    其各部分关系如图2所示。

    4.2 开发工具简介在在线投票系统的设计发开过程中主要用了JSP技术,其中用到的工具有:JDK1.8、Eclipse、MySQL、Tomcat 等。
    4.2.1 JSP简介JSP技术有点类似ASP技术,它是在传统的网页HTML文件中插入Java程序段和JSP,从而形成JSP文件”。JSP技术使用Java编程语言编写类XML的tags和scriptlets,来封装产生动态网页的处理逻辑。在线投票系统用JSP将网页逻辑与网页设计和显示分离,支持可重用的基于组件的设计,使本系统基于Web的应用程序的开发变得迅速和容易。

    一次编写,到处运行。在这一一点上Java比PHP更出色,除了系统之外,代码不用做任何更改
    系统的多平台支持。基本上可以在所有平台上的任意环境中开发,在任意环境中进行系统部署,在任意环境中扩展。相比ASP/PHP的局限性是现而易见的
    强大的可伸缩性。从只有一个小的Jar文件就可以运行Servlet/JSP,到由多台服务器进行集群和负载均衡,到多台Application进行事务处理,消处理,-台服务器到无数台服务器,Java 显示了一个巨大的生命力
    多样化和功能强大的开发工具支持。这一点与ASP很像,Java已经有了许多非常优秀的开发工具,而且许多可以免费得到,并且其中许多已经可以顺利的运行于多种平台之下

    4.2.2 SQL server简介SQL Server系列软件是Microsoft 公司推出的关系型数据库管理系统。2008年10月,SQL Server 2008简体中文版在中国正式上市,SQL Server 2008 版本可以将结构化、半结构化和非结构化文档的数据直接存储到数据库中。可以对数据进行查询、搜索、同步、报告和分析之类的操作。数据可以存储在各种设备上,从数据中心最大的服务器一直到桌面计算机和移动设备,它都可以控制数据而不用管数据存储在哪里。
    此外,SQL Server 2008 允许使用 Microsoft .NET 和Visual Studio开发的自定义应用程序中使用数据,在面向服务的架构(SOA)和通过 Microsoft BizTalk Server 进行的业务流程中使用数据。信息工作人员可以通过日常使用的工具直接访问数据。
    4.3 数据库设计数据库设计是软件开发中的重要环节,是对系统数据全面的、详细的分析。数据库设计的好坏直接关系到整个项目设计的效率的高低,关系到设计的稳定性。根据在线投票系统不同模块之间的联系和前期的设计目的与需求,设计了三个不同的数据表,它们分别是用户表、投票选项表和投票主题表。
    4.3.1 用户表(T_USER)用户表包括所有用户的各种信息,如账户、密码等详细数据并规定了每个字段的类型,如表1所示。



    字段名
    数据类型
    长度
    是否主键
    描述




    ACCOUNT
    VARCHAR
    40

    账号


    PASSWARD
    VARCHAR
    40

    密码


    UNAME
    VARCHAR
    40

    用户姓名


    ADMIN
    BIT
    1

    管理员标识


    TICKET
    INT
    255

    票数



    4.3.2 投票表(T_VOTE)投票表包括教师编号、教师姓名和得票数。如表2所示。



    字段名
    数据类型
    长度
    是否主键
    描述




    TEACHERNO
    VARCHER
    40

    教师编号


    TEACHERNAME
    VARCHER
    40

    教师姓名


    VOTE
    INT
    255

    得票数



    5 开发与实现5.1 程序模块实现5.1.1 用户登录模块关键代码<% request.setCharacterEncoding("gb2312"); String account = request.getParameter("account"); String password = request.getParameter("password"); UserDao udao = new UserDao(); User user = udao.getUserByAccount(account); if(user == null||!user.getPassword().equals(password)){ %> <br><br><br><br><br><br> 登录失败,<a href="loginForm.jsp">返回登录页面</a> <% } else if(user.getAdmin()==true){ session.setAttribute("user", user); response.sendRedirect("admin.jsp"); } else{ session.setAttribute("user", user); response.sendRedirect("display.jsp"); }%>
    5.1.2 投票模块关键代码<% String account=user.getAccount(); String[]teacherno =request.getParameterValues("teacherno"); VoteDao vdao = new VoteDao(); UserDao udao = new UserDao(); int ticket= udao.ticket(account); if(ticket>0){ if(teacherno!=null){ vdao.updateVotes(teacherno); vdao.updateTictet(account); } response.sendRedirect("display.jsp"); } else{ response.sendRedirect("noTicket.jsp"); } %>
    5.1.3 新建用户模块关键代码<% request.setCharacterEncoding("gb2312"); String account = request.getParameter("account"); String password = request.getParameter("password"); String uname = request.getParameter("uname"); String admin =request.getParameter("admin"); UserDao udao = new UserDao(); User user2=udao.getUserByAccount(account); if(user2!=null){ %> <jsp:forward page="changeError2.jsp"></jsp:forward> <% } udao.adduser(account, password, uname, admin); %> <br><br><br><br><br><br> <p align="center">创建用户成功!<br> <a href="admin.jsp">返回</a></p>
    5.1.4 删除用户模块关键代码<% request.setCharacterEncoding("gb2312"); String account = request.getParameter("account"); String acc = user.getAccount(); if(acc.equals(account)){ %> <jsp:forward page="changeError3.jsp"></jsp:forward> <% } UserDao udao = new UserDao(); User user2=udao.getUserByAccount(account); if(user2==null){ %> <jsp:forward page="changeError.jsp"></jsp:forward> <% } udao.deluser(account); %> <br><br><br><br><br><br> <p align="center">删除用户成功!<br> <a href="admin.jsp">返回</a></p>
    5.1.5 修改用户模块关键代码<% request.setCharacterEncoding("gb2312"); String account = request.getParameter("account"); String password = request.getParameter("password"); String uname = request.getParameter("uname"); String admin =request.getParameter("admin"); UserDao udao = new UserDao(); User user2=udao.getUserByAccount(account); if(user2==null){ %> <jsp:forward page="changeError.jsp"></jsp:forward> <% } udao.changeuser(account, password, uname, admin); %> <br><br><br><br><br><br> <p align="center">修改用户成功!<br> <a href="admin.jsp">返回管理页面</a></p>
    5.1.6 初始化投票模块关键代码public void initVote() throws Exception{ initConnection(); String sql="UPDATE T_VOTE SET VOTE = 0"; PreparedStatement ps = conn.prepareStatement(sql); ps.executeUpdate(); closeConnection();}public void initTicket() throws Exception{ initConnection(); String sql="UPDATE T_USER SET TICKET = 1"; PreparedStatement ps = conn.prepareStatement(sql); ps.executeUpdate(); closeConnection();}
    5.1.7 注销登录模块关键代码<% session.invalidate(); response.sendRedirect("loginForm.jsp");%>
    5.1.8 非法用户的session检查管理员权限
    <% User user =(User)session.getAttribute("user");if(session.getAttribute("user")==null||user.getAdmin()==false { %> <jsp:forward page="loginError.jsp"></jsp:forward> <% } %>
    普通用户权限
    <%5.1.8非法用户的session检查管理员权限: User user =(User)session.getAttribute("user"); if(user==null){ %> <jsp:forward page="loginError.jsp"></jsp:forward> <% } %>
    5.1.9 防刷票的实现public int ticket(String account) throws Exception{ initConnection(); String sql ="SELECT TICKET FROM T_USER WHERE ACCOUNT= ? "; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, account); ResultSet rs = ps.executeQuery(); rs.next(); int ticket =rs.getInt("TICKET"); if(ticket==0) { closeConnection(); return 0; } else { closeConnection(); return 1; } }
    5.2 数据库与操作设计开发5.2.1 连接数据库public void initConnection() throws Exception { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");conn=DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=Vote;user=root;password=123456789"); }
    5.2.2 数据库操作设计public User getUserByAccount(String account) throws Exception{ User use = null; initConnection(); String sql="SELECT ACCOUNT,PASSWORD,UNAME,ADMIN FROM T_USER WHERE ACCOUNT = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, account); ResultSet rs =ps.executeQuery(); if(rs.next()) { use = new User(); use.setAccount(rs.getString("ACCOUNT")); use.setPassword(rs.getString("PASSWORD")); use.setUname(rs.getString("UNAME")); use.setAdmin(rs.getBoolean("ADMIN")); } closeConnection(); return use; }
    6 测试与部署6.1 运行环境
    平台:Windows10
    开发环境:JDK 1.8、 Eclipse4.9、 Tomcat6.0、 SQL server 2008

    6.2 功能测试6.2.1 防止非法访问功能
    功能描述:未登录的用户不能访问受限页面,已登录的普通用户不能访问管理员权限页面
    目的:防止未授权访问,保证安全性
    前提条件:无
    输入:浏览器地址栏直接输入页面地址
    输出:身份验证通过,转到要访问页面;身份验证不通过,转到拒绝访问页面
    是否实现:实现

    6.2.2 初始化投票功能
    功能描述:管理员清空当前投票
    目的:初始化投票者的票数及被投票者的得票数
    前提条件:登录到管理员页面
    输入:点击初始化投票链接
    输出:初始化成功转到管理页面
    是否实现:实现

    6.2.3 登录功能
    功能描述:输入用户名和密码登陆到系统,不同用户登录到不同界面
    目的:检验数据判断能否登陆
    前提条件:进入登陆界面
    输入:用户名和密码
    输出:数据判断成功,普通用户登陆到投票界面,管理员用户登录到后
    一台管理界面:数据判断失败,转到失败页面
    是否实现:实现

    6.2.4 注销登录功能
    功能描述:已登录到系统的用户可以通过注销来退出系统
    目的:用户退出时,清空用户session
    前提条件:用户已登录
    输入:点击注销链接
    输出:注销成功转到初始欢迎页面
    是否实现:实现

    6.2.5 投票功能
    功能描述:给相应的所选教师投票
    目的:实现数据库中相应教师得票数的数据更新
    前提条件:成功登录到投票界面
    输入:勾选教师,点击投票
    输出:相应老师的得票数加1
    是否实现:实现

    6.2.6 添加用户功能
    功能描述:管理员添加用户账号到系统中
    目的:在数据库用户表中添加用户账号信息
    前提条件:成功登录后台管理页面
    输入:新的用户数据
    输出:创建用户成功,输出成功提示转到管理界面;创建用户失败,输出相应失败提示转到管理界面
    是否实现:实现

    6.2.7 删除用户功能
    功能描述:管理员从系统中删除用户账号
    目的:在数据库用户表中删除用户账号信息
    前提条件:成功登录到后台管理页面
    输入:要删除用户的账号
    输出:删除用户成功,输出成功提示转到管理界面;删除用户失败,输出相应提示转到管理界面
    是否实现:实现

    6.2.8 修改用户功能
    功能描述:管理员修改系统中已存在的用户信息
    目的:在数据库中修改相应的用户信息
    前提条件:登录到后台管理页面
    输入:要修改的账号及新的用户信息
    输出:修改成功,输出成功提示转到管理界面;修改失败输出相应失败提示转到管理界面
    是否实现:实现-

    6.2.9 显示系统用户功能
    功能描述:在管理页面显示当前系统中已存在的账号及是信息
    目的:显示账号,账号用户及是否投票
    前提条件:登录到后台管理页面
    输入:无
    输出:系统中的账号信息
    是否实现:实现

    6.2.10 显示得票数功能
    功能描述:在管理页面及投票页面显示当前的得票情况
    目的:显示各教师的得票数,并以相应比例长的的矩形显示
    前提条件:登录到系统中
    输入:无
    输出:各教师的得票情况
    是否实现:实现

    7 结论分析为期数天的jsp集中上机课程设计让我受益匪浅。和上学期数据库课程设计不同,这次要求我们每个人都要做,主要是考查我们jsp 动手实践能力。这次的课程设计让我再一次翻起了sql课本,已经有好些知识忘了,借此机会刚好复习了一遍,由于这次时间安排的比较紧,赶在了我们期末考试这一特殊时期,所以我选择做个功能齐全的投票系统,其实也算不上齐全,但是也学了不少东西。特别是在具体功能的设计上遇到很多问题,但是经过我上网搜索资料以及寻找同学帮助,这些问题都迎刃而解了。但是不足的时还有很多想做的功能由于时间的关系没有实现,例如动态管理投票对象、更改用户可投票数、非匿名投票等等。
    经过了数天的学习研究,终于完成了该课程设计的架构到实现。从开始选定题到系统的规划实现,再到报告的完成,这次的课程设计是对于我们是一个很好的锻炼,通过本次设计,我基本明确了一个基于jsp应用系统从设计到开发的大致流程和重点关注的方向,同时对JSP, SQL语句数据库关系等都有了基本的掌握和应用能力。
    通过这次课程设计,我再一次感受到jsp的魅力,自己设计的不是特别完美,但毕竟做成果,心里很高兴,我们对学习jsp的信心也增强不少。
    参考文献[1] 郭克华、奎晓燕. Java Web程序设计 清华大学出版社2011
    [2] 明日科技 HTML5从入门到精通 清华大学出版社2012
    [3] 何玉洁 数据库基础与实践技术(SQL Server 2008) 机械工业出版社2018
    1 评论 50 下载 2019-07-26 17:48:21 下载需要15点积分
  • 基于PHP的网上商城

    第一章 需求分析1.1 引言伴随着Internet的蓬勃发展,网络购物中心作为电子商务的一种形式正以其高 效、低成本的优势,逐步成为新兴的经营模式和理念,人们已不再满足于信息浏览 和发布,而是渴望着能够充分享受网络所带来的更多的便利。的确,客户足不出户 便可以方便快捷的选购自己喜欢的商品,这正是网络购物中心为客户带来的好处。 网络商城将传统的商务流程电子化、数字化,一方面以电子流代替了实物流,可以大量减少人力、物力,降低了成本;另一方面突破了时间和空间的限制,使得 交易活动可以在任何时间、任何地点进行,从而大大提高了效率网络商城所具有的 开放性和全球性的特点,为企业创造了更多的贸易机会。网络商城使企业可以以相 近的成本进入全球电子化市场, 使得中小企业有可能拥有和大企业一样的信息资源, 提高了中小企业的竞争能力。网络商城重新定义了传统的流通模式,减少了中间环节,使得生产者和消费者的直接交易成为可能,从而在一定程度上改变了整个社会 经济运行的方式。网络商城一方面破除了时空的壁垒,另一方面又提供了丰富的信 息资源,为各种社会经济要素的重新组合提供了更多的可能,这将影响到社会的经 济布局和结构。 现在的购物商场成蓬勃向上发展的。
    1.2 需求分析一个网络购物系统,首先我们要保证客户能够很方便进行商品选择,系统应该具有分类选择商品功能,系统要实现购买功能。在系统的后台,管理员能够管理商品,商品分类,以及客户购买订单。 因此分析,本系统主要由前台和后台两部分组成,前台为客户端,顾客可以在此处购买商品,后台为商品管理端,实现对商品和订单的管理。
    第二章 系统分析2.1 开发环境根据用户的需求和实际的考察与分析,确定商城的开发环境,具体如下:

    服务器:从稳定性、广泛性及安全性方面综合考虑,采用市场主流的Web服务器软件Apache服务器
    数据库:采用最受欢迎的开源SQL数据库管理系统和被誉为PHP黄金搭档的MySQL
    开发框架:选用具有快速、兼容、开源、简单易学等特点的轻量级国产PHP开发框架—ThinkPHP

    2.4 系统运行环境该网上商城可运行在分辨率为1920×1080的chrome浏览器下。
    2.3 功能结构商城分为前台模块和后台模块。下面分别给出前、后台的功能结构图。


    2.4 目录结构目录结构即为think php 目录结构,再次不做介绍。
    第三章 数据库设计3.1 商品分类表(itcast_category)


    字段名
    数据类型
    描述




    cid
    Int unsigned
    主键ID,自动增长


    cname
    varchar(20)
    商品分类名称


    pcname
    varchar(20)
    父类分类名称



    3.2 商品表(itcast_goods)


    字段名
    数据类型
    描述




    gid
    varchar(255)
    主键ID,自动增长


    gname
    varchar(255)
    商品名称


    price
    int
    商品价格


    thumb
    varchar(255)
    商品图片路径


    status
    Enum(‘no’,’yes’)
    是否上下架,上架为yes,否则为no


    description
    text
    商品描述


    stock
    int
    商品库存


    cid
    Int unsigned
    商品分类ID


    sales
    int
    商品销量


    turn
    Int unique
    排序码,自动增长



    3.3 会员信息表(itcast_member)


    字段名
    数据类型
    描述




    mid
    Int unsigned
    主键ID,自动增长


    user
    varchar(20)
    会员昵称


    email
    varchar(30)
    会员电子邮件地址


    pwd
    char(32)
    会员登陆密码


    birthday
    date
    会员生日



    3.4 会员收货地址表(itcast_address)


    字段名
    数据类型
    描述




    aid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    consignee
    varchar(20)
    收货人姓名


    phone
    varchar(11)
    电话号码


    postcode
    varchar(6)
    邮政编码


    address
    varchar(255)
    收货地址


    freight
    int
    运费



    3.5 购物车表(itcast_shopcart)


    字段名
    数据类型
    描述




    scid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    addTime
    timestamp
    加入购物车时间


    gid
    varchar(255)
    商品ID


    num
    tinyint(3)
    商品数量



    3.6 购买记录表(itcast_record)


    字段名
    数据类型
    描述




    rid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    time
    timestamp
    购买时间


    gid
    varchar(255)
    商品ID


    num
    tinyint(3)
    商品数量


    price
    Int unsigned
    商品单价



    3.7 折扣商品表(itcast_discountgoods)


    字段名
    数据类型
    描述




    dgid
    Int unsigned
    主键ID,自动增长


    gid
    varchar(255)
    商品ID


    discount
    int
    折扣



    第四章 具体功能实现4.1 前台具体功能4.1.1 公共部分

    显示登录后的用户昵称、退出登录
    分类搜索商品
    进入各个功能模块
    登录按钮
    登录后查看购买记录、进入购物车



    购物车简要信息:总价(折扣前)、数量
    商品分类,点击即可查看该分类下的商品



    显示当前销量最高的商品


    网站相关信息
    联系方式

    4.1.2 主页
    滚动广告


    推荐商品


    新品推荐

    4.1.3 商品列表页
    商品列表页
    例如,在任一页面的导航栏的搜索框中输入关键词——手机,选择“手机”分类,点击“搜索”按钮。

    之后就可进入商品列表页,该页面显示了所有在指定分类下与关键词相关的商品。

    此时可点击排序下拉菜单,对商品进行排序(默认按照新品排序),例如按照价格升序排列

    类似还可进行:“新品”、“价格降序”、“销量”方式排序。

    折扣商品列表页
    若点击导航栏的“特价优惠”链接,即进入折扣商品列表页

    4.1.4 商品详情页
    查看商品信息
    在任何页面的商品列表点击指定商品,即可查看该商品的详细信息。

    此时,点击“相关商品”按钮,可查看该与该商品相关的其他商品。
    有两种查看方式:



    加入购物车
    会员登录后,在详情页选择购买数量,点击“加入购物车”按钮即可将选择的商品加入购物车。
    4.1.5 会员注册、登录在任一页面的导航栏点击“登录”按钮,即可进入下图所示页面:

    左部为注册框,右部为登录框。

    注册
    在注册框中输入要创建账户的邮箱地址,点击“创建”按钮

    进入下图所示页面填写会员详细信息。

    点击“注册”按钮,即完成了会员注册操作。

    登录
    在登录页面填写正确的邮箱、密码和验证码,点击“登录”按钮即可成功登录。

    若验证码填写错误,页面将显示如下提示信息,并返回登录页面。
    若用户名或密码填写错误,页面将显示如下提示信息,并返回登录页面。
    4.1.6 购物车
    简介
    登录成功后即进入购物车页面(新用户购物车为空),用户可在该页面对加入购物车的商品进行购买。

    假设会员已将自己心仪的商品加入到购物车,购物车页面将自动计算总价。


    更改购物车商品
    此时,会员可通过点击每个商品的“+”、“-”按钮对该商品数量进行增加和减少操作。

    若会员不想购买某件商品,可点击“×”按钮从购物车中删除该商品。


    购买商品
    点击“购买”按钮即将购物车中的商品全部购买。

    4.1.7 购买记录点击上方导航栏的“购买记录”按钮或点击下方页脚的“购买记录”超链接

    即可进入购买记录页查看购买记录。

    4.2 后台具体功能4.2.1 登录页面在登录页面填写正确的用户名、密码和验证码,点击“登录”按钮即可成功登录。

    若验证码填写错误,页面将显示如下提示信息,并返回登录页面。
    若用户名或密码填写错误,页面将显示如下提示信息,并返回登录页面。
    4.2.2 公共部分
    显示标题
    显示管理员名称
    “前台首页”超链接
    “退出登录”按钮



    左部导航栏,点击链接可进入相对应模块
    4.2.3 首页欢迎页面,引导管理员进行操作。

    4.2.4 商品添加在左侧导航栏点击“商品添加”链接进入商品添加页面。
    依次按要求填写商品信息,上传图片。

    点击“确定”按钮,即完成商品添加操作。
    若商品编号重复,则不添加该商品,直接进入商品修改页修改该商品。

    4.2.5 商品查看、修改、删除
    查看
    在左侧导航栏点击“商品列表”链接进入商品列表页面。

    管理员可对指定分类下的商品进行排序(默认对所有商品按照新品排序),例如对“手机—手机”分类按照价格升序排序:


    修改
    点击“修改”可以对指定商品属性进行修改(商品编号不可修改)。


    删除
    点击“删除”

    点击“确定”

    可以看到商品列表中编号为“562390304003”的商品已经被删除。

    4.2.6 查看、添加、删除商品分类
    查看商品分类
    在左侧导航栏点击“商品分类”链接进入商品分类列表页面


    添加商品分类
    点击商品分类列表页的“添加分类”按钮,即可进入商品分类添加页面。例如,选择一级分类为“电脑/办公”,分类名称为“服务器”


    删除商品分类
    点击商品分类列表页每一个商品分类对应的的“删除”
    可以看到商品分类列表页中名为“服务器”的商品分类已经被删除。

    查看会员信息
    在左侧导航栏点击“会员管理”链接进入会员信息列表页面

    点击每个会员的“查看详情”操作,可以查看该会员的详细信息和购买记录

    第五章 总结与心得体会通过这次网上商城开发,让我清楚认识到软件工程的重要程度,软件项目涉及到以下阶段,即计划阶段、需求分析、软件设计、编码、测试阶段、运行维护等。经过一段时间的努力,我们终于完成了网上商城网站系统,基本实现了题目的基本要求。总的来说,在做这个毕业设计的过程中,我们查阅了大量关于网上销售的相关资料,切实地按照软件工程的步骤,从需求分析,概要设计,详细设计,数据库设计,再到编码,调试运行,测试等步骤。从中我学到了很多东西,对我们来说,无论是理论还是实践上都是一个较大幅度的提高,可以说是理论到实践的一个飞跃。我还了解了软件开发的大体过程,在当今竞争激烈的社会中只有学到本领才能有立足之地,通过这次综合实验也使我们知道做软件开发的辛苦,首先要有足够的耐心,要勇于面对密密麻麻的代码,无数遍的调试,和无数遍的修改,但是,当调试成功时,你就会感到这些努力的意义,成功的喜悦。软件开发,还要注意借鉴,查看已有的例子的代码,这样可以节省大量的时间,同时也实现了代码重用。此外,我知道了基础课的重要,要学好一门编程语言,一定要动手,实践是最好的方法!
    5 评论 142 下载 2019-05-23 17:51:27 下载需要13点积分
  • 基于java语言的C/S模式网络聊天室软件

    一 需求分析
    采用C/S模式,基于TCP协议编程的方式,使得各个用户通过服务器转发实现聊天的功能
    分为三大模块:客户端模块、服务器端模块和公共辅助类模块
    客户端模块的主要功能:

    登陆功能:用户可以注册,然后选择服务器登入聊天室
    显示用户:将在线用户显示在列表中
    接收信息:能接收其他用户发出的信息
    发送信息:能发出用户要发出的信息

    服务器端模块的主要功能:

    检验登陆信息:检查登陆信息是否正确,并向客户端返回登陆信息,如信息正确。就允许用户登陆
    显示在线状态:将该用户的状态发给各在线用户
    转发聊天信息:将消息转发给所有在线的用户

    公共辅助类模块的主要功能:

    定义完整的消息传递机制
    对消息转发的方式进行有效约束
    规定消息类型


    二 程序设计2.1 程序设计思想实现网络聊天室编程,关键在于Socket通信,程序的功能都是在Socket的基础上一层一层增加的。
    实现Socket通信的基本方法为以下4个步骤:

    客户端与服务器端分别实例化ServerSockot/Socket
    打开连接到Socket的面向对象输入输出流
    利用输入输出流按照TCP协议对Socket进行读写操作
    关闭输入输出流和Socket

    我们要实现的功能都是在第3步对Socket的输入输出流做相应的操作:

    涉及到多客户端并发访问,必须用线程进行控制,不同的处理线程为不同的客户服务,主线程只负责循环等待,处理线程负责网络连接,接受客户输入的信息,根据消息类型对消息转发。
    2.2 系统模块划分客户端

    Client类模块,ClientUI界面模块,ClientMsgType消息处理模块
    服务器

    Server类模块,ServerHandler模块,ServerMsgType消息处理模块
    公共类:

    Message消息结构类,Common公共函数类,Config系统配置类
    系统模块结构图

    2.3 模块功能设计客户端

    Client类模块:与服务端建立连接,完成客户端登录,实现消息的群聊,私聊,用户列表更新,文件转换等功能
    ClientUI界面模块:客户端窗口界面,实现用户友好,方便用户使用,在窗口上有消息显示,用户列表,消息类型,在线人数等视图信息
    ClientMsgType消息处理模块:定义了常用的消息处理方法

    服务端

    Server类模块:启动监听,与客户端建立连接
    ServerHandler模块:提供线程管理,实现多用户的管理。针对每一个用户发过来的消息,进行相应处理,再转发给客户
    ServerMsgType消息处理模块:对客户端发送的消息进行指定操作模式处理,只进行消息转发,涉及一对多和一对一两种模式

    公共类

    Message消息结构类:客户端和服务器传输的消息被定义为类,通过对Message对象的序列化传输
    Common公共函数类:提供文件序列化的处理以及常用的功能性函数
    Config系统配置类:包含连接IP及端口号的配置信息

    三 程序实现3.1 客户端实现Client类模块
    关键模块,通过服务器IP地址与端口建立连接。客户端有套接字和输入输出流。首先输入用户名,然后包装成Message对象发送给服务器,之后进入主循环对来自服务器的消息进行处理。
    public void run() { try { // 获取用户名 while (name == null || name.equals("")) name = JOptionPane.showInputDialog("请输入名字").trim(); // 第一次运行,发送登陆消息 send(new Message("login", name, null, null)); // 显示界面 ui = new ClientUI(this, name + " 的客户端"); // 消息处理主循环 while (true) { Message rcv = (Message) in.readObject(); // 服务器端发来的消息 System.out.println(rcv.toString()); new ClientMsgType(this, rcv).process(); } } catch (Exception e) { System.out.println(e.toString() + " 客户端退出"); } }
    ClientUI类模块
    用户界面,所有的界面元素及相关的操作都在里面定义了方法,不论UI界面如何改变都不会影响到外部类。里面含有各种控件极其功能定义,还包括文件传输。主体代码如下:
    public ClientUI(Client client, String winname) { super(winname); // 继承父类的名字 setSize(600, 400); this.client = client; // 消息文本显示区域 msgArea = new JTextArea(400, 400); msgArea.setEditable(false); textAreaScrollPane = new JScrollPane(msgArea); add(textAreaScrollPane, BorderLayout.CENTER); // 发送消息区域 textFieldPanel.setLayout(new FlowLayout(0, 10, 10)); add(textFieldPanel, BorderLayout.SOUTH); clientList = new JComboBox<String>(); // 在线用户列表 clientList.addItem("All"); textFieldPanel.add(clientList); msgType = new JComboBox<String>(); // 聊天文本或者文件 msgType.addItem("chat"); msgType.addItem("file"); textFieldPanel.add(msgType); msgField = new JTextField(20); // 输入消息文本 textFieldPanel.add(msgField); btn = new JButton("发送"); // 发送消息按钮 btn.setMnemonic(KeyEvent.VK_ENTER); textFieldPanel.add(btn); cntLabel = new JLabel("在线人数:1"); // 显示在线人数 textFieldPanel.add(cntLabel); // 发送消息按钮监听器 btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String type = (String) msgType.getSelectedItem(); String title = ""; String content = getText().trim(); byte[] fbyte = null; if (content.equals("")) { JOptionPane.showMessageDialog(client.ui, "输入不能为空"); return; } if (type.equals("file")) { // 获取客户端发送文件 JFileChooser dlg = new JFileChooser(); dlg.setDialogTitle("选择文件"); int result = dlg.showOpenDialog(client.ui); if (result == JFileChooser.APPROVE_OPTION) { File file = dlg.getSelectedFile(); fbyte = Common.file2Byte(file); title = file.getName(); } else { return; } append("TO " + getName() + ": " + title + "\n"); } append("TO " + getName() + ": " + content + "\n"); client.send(new Message(type, client.name, getName(), title, content, fbyte)); clear(); } }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { client.close(); System.exit(0); } }); setVisible(true);}
    ClientMsgType类模块
    定义了客户端处理消息的方法,主体架构和服务区类似,这里我们只介绍处理文件的方法:
    /** * 文件处理 利用字节数组可以处理所有文件 */ public void file() { String toAll = "[public]"; if (msg.getTo().equals(client.name)) toAll = "[private]"; client.ui.append(toAll + msg.getFrom() + ": " + msg.getContent() + "\n"); int confirm = JOptionPane.showConfirmDialog(client.ui, "收到了来自" + msg.getFrom() + "的文件:" + msg.getTitle() + ",需要保存吗?"); if (confirm != JOptionPane.YES_OPTION) return; // 获取保存路径 JFileChooser dlg = new JFileChooser(); dlg.setDialogTitle("选择保存路径"); dlg.setSelectedFile(new File(msg.getTitle())); int result = dlg.showSaveDialog(client.ui); if (result != JFileChooser.APPROVE_OPTION) return; File file = dlg.getSelectedFile(); if (file.exists()) { int copy = JOptionPane.showConfirmDialog(null, "是否要覆盖当前文件?", "保存", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (copy == JOptionPane.YES_OPTION) { dlg.approveSelection(); } } else { dlg.approveSelection(); } if (Common.byte2File(msg.getFile(), file)) { JOptionPane.showMessageDialog(client.ui, "文件保存成功"); } else { JOptionPane.showMessageDialog(client.ui, "文件保存失败"); }}
    3.2 服务器实现Server类模块
    创建ServerSocket,在指定端口监听,当有用户登录,接受套接字输入流,显示其登录信息,调用ServerHandler线程进行管理。
    public Server(int port) { try { @SuppressWarnings("resource") ServerSocket server = new ServerSocket(port); while (true) { Socket conn = server.accept(); // 建立客户端套接字 // 新建线程和客户端建立全双工通信 new Thread(new ServerHandler(conn)).start(); } } catch (Exception e) { System.out.println(e.toString()); } }
    ServerHandler模块
    服务端的核心,其中包含客户端线程连接类,套接字及输入输出流的初始化。每个新连接的客户端被初始化为一个ServerHandler对象线程,首先进行的是客户端登陆合法化检验,通过后将客户端添加到服务器线程池并通知所有在线用户。之后采用一个无限循环来和该客户端进行双向通信。
    代码中数据结构定义:
    // 输入流private ObjectInputStream in; // 输出流private ObjectOutputStream out; // 用户名private String name;// 把用户线程放入对象数组private static ArrayList<ServerHandler> clientList = new ArrayList<ServerHandler>(); // 把用户名和连接线程关联便于查找private static HashMap<String, ServerHandler> clientMap = new HashMap<>();
    初始化代码
    public ServerHandler(Socket socket) { try { in = new ObjectInputStream(socket.getInputStream()); out = new ObjectOutputStream(socket.getOutputStream()); } catch (IOException e) { System.out.println(e.toString() + " 服务端初始化失败"); } }
    核心代码
    public void run() { if (!loginVerify()) return; // 登陆校验 try { addClient(); // 客户端经过登陆检验 添加到列表 sendMessage(new Message("online", name, "All", "上线了!")); // 广播用户列表 while (true) { Message rcv = (Message) in.readObject(); // 等待客户端消息 sendMessage(rcv); } } catch (Exception e) { System.out.println(e.toString() + " 客户端已退出"); } finally { removeClient(); sendMessage(new Message("offline", name, "All", "下线了!")); // 广播下线通知 } }
    登陆校验
    private Boolean loginVerify() { try { Message rcv = (Message) in.readObject(); System.out.println(rcv.toString()); this.name = rcv.getFrom(); if (clientMap.containsKey(name)) { // 用户名重复 sendToClient(this, new Message("login", null, name, "用户名已存在")); return false; } } catch (Exception e) { System.out.println(e.toString() + "登陆检验失败"); return false; } return true; }
    消息广播
    把msg消息发送给每一个用户。其中clientList是ServerHandler的客户端容器类型,可以指向每一个用户。clientMap 是用户名到线程的映射。
    public void sendAllClient(Message msg) { String name = msg.getFrom(); ServerHandler sh = clientMap.get(name); synchronized (clientList) { for (ServerHandler client : clientList) { if (client != sh) { sendToClient(client, msg); } } } }
    上线提醒
    sendClientList函数:更新用户列表,当有新用户上线时,对以前的用户的好友列表,加上这个新用户名字,对于这个新用户,加上所有用户的名字。通过将消息包装成Message对象进行数据交互。
    public synchronized void sendClientList(Message msg) { // 新上线的用户 ServerHandler newclient = clientMap.get(msg.getFrom()); for (ServerHandler client : clientList) { if (client == newclient) continue; // 给新上线用户所有列表 sendToClient(newclient, new Message(msg.getType(), client.name, newclient.name, null)); // 更新老用户 sendToClient(client, new Message(msg.getType(), newclient.name, client.name, msg.getContent())); }}
    ServerMsgType模块
    定义了服务端对客户端发送消息的处理方式。消息包装成Message类,经过序列化后在服务器和客户端间传输。服务端根据消息的收发对象和类型进行转发。这里利用到Java的反射机制,根据消息类型获取相应处理函数。
    public class ServerMsgType { ServerHandler server; Message msg; public ServerMsgType(ServerHandler server, Message msg) { this.server = server; this.msg = msg; } public void process() { try { Method method = this.getClass().getDeclaredMethod(msg.getType()); method.invoke(this); } catch (Exception e) { System.out.println(e.toString() + " 使用了未定义的操作"); try { Method method = this.getClass().getDeclaredMethod("chat"); method.invoke(this); } catch (Exception e1) { System.out.println(e1.toString() + " 默认处理失败"); } } } public void chat() { if (msg.getTo().equals("All")) { // 给所有人发送消息 server.sendAllClient(msg); } else { // 发送消息给指定的人 server.sendToClient(msg); } } public void online() { server.sendClientList(msg); } public void offline() { server.sendAllClient(msg); } public void login() { server.sendToClient(msg); }}
    3.3 公共类实现Message模块
    底层数据封装的核心。所有的消息全部通过封装成Message从而在服务器和客户端之间收发。主体内容如下:
    private String type; // 消息类型private String from; // 来源private String to; // 目的private String title; // 附件名private String content; // 消息内容private byte[] file; // 附件内容
    其中系统已经定义的消息类型包括:

    chat:聊天内容、online:客户端上线、offline:客户端下线、login:客户端登陆
    每种类型在服务器和客户端的MsgType类中都有定义对应的处理函数
    From字段标识消息发送的来源,为客户端的用户名
    To字段标识消息发送的对象,为客户端的用户名,如果为All则是广播消息
    Title字段当发送文件时有效,为原文件名
    Content字段为消息的内容
    File字段为附件的字节数组

    把以上字段组装成类之后经过序列化就可以在客户端和服务器之间传输。
    Common模块
    定义了文件处理相关的函数,这里我们把文件作为字节数组处理,这样实现了文件的透明化传输。
    文件转化为字节数组
    public static byte[] file2Byte(File file) { byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int len = 0; try { FileInputStream fin = new FileInputStream(file); len = fin.read(buffer); fin.close(); } catch (Exception e) { System.out.println(e.toString() + " 文件无法转化成字节"); } byte[] ret = new byte[len]; for (int i = 0; i < len; i++) ret[i] = buffer[i]; return ret; }
    字节数组转化为文件
    public static boolean byte2File(byte[] in, File out) { try { FileOutputStream fout = new FileOutputStream(out); fout.write(in); fout.close(); } catch (Exception e) { System.out.println(e.toString() + " 字节转换失败"); return false; } return true; }
    Config模块
    主要包括IP和port的配置。
    四 运行测试4.1 客户端进入聊天室在客户端输入名字进入聊天室,如果名字重复会弹框提示。


    4.2 聊天界面聊天界面显示客户端名字,好友列表,在线人数,消息类型等信息。

    4.3 选择聊天类型4.3.1 聊天类型为chat分为群聊和私聊:群聊是选择All,私聊是选择具体的用户名。


    4.3.2 聊天类型为file发送文件,可以输入描述信息,客户端可以选择是否接收文件。




    4.4 人数动态刷新
    6 评论 194 下载 2018-11-04 21:43:30 下载需要12点积分
  • 基于python的B站弹幕数据分析(爬虫+可视化)

    python—B站弹幕数据分析1 背景在视频网站上,一边看视频一边发弹幕已经是网友的习惯。B站就是其中一个比较出名的弹幕网站,许多年轻人都喜欢逛B站,看喜欢的动漫亦或某些UP主做的一些剪辑。本项目,就是对B站弹幕数据进行分析。选取分析的对象是B站上一部国漫《全职高手》。
    2 环境的安装本项目实在pycharm中实现,使用到的第三方库有requests,bs4,pandas,jieba.posseg,pyecharts。具体如何安装,百度都有详细的步骤,我在这里就不啰嗦了。
    3 相关代码代码实现3.1 爬虫部分在《全职高手》动漫的播放页查看网页源码,找到cid

    因为b站需要通过格式为:”https://api.bilibili.com/x/v2/dm/history?type=1&oid={}&date=2018-{}-{}“ 的链接获取弹幕历史文件,所以我们到时候可以通过url拼接,来爬取我们想要的具体哪一天的弹幕。
    存放B站弹幕的是网站是xml格式的,如下图所示

    其中每个字段都有对应的含义:

    第一个参数是弹幕出现的时间以秒数为单位
    第二个参数是弹幕的模式1..3 滚动弹幕 4底端弹幕 5顶端弹幕 6.逆向弹幕 7精准定位 8高级弹幕
    第三个参数是字号, 12非常小,16特小,18小,25中,36大,45很大,64特别大
    第四个参数是字体的颜色以HTML颜色的十进制为准
    第五个参数是Unix格式的时间戳。基准时间为 1970-1-1 08:00:00
    第六个参数是弹幕池 0普通池 1字幕池 2特殊池【目前特殊池为高级弹幕专用】
    第七个参数是发送者的ID,用于“屏蔽此弹幕的发送者”功能
    第八个参数是弹幕在弹幕数据库中rowID 用于“历史弹幕”功能

    遍历爬取弹幕历史文件,存入csv中,保存格式为:[‘弹幕出现时间’, ‘弹幕格式’, ‘弹幕字体’, ‘弹幕颜色’, ‘弹幕时间戳’,’弹幕池’,’用户ID’,’rowID’,’弹幕信息’]

    3.2 可视化部分保存了所有数据之后,先对数据进行处理:先读取csv文件,再进行可视化分析
    每一集弹幕总量变化—折线图:逐个读入弹幕历史文件:d1.csv,d2.csv,……经过去重后,统计出每集的弹幕总量:
    data = pd.read_csv(path.strip(),encoding='gbk',engine='python')
    统计每一集的弹幕总量,存放在字典中
    episode_comment_dic[i] = every_episode_comment(data)
    之后便进行可视化绘图了
    def every_episode_comment_change(episode_comment_dic):line = pyecharts.Line("每一集弹幕变化总量",'2018-12-27',width=1200,height=600) keys = [] values = [] for i in episode_comment_dic: keys.append("第%d集" % i) values.append(episode_comment_dic[i]) '''每一集弹幕总量的折线变化图''' line.add("每一集弹幕总量的折线变化图",keys,values) es = pyecharts.EffectScatter() es.add("",keys,values) overlap = pyecharts.Overlap() overlap.add(line) overlap.add(es) return overlap
    发送弹幕总量TOP5用户:1.统计每一集弹幕数量,把用户排序,每一集排序后的结果都是一个DataFrame, 结果的大致结构:user_sort_dic = {1: DataFrame1, 2:DataFrame2, ……,12: DataFrame12}。
    '''每一集用户发送弹幕数量排序''' def every_episode_usersort(data): df = data.drop_duplicates() dd = df.groupby("用户ID").count() user_sort = dd.sort_values(by = '弹幕信息',ascending=False).loc[:,['弹幕信息']] return user_sort
    用户发弹幕长度分布,统计发送弹幕的字符串长度:

    用户发弹幕数量分布,统计一集,用户发送弹幕数量的百分比分布图。
    user_sort_dic[i] = every_episode_usersort(data) '''统计用户发送弹幕数量的百分比分布图''' for i in user_sort_dic: d_tmp = user_sort_dic[i] pie = every_episode_barrage_pie(d_tmp,i) timeline1.add(pie,i) del d_tmp
    每集弹幕密度变化图:每个弹幕都有一个时间参数,代表了用户在视频的什么时间发了弹幕。所以我们可以统计出在相同时间参数发出的弹幕量,然后再画出“时间—弹幕数量”折线图,就可以看到弹幕量的变化了。
    词云图:对所有的弹幕文本进行分析。先用jieba词库进行分词,然后逐行对弹幕进行词频统计,最后用pyecharts的WordCloud画出词云图,wordcloud = pyecharts.WordCloud(“全职高手-词云图”)

    3.3 统计结果全职高手动漫12集用户发送弹幕条数饼图—轮播:

    每一集弹幕总量的变化:

    每一集用户发送弹幕的字数饼图—轮播:

    12集用户发送弹幕总量排名—直方图:

    一集视频中用户发送的弹幕密度图:

    12集动漫的词云图:

    3.4 结果分析通过以上的图表数据可以看出用户的弹幕的一些行为。
    12集剧里,第一集,第二季和最后一集,弹幕数量是最多的,说明这部动漫刚上线的时候关注度还是很高的,之后的弹幕数量不高可能是因为剧情发展比较平稳,最后一集弹幕数量又多了起来,说明动漫结束,用户们都纷纷发表感慨,从词云中的‘散场’也可看出。
    统计了12集的弹幕,用户ID为5ff19abd的用户共发送了480多条弹幕,算是超级粉丝了。
    通过弹幕数量和长度的分布,可以看出:绝大部分用户只会发送2条以内的弹幕。参与弹幕讨论的用户以10个字符以内的短语为主。
    统计每一集的弹幕密度,可以通过用户观看时的讨论,大致确定视频的精彩点。
    通过词云图,可以大致看出用户的分布。例如‘散场’作为热词显示里。说明许多用户都为动漫的结束发表感慨。
    4 总结因为绘制热词云图的代码运行太久了,所以本实验的词云图只截取了某些天的弹幕,或许可以引入多线程使程序效率更高。因为B站的网页是动态的,当时做爬虫的时候准备用webdriver,无奈环境搞不会,放弃了。本项目还是有许多缺陷的,当时做的时候借鉴了一篇博客,又把它给改进了一下,然后才实现出了该项目。(大三上python数据分析大作业,本人很菜,不完全原创,内有许多不足。)
    9 评论 222 下载 2018-12-31 16:21:34 下载需要13点积分
  • 基于java的多人聊天程序课程设计

    一、设计内容及要求1.1 设计内容聊天工具大多数由客户端程序和服务器程序外加服务器端用于存放客户数据的数据库组成,本程序采用客户机/服务器架构模式。通过Java提供的Socket类来连接客户机和服务器并使客户机和服务器之间相互通信,由于聊天是多点对多点的而Java提供的多线程功能。用多线程可完成多点对多点的聊天。
    1.2 设计要求主要有两个应用程序,分别为服务器程序和客户端程序。服务器应用程序主要用于消息转发、客户登录信息的管理以及向所有用户发送系统消息等;客户端应用程序主要用于客户聊天记录的显示和信息输入。采用Client/Server(C/S)体系结构,即客户机/服务器体系结构。聊天服务器专门用于监控用户状态和转发消息,客户端负责接收消息的用户序列和消息文本发送到服务器。该聊天系统实现私聊,群聊,用户注册,登陆,退出聊天系统等功能。
    二、系统需求分析2.1 系统介绍在当今信息时代,有许多的聊天工具,例如QQ、微信等。本程序就是利用Java网络编程的知识,采用客户机/服务器架构模式来实现客户端与客户端之间的通讯。
    2.2 开发背景在当今信息时代,越来越多的聊天工具被应用,Java语言是当今流行的网络编程语言,它具有面对对象、跨平台、安全、多线程等特点。使用Java语言不仅可以实现大型企业级的分布式系统应用,还能为小型的、嵌入式设备进行应用程序开发。面对对象的开发方法是当今最流行的开发方法,它不仅更贴近现实,而且有利于软件的维护和继承。为了进一步巩固课堂上所学到的知识,深刻把握Java语言的重要概念及面对对象的特性,锻炼我们熟练的应用面对对象的思想和设计方法解决实际问题的能力,所以我选择了开发Java多人聊天程序。
    三、系统总体设计3.1 系统功能结构图
    3.2 系统数据流程图
    四、系统详细设计4.1 本设计所涉及技术和知识点
    C/S体系结构:采用Client/Server(C/S)体系结构,即客户机/服务器体系结构。其中客户端用了发送和接受收显示聊天消息,服务器用来监听客户端的消息,并将接收到的数据转发到所用的客户端上,这样实现群聊功能。Socket套接字:作为多人聊天程序,网络编程的知识必不可少,其中用到了Socket套接字、IP地址、端口号等网络编程的知识。客户端与服务器的窗口用到Java Swing图形用户界面知识,其中包括界面的布局、组件的使用以及各种监听事件的使用。多线程:为了是客户端与客户端之间的消息、客户端发送与接收的消息不发生冲突,将每个客户端的发送和接收用不同的线程。IO流:聊天时客户端与服务器之间肯定有数据的输入输出,所以IO流的知识在这里也十分重要。
    4.2 功能模块详细设计
    客户端:运行客户端时,弹出登录客户端界面,这是需要设置用户名、IP地址和端口号,其中IP和端口号必须和服务器的对应,否则连接不上服务器。登录后进入客户端聊天界面窗口。这时可以与其他客户端之间在一个聊天室中聊天,若想私聊某人,只需在聊天内容前加上@该用户的姓名+:就可以了。点击右上角的下线按钮即退出该聊天室。
    发送消息:当点击发送按钮时,将消息通过DataOutputStream流中的writeUTF发送到服务端,服务端通过DataInputStream中的readUTF接受客户端的消息,紧接着服务端通过DataOutputStream中的writeUTF发送个所有客户端。
    私聊:当用户登录后,在没有点击发送按钮前,先将该客户端的姓名发送到服务器,服务器先接收到用户的姓名,然后在接收聊天消息。
    服务器:启动服务器,弹出设置服务器的窗口,填写服务器的IP地址和端口号,点击确定后,服务器启动,并不断监听客户端的连接和接收客户端发来的消息。

    五、编码与实现5.1 具体功能模块实现该多人聊天程序包括8个文件,功能如下:
    1. Client.java 包含Client类,其中有main、connectServer函数,是客户端启动的入口。在main函数中通过创建一个ClientLogin对象,来打开登陆窗口。 ClientLogin login = newClientLogin();
    2. ClientFrame.java
    包含ClientFrame类,该类继承于Jframe类,用来创建客户端的窗口。 其中包括窗口中的各个组件、布局以及组件的监听事件。
    sendButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { msg = msgText.getText().toString(); ClientSend.send(); msgText.setText(null); });
    发送按钮的监听事件,当点击发送时,将发送栏中的消息保存在字符串msg中,在,然后调用 ClientSend.send()函数,将该字符串发送到服务器。
    3. ClientLogin.java
    包含ClientLogin类,该类继承于Jframe类,用来创建客户端登录窗口,其中包括设置IP地址、端口号、用户姓名的各种组件,和登录按钮的监听事件:
    btn_login.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { name = jtf_userName.getText().toString(); ip = jtf_ip.getText().trim(); port = Integer.parseInt(jtf_port.getText().toString()); //点击登录后,客户端连接服务器 try { Client.connectServer(); } catch (UnknownHostException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } //隐藏当前窗口 dispose(); //关闭 //打开客户端 new ClientFrame(); } });
    点击登录按钮时,将窗口中用户姓名、IP地址和端口号分别赋值给字符串name、ip、port变量,然后调用Client.connctServer()来连接服务器,并且关闭该窗口,创建ClientFrame对象来打开客户端窗口。
    4. ClientReceive.java
    包含ClientReceive类,该类中有Runnable接口,用于创建一个用户接受服务器消息的客户端接受线程。
    线程体:
    public void run() { String msg = null; while(isRunning){ //将接受信息输出到客户端窗口中 msg = receive(); System.out.println(msg); ClientFrame.msgRecordText.append(msg+"\n"); } }
    该线程处于死循环中,其中receive()函数用来接收服务器的消息,并通过ClientFrame.msgReceordText.append(msg+”\n”)将消息显示在客户端的窗口聊天记录面板上。
    5. ClientSend.java
    包含ClientReceive类,该类中有Runnable接口,用于发送消息,该线程的线程体为空:
    public void run() { }
    当用户点击客户端窗口中的发送按钮时,会调用该类中的send函数,用于将消息发送到服务器。
    6. Server.java
    包含Server类,其中包含main、setServer方法和List<ServerForward>集合类。
    其中main函数是服务器的入口,用来打开服务器设置窗口。
    main函数:
    ServerFrame serverFrame = new ServerFrame();
    setServer函数:
    public static void setServer() throws IOException { //从服务器窗口获取ip和端口号 String ip = ServerFrame.ip; int port = ServerFrame.port; ServerSocket server = new ServerSocket(port, 0,InetAddress.getByName(ip)); while(true) { Socket client = server.accept(); System.out.println("连接成功"); ServerForward serverForward = new ServerForward(client); allClient.add(serverForward); Thread thread = new Thread(serverForward); thread.start(); } }
    启动服务器,不断的监听客户端的连接,当有客户端连接上时,在List<ServerForward>集合类中添加该线程,并启动该线程。
    7. ServerForward.java
    包含ServerForward类,服务器接受一个客户端得信息,再转发给其他客户端。该类中主要包括receive、send函数,用于转发客户端的消息。
    线程体:
    public void run() { //接受用户的昵称 while(isRunning) { if (name == null) name = getClientName(); else break; } while(isRunning) { sendToClient(receive()); } }
    因为服务器的工作不间断的,所以用无限循环。getClientName()从客户端中接受用户的姓名,并保存字符串name中。sendToClient(receive())接收一个客户端的消息,并转发给所有客户端。
    8. ServerFrame.java
    包含serverFrame类,该类继承于JFrame,用于创建服务器设置界面,其中包括设置IP地址、端口号,和确认按钮的监听事件:
    btn_set.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ip = jtf_ip.getText().trim(); port = Integer.parseInt(jtf_port.getText().toString()); //关闭窗口 dispose(); //点击确定后,设置服务器ip和端口号 try { Server.setServer(); } catch (UnknownHostException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } });
    将服务器设置窗口中的IP地址和端口信息ip、port字符串中,并调用Server.setServer函数,开始启动服务器。
    5.2 演示界面5.2.1 服务器设置窗口
    5.2.2 客户端登录窗口
    5.2.3 客户端窗口
    5.2.4 群聊和私聊
    8 评论 333 下载 2018-10-21 14:43:22 下载需要13点积分
  • 基于JAVA和Oracle数据库实现的项目信息管理系统

    1 需求分析1.1 背景项目管理系统,是反映公司员工资料,项目负责部门和项目进度,设备采购软件系统,是管理项目的有效工具。
    面向用户:对项目进度跟踪,公司人员信息管理和设备管理的公司。只能由公司内部查看数据库中的数据。要求使用本系统的用户管理系统的使用有所了解,知道基本的操作和使用过程的注意事项。
    1.2 系统目标系统对外部封闭,不允许外部人员访问公司项目管理系统中的数据库。能够安全的访问系统独立的数据库。程序实现数据库数据的直观显示,保证数据库的能够为公司提供较为方便和基础的项目管理服务,能够为公司管理层提供监督渠道。
    数据分析:员工的基本信息,部门的基本信息,项目的基本信息,设备基本信息
    1.3 功能分析项目管理系统应该能够提供以下功能:管理员登陆、员工信息管理、部门信息管理、项目信息管理、设备信息管理。

    管理员登录:项目管理系统采用Oracle数据库连接的服务名,用户名和口令(密码)验证模式,进入项目管理系统前项目管理员必须在登陆界面输入验证信息方可进入项目管理系统的主界面进行相应的操作
    学生信息管理:学生信息管理包含五个模块:学生信息的浏览、添加、删除、查询、修改
    部门信息管理:包含一个模块:部门经理信息查询
    项目信息管理:包含两个模块:根据项目经理的姓名查询项目的完成情况、查询某个项目的设备购买情况
    设备信息管理:包含三个模块:设备费用查询、设备供应商查询、添加设备

    1.4 性能需求分析
    系统易操作性:项目管理系统应该做到操作简单,界面友好,使得用户可以快速上手使用,不受到专业知识的限制
    系统可维护性:由于系统涉及的信息比较多,数据库中的数据需定期修改,系统可利用的空间及性能也随之下降,为了使系统更好地运转,用户可以对系统数据及一些简单的功能进行独立的维护及调整

    2 概念设计2.1 概念模型(E-R图)
    2.2 数据字典数据字典包括的项目有数据项、数据结构、数据流、数据存储、加工逻辑和外部实体。可使用一些符号来表示数据结构、数据流和数据存储的组成。
    2.2.1 员工表


    数据元素
    数据类型
    数据长度
    数据描述




    w_id
    VARCHAR2
    4
    员工编号


    w_name
    VARCHAR2
    16
    员工姓名


    sex
    CHAR
    2
    员工性别


    age
    CHAR
    20
    员工年龄


    contract_date
    DATE

    合同日期


    d_id
    VARCHAR2
    4
    部门编号


    d_name
    VARCHAR2
    12
    部门名称


    post
    VARCHAR2
    6
    员工职务



    2.2.2 部门表


    数据元素
    数据类型
    数据长度
    数据描述




    d_id
    VARCHAR2
    4
    部门编号


    d_name
    VARCHAR2
    12
    部门名称


    m_id
    VARCHAR2
    4
    部门经理编号


    m_name
    VARCHAR2
    8
    部门经理姓名


    w_num
    NUMBER

    员工人数



    2.2.3 项目表


    数据元素
    数据类型
    数据长度
    数据描述




    p_id
    VARCHAR2
    4
    项目编号


    p_name
    VARCHAR2
    16
    项目名称


    d_id
    VARCHAR2
    4
    部门名称


    w_id
    VARCHAR2
    4
    项目经理编号


    fund
    FLOAT
    126
    项目经费


    type
    VARCHAR2
    4
    项目类型


    signing_time
    DATE

    签订时间


    complete_time
    DATE

    应完成时间


    check_time
    DATE

    验收时间


    remarks
    VARCHAR2
    8
    备注



    2.2.4 设备表


    数据元素
    数据类型
    数据长度
    数据描述




    e_id
    VARCHAR2
    4
    设备编号


    e_name
    VARCHAR2
    12
    设备名称


    fee
    FLOAT

    设备费用


    supplier
    VARCHAR2
    12
    供应商


    p_id
    VARCHAR2
    4
    项目编号


    remarks
    VARCHAR2
    8
    备注



    2.3 数据流图数据流图(Data Flow Diagram)是一种图形化技术,它描绘信息流和数据从输入到输出的过程中所经受的变换。根据数据流图,可以分析出程序所需的模块和模块之间的调用关系。
    如下图所示,在项目管理系统,在用户界面捕捉用户的操作,接受事务后数据流流向不同的模块。

    3 逻辑结构设计3.1 关系描述
    员工与部门的关系:n:1一个员工只能属于一个部门,一个部门可以有多个员工
    部门与项目的关系:1:n一个部门可以负责多个项目,一个项目只能由一个部门负责
    员工与项目的关系:m:n一个员工可以实现多个项目,一个项目可以由多个员工实现
    员工与设备的关系:1:n一个员工可以采购多个设备,一个设备只能由一个员工采购
    项目与设备的关系:1:n一个项目可以使用多个设备,一个设备只能由一个部门使用

    3.2 系统结构图
    3.3 流程图
    4 系统实施4.1 建表语句/*==============================================================*//* Table: "worker" *//*==============================================================*/create table "worker" ( "w_id" VARCHAR2(4) not null, "w_name" VARCHAR2(8), "sex" CHAR(2), "age" CHAR(2), "contract_date" DATE, "post" VARCHAR2(8), "d_id" VARCHAR2(4), constraint PK_WORKER primary key ("w_id"));/*==============================================================*//* Table: "department" *//*==============================================================*/create table "department" ( "d_id" VARCHAR2(4) not null, "d_name" VARCHAR2(16), "m_id" VARCHAR2(4), constraint PK_DEPARTMENT primary key ("d_id"));/*==============================================================*//* Table: "project" *//*==============================================================*/create table "project" ( "p_id" VARCHAR2(4) not null, "p_name" VARCHAR2(16), "fund" FLOAT(126), "type" VARCHAR2(4), "signing_time" DATE, "complete_time" DATE, "check_time" DATE, "m_id" VARCHAR2(4), "remarks" VARCHAR2(256), constraint PK_PROJECT primary key ("p_id"));/*==============================================================*//* Table: "equipment" *//*==============================================================*/create table "equipment" ( "e_id" VARCHAR2(4) not null, "e_name" VARCHAR2(16), "fee" FLOAT(126), "supplier" VARCHAR2(16), "p_id" VARCHAR2(4), "remarks" VARCHAR2(256), constraint PK_EQUIPMENT primary key ("e_id"));
    4.2 插入数据4.2.1 worker表INSERT INTO "worker" VALUES ('1008', '马化腾', '男', '29', TO_DATE('20171229201245', 'YYYYMMDDHH24MISS'), '经理', '11');INSERT INTO "worker" VALUES ('1011', '雷军', '男', '21', TO_DATE('20171220085418', 'YYYYMMDDHH24MISS'), '职员', '11');INSERT INTO "worker" VALUES ('1006', '司马懿', '男', '56', TO_DATE('20171229202106', 'YYYYMMDDHH24MISS'), '职员', '13');INSERT INTO "worker" VALUES ('1005', '王超', '男', '38', TO_DATE('20171018161809', 'YYYYMMDDHH24MISS'), '经理', '14');INSERT INTO "worker" VALUES ('1001', '张三', '男', '26', TO_DATE('20171026143550', 'YYYYMMDDHH24MISS'), '职员', '11');INSERT INTO "worker" VALUES ('1002', '李四', '男', '25', TO_DATE('20171026143733', 'YYYYMMDDHH24MISS'), '职员', '12');INSERT INTO "worker" VALUES ('1003', '李玲', '女', '27', TO_DATE('20171026144030', 'YYYYMMDDHH24MISS'), '经理', '13');INSERT INTO "worker" VALUES ('1004', '王五', '男', '25', TO_DATE('20171026144412', 'YYYYMMDDHH24MISS'), '经理', '14');
    4.2.2 department表INSERT INTO "department" VALUES ('11', '采购部', '1008');INSERT INTO "department" VALUES ('12', '销售部', '1002');INSERT INTO "department" VALUES ('13', '策划部', '1003');INSERT INTO "department" VALUES ('14', '人事部', '1004');
    4.2.3 project表INSERT INTO "project" VALUES ('004', '跨海大桥', 300000000, '桥梁', TO_DATE('20160226150155', 'YYYYMMDDHH24MISS'), TO_DATE('20170326150206', 'YYYYMMDDHH24MISS'), TO_DATE('20171001150213', 'YYYYMMDDHH24MISS'), '1004', NULL);INSERT INTO "project" VALUES ('001', '京沪高速', 300000, '建筑', TO_DATE('20170901145036', 'YYYYMMDDHH24MISS'), TO_DATE('20171026145042', 'YYYYMMDDHH24MISS'), TO_DATE('20171027145048', 'YYYYMMDDHH24MISS'), '1005', NULL);INSERT INTO "project" VALUES ('002', '青藏铁路', 1500000, '建筑', TO_DATE('20170701145439', 'YYYYMMDDHH24MISS'), TO_DATE('20170901145459', 'YYYYMMDDHH24MISS'), TO_DATE('20171026145505', 'YYYYMMDDHH24MISS'), '1008', NULL);INSERT INTO "project" VALUES ('003', '鸟巢', 30000000, '工程', TO_DATE('20170801145900', 'YYYYMMDDHH24MISS'), TO_DATE('20170901145906', 'YYYYMMDDHH24MISS'), TO_DATE('20171004145911', 'YYYYMMDDHH24MISS'), '1003', NULL);
    4.2.4 equipment表INSERT INTO "equipment" VALUES ('101', '挖掘机', 100000, '徐工', '001', NULL);INSERT INTO "equipment" VALUES ('102', '玻璃', 10000, '通用', '003', NULL);INSERT INTO "equipment" VALUES ('103', '铝合金', 20000, '上汽', '002', NULL);INSERT INTO "equipment" VALUES ('104', '液晶', 50000, '京东方', '004', NULL);INSERT INTO "equipment" VALUES ('105', '台式机', 100000, '清华同方', '003', '办公专用');INSERT INTO "equipment" VALUES ('108', 'A4纸', 200, '华润', '003', NULL);INSERT INTO "equipment" VALUES ('109', '键鼠套装', 1000, '达尔优', '003', NULL);
    5 运行维护转储:定期进行静态转储,动态转储,海量转储。
    数据库中可能发生各种各样的故障,大致可以分为以下几类:

    事务故障的恢复策略主要是:反向扫描日志文件,查找该事物的更新操作;对该事物的更新操作执行逆操作;继续反向扫描日志文件,查找该事物的其他更新操作,并做同样处理;如此处理下去,直至读到此事物的开始标记,事物故障恢复就完成了。
    系统故障的恢复策略主要是:正向扫描日志文件,找出在故障发生前已经提交的事务,将其事务标识记入重做队列。同时找出故障发生时尚未完成的事务,将其事务标识记入撤消队列;对撤消队列的各个事务进行撤消处理;对重做队列的各个事务进行重做处理。
    介质故障的恢复策略主要是:装入最新的数据库后备副本,使数据库恢复到最近一次转储的一致性状态。装入相应的日志文件副本,重做已完成的事物。

    6 用户手册6.1 安装及配置本系统基于Java开发,要使用本系统在本机上必须安装有Java开发环境,数据库使用Oracle。
    6.2 使用方法登录界面

    主界面

    6.3 基本操作运行程序之后首先进入登陆界面,在登录界面中输入服务名,用户名(数据库的用户名,比如SCOTT),密码。例如我的数据库的服务名是orcl,用户名是CHAN。经过正确的连接即可进入到程序的主界面。

    如下图所示,应用程序界面包括 “切换卡 (用来切换页面)”,“显示区 (显示基本信息)”,“查询区 (用来进行数据查询)”,“控制按钮 (用来进行基本操作)”。

    员工页面,点击打开表格可以查看所有员工的基本信息,关闭表格可以清空页面上的表格显示。

    点击添加信息可以添加某个员工的信息:

    点击修改可以修改某个员工的信息(注意:员工的编号在数据库中是唯一的,被修改的员工的编号在数据库中必须存在)

    点击删除信息可以删除某个员工的信息(同样编号必须存在)

    在部门页面可以通过部门名称查询部门经理的信息:


    在项目页面

    点击查询完成情况可以查询某项目经理(姓名)完成合同的情况

    点击查询设备采购可以某个项目设备采购情况

    在设备页面有一下几个查询:

    7 附录对客户端进行响应的逻辑处理函数大部分通过在客户端中调用数据库的存储过程完成。
    客户端程序逻辑处理的核心代码如下(界面代码在提交的工程中):
    import com.connectdb.ConnectDB;import oracle.jdbc.OracleTypes;import java.sql.*;import java.text.SimpleDateFormat;import java.util.Vector;import java.util.Date;public class ProjectFunction { /** * 添加员工信息 * @param */ public static boolean addWorker(String wId,String dId,String wName,String sex,String age,String date,String dName,String post){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); System.out.println(wId+dId+wName+sex+age+date+dName+post); try { CallableStatement c = conn.prepareCall("{call p_addworker(?,?,?,?,?,?,?)}"); c.setString(1,wId); c.setString(2,wName); c.setString(3,sex); c.setString(4,age); System.out.println("传入的时间是 "+date); c.setString(5,date); c.setString(6,post); c.setString(7,dId); //c.setDate(6,Date.valueOf(date)); //执行Oracle存储过程 c.execute(); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return true; } /** * 删除员工信息 * @param */ public static void deleteWorker(String wId){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); try { CallableStatement c = conn.prepareCall("{call p_delworker(?)}"); c.setString(1,wId); //执行Oracle存储过程 c.execute(); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } } /** * 查询员工信息 * @param */ public static Vector<Vector<String>> queryWorker(String sql){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); ResultSet rs=null; Vector<Vector<String>> list=new Vector<Vector<String>>(); int i=0; try { Statement st = conn.createStatement(); rs = st.executeQuery(sql); System.out.println("执行查询"); // 判断结果集是否有效 while(rs.next()){ // 数据库存在,返回结果集 System.out.println(rs.getString("w_id")); System.out.println(rs.getString("w_name")); System.out.println(rs.getString("sex")); System.out.println(rs.getString("post")); Vector<String> result=new Vector<String>(); result.add(rs.getString("w_id")); result.add(rs.getString("w_name")); result.add(rs.getString("sex")); result.add(rs.getString("d_name")); result.add(rs.getString("post")); result.add(rs.getDate("contract_date").toString()); list.add(i,result); i++; } // 释放此 ResultSet 对象的数据库和 JDBC 资源 rs.close(); // 释放此 PreparedStatement 对象的数据库和 JDBC 资源 st.close(); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return list; } /** * 修改员工信息 * @param */ public static boolean updateWorker(String wId,String dId,String wName,String sex,String age,String date,String dName,String post){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); System.out.println(wId+dId+wName+sex+age+date+dName+post); try { CallableStatement c = conn.prepareCall("{call p_updateworker(?,?,?,?,?,?,?)}"); c.setString(1,wId); c.setString(2,wName); c.setString(3,sex); c.setString(4,age); System.out.println("传入的时间是 "+date); c.setString(5,date); c.setString(6,post); c.setString(7,dId); //c.setDate(6,Date.valueOf(date)); //执行Oracle存储过程 c.execute(); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return true; } /** * 获取员工列表 * @param */ public static Vector<Vector<String>> getWorkerList(){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); ResultSet rs=null; Vector<Vector<String>> list=new Vector<Vector<String>>(); int i=0; try { Statement st = conn.createStatement(); String sql="SELECT * FROM \"worker\""; rs = st.executeQuery(sql); // 判断结果集是否有效 while(rs.next()){ // 数据库存在,返回结果集 Vector<String> result=new Vector<String>(); result.add(rs.getString("w_id")); result.add(rs.getString("w_name")); result.add(rs.getString("sex")); result.add(rs.getString("post")); result.add(rs.getDate("contract_date").toString()); result.add(rs.getString("d_id")); list.add(i,result); i++; } System.out.println("长度 "+list.size()); rs.close(); // 释放此 PreparedStatement 对象的数据库和 JDBC 资源 st.close(); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return list; } /** * 查询部门经理 * @param */ public static Vector<String> queryManager(String dName){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); String wId="",wName="",sex="",age="",post="",contractTime=""; Vector<String> list=new Vector<String>(); try { CallableStatement c = conn.prepareCall("{call P_FDMANAGER(?,?,?,?,?,?,?)}"); c.setString(1,dName); c.registerOutParameter(2, OracleTypes.VARCHAR); c.registerOutParameter(3,OracleTypes.VARCHAR); c.registerOutParameter(4,OracleTypes.CHAR); c.registerOutParameter(5,OracleTypes.CHAR); c.registerOutParameter(6,OracleTypes.DATE); c.registerOutParameter(7,OracleTypes.VARCHAR); //执行Oracle存储过程 c.execute(); wId=c.getString(2); wName=c.getString(3); sex=c.getString(4); age=c.getString(5); contractTime=c.getDate(6).toString(); post=c.getString(7); System.out.println("执行查询"); list.add(wId); list.add(wName); list.add(sex); list.add(age); list.add(contractTime); list.add(post); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return list; } /** * 查询项目设备采购信息 * @param */ public static Vector<String> getEqubyPro(String pName){ Vector<String> result=new Vector<String>(); // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); String eNmae="",eSupplier=""; float eFee; try { CallableStatement c = conn.prepareCall("{call P_GETEBYP(?,?,?,?)}"); c.setString(1,pName); c.registerOutParameter(2, OracleTypes.VARCHAR); c.registerOutParameter(3,OracleTypes.FLOAT); c.registerOutParameter(4,OracleTypes.VARCHAR); //执行Oracle存储过程 c.execute(); eNmae=c.getString(2); eFee=c.getFloat(3); eSupplier=c.getString(4); result.add(eNmae); result.add(String.valueOf(eFee)); result.add(eSupplier); System.out.println("查询设备 "+eNmae+eFee+eSupplier); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return result; } /** * 查询设备采购费用信息 * @param */ public static float getEquFee(String eName){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); float eFee=0; try { CallableStatement c = conn.prepareCall("{call P_GETEFEE(?,?)}"); c.setString(1,eName); c.registerOutParameter(2,OracleTypes.FLOAT); //执行Oracle存储过程 c.execute(); eFee=c.getFloat(2); System.out.println("查询设备 "+eName+eFee+"----"); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return eFee; } /** * 添加设备 * @param */ public static void addEqu(String eId,String eName,float fee,String supplier,String remarks,String pId){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); System.out.println(eId+eName+fee+supplier+remarks+pId); try { CallableStatement c = conn.prepareCall("{call p_addequ(?,?,?,?,?,?)}"); c.setString(1,eId); c.setString(2,eName); c.setFloat(3,fee); c.setString(4,supplier); c.setString(5,pId); c.setString(6,remarks); //执行Oracle存储过程 c.execute(); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } } /** * 查询设备供应商信息 * @param */ public static String getEquSup(String eName){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); String eSupplier=""; try { CallableStatement c = conn.prepareCall("{call P_GETESUP(?,?)}"); c.setString(1,eName); c.registerOutParameter(2,OracleTypes.VARCHAR); //执行Oracle存储过程 c.execute(); eSupplier=c.getString(2); System.out.println("查询设备 "+eName+eSupplier); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return eSupplier; } /** * 查询项目员工信息 * @param */ public static void getWerbyPro(String pName){ // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); String wNmae="",wId=""; float eFee; try { CallableStatement c = conn.prepareCall("{call P_GETEBYP(?,?,?,?)}"); c.setString(1,pName); c.registerOutParameter(2, OracleTypes.VARCHAR); c.registerOutParameter(3,OracleTypes.FLOAT); c.registerOutParameter(4,OracleTypes.VARCHAR); //执行Oracle存储过程 c.execute(); wNmae=c.getString(2); wId=c.getString(4); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } } /** * 查询项目完成情况 * @param */ public static Vector<String> queryComplete(String ManagerName){ Vector<String> result=new Vector<String>(); // 获取数据库连接Connection对象 Connection conn = ConnectDB.getConnection(); String date1="",date2="",pName="",isComplete=""; try { CallableStatement c = conn.prepareCall("{call P_ISFINISHED(?,?,?,?)}"); c.setString(1,ManagerName); c.registerOutParameter(2, OracleTypes.DATE); c.registerOutParameter(3, OracleTypes.DATE); c.registerOutParameter(4, OracleTypes.VARCHAR); //执行Oracle存储过程 c.execute(); date1=c.getString(2).toString(); date2=c.getString(3).toString(); pName=c.getString(4); System.out.println("签订时间是:"+date2); System.out.println("应完成时间是:"+date1); //pName=c.getString(4); Date date; date=c.getDate(2); System.out.println("应完成时间是 "+date1); System.out.println("时间格式 "+date); Date now = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//可以方便地修改日期格式 String hehe = dateFormat.format( now ); System.out.println(hehe); if(date1.compareTo(hehe)<0){ isComplete="已完成"; System.out.println("已经完成"); } else { isComplete="未完成"; } result.add(pName); result.add(date2); result.add(date1); result.add(isComplete); } catch (Exception e) { e.printStackTrace(); }finally{ // 关闭数据库连接 ConnectDB.closeConnection(conn); } return result; }}
    8 总结8.1 遇到的问题Oracle数据库与之前接触过的数据库SQL语句有些不一样,除了常用的增删查改与其他主关系型数据库相似,其他的语句还是有一些区别的。在实习中经常因为在SQL语句中少个双引号或者将双引号写成单引号而出错,所以在刚开始写存储过程中用了不少时间。可见Oracle数据库对语法的要求比较严格。
    8.2 存在的问题
    在这次实习中,我发现自己的数据库设计能力还稍有不足,对项目管理系统的几个表的设计不是很合理,表中有些字段的类型和长度可能与实际应用不相符。在进行数据库设计时应该考虑实际项目中的应用,这样才能设计出满足实际需求的数据库管理系统。
    客户端程序中的程序设计时没有很好地采用架构来实现。使用三层逻辑架构时没有做到完全的层次分离,在表现层中还存在一些业务逻辑层的代码。数据库中每个表都应该用一个类来封装,然后对类中的成员变量也就是数据库表中的每个字段分别设置set()和get()方法。
    程序尚存在一些已知和未知的BUG,反映出自己在程序设计时对问题的考虑不够全面,对于在实际中可能存在的情况没完全把握。

    8.3 收获
    通过完成数据库课程设计,加深了我对数据库理论知识和在实际应用方面的认识,在实践中发现了自己在数据库设计和程序设计上的不足,对于提高自己的编程能力很有帮助。实习中的数据库设计部分可以巩固在实用数据库中的知识,让我学会在实际场景中更好地使用数据库。
    这次实习让我学习了在项目中经常使用到的主流关系型数据库Oracle,掌握Oracle的特征和基本使用方法。在项目中使用Oracle可以实现对SQL语句的封装,不仅提高程序的执行效率还提高了数据的安全性。当对数据库进行复杂操作时(如对多个表进行 Update, Insert, Query, Delete 时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。这些操作,如果用程序来完成,就变成了一条条的 SQL 语句,可能要多次连接数据库。而换成存储,只需要连接一次数据库就可以了。
    6 评论 261 下载 2018-11-05 15:14:59 下载需要14点积分
  • 基于C++实现的语法分析之LL(1)分析法实现

    一、设计目的根据某一文法编制调试LL(1)分析程序,以便对任意输入的符号串进行分析。本次实验的目的主要是加深对预测分析LL(1)分析法的理解。
    二、设计要求程序输入/输出示例:
    对下列文法,用LL(1)分析法对任意输入的符号串进行分析:
    原文法
    E->E+T|E-T|TT->T*F|T/F|FF->id|(E)|num其中: id: a-f, A-F,num:0-9
    消左递归
    E->TA A->+TA A->-TA A->eT->FB B->*FB B->/FB B->eF->i F->(E) F->n其中:i:id, n:num, e:epsilonE->TG
    FIRST集和FOLLOW集




    TA
    +TA
    -TA
    e
    FB
    *FB
    /FB
    e
    i
    (E)
    n




    FIRST
    i,(,n
    +
    -
    e
    i,(,n
    *
    /
    e
    i
    (
    n







    E
    A
    T
    B
    F




    FOLLOW
    $,)
    $,)
    +,-,$,)
    +,-,$,)
    *,/,+,-,$,)



    输出的格式如下

    输入一以#结束的符号串(包括+—*/()i#):
    输出过程如下:





    输入
    输出




    $E
    (a-1)*(3+4/2)+((8*2))$
    E->TA




    输入符号串为非法符号串(或者为合法符号串)
    注意

    表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#
    如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好)
    测试用的表达式可以事先放在文本文件中,一行存放一个表达式,同时以分号分割。同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照

    三、设计说明3.1 需求分析3.1.1 输入及其范围输入为文法,表达式中允许使用运算符(+-*/)、分割符(括号)、字符a。
    3.1.2 输出形式



    输入
    输出




    $E
    (a-1)*(3+4/2)+((8*2))$
    E->TA



    3.1.3 程序功能根据输入的文法进行分析,利用LL(1)控制程序根据显示栈栈顶内容、向前看符号以及LL(1)分析表,对输入符号串自上而下的分析过程。
    3.1.4 测试数据
    输入:文件“fin.txt”输入待分析串
    输出:命令行界面输出预测分析表,LL(1)分析过程输出至“fout.txt”

    3.2 概要设计3.2.1 数据类型的定义// 预测分析表vector<vector<string>> table(5,vector<string>(9));// 消除左递归后的文法产生式vector<string> G; // 文法符号到下标的转换字典map<char, int> index; // 终结符string terminal("in+-*/()$"); // 非终结符string nonTerminal("EATBF"); // 产生式右部符号串的first集vector<string> First; // 非终结符的follow集vector<string> Follow;
    3.2.2 主程序流程
    3.3 详细设计int main(){ for(文法G每个产生式itG,itFirst为其右部符号串的first集) { x = itG左部非终结符号的下标; for(itFirst中的每个终结符号first) { y = 终结符号first的下标; 把itG加入分析表表G[x][y]; } if(终结符号first == epsilon) for(Follow集中的每个符号follow) { y = follow的下标; 把itG加入分析表G[x][y]; } } for(所有非终结符号的Follow集) if(对应表项为空) 写入synch; 将分析表输出到命令行界面; return analysis();}
    int analysis(void){ 从文件fin.txt读取待分析串到s; s末尾加‘$’; 分析栈vector<char> analyStack; 向栈压入‘$’、‘E’; ip指向s的第一个字符; do { top是栈顶符号; cur是ip所指向的输入符号; if(cur是字母) cur = ‘i’; if(cur是数字) cur = ‘n’; if(top是终结符号或‘$’) { if(top == cur) { 从栈顶弹出cur; ip前移一个位置; } else error; } else { x = top对应下标; y = cur对应下标; 产生式production = table[x][y]; if(production非空) { 栈顶弹出cur; 把production右部逆序压栈; 输出production; } else error; }while(top != ‘$’);}
    四、运行结果及分析4.1 测试数据fin.txt文件入字符串:(a-1)*(3+4/2)+((8*2))
    4.2 测试输出的结果
    4.3 输出文件


    4.4 设计和思考主要的难点在于对LL(1)的理解部分,消除二义性、消除左递归、提取左因子,判断是否为LL(1)文法,然后开始整理思路进行编码阶段。开始要对错误的文法进行分析,并提示详细的错误信息。思考之后实现了表达式中允许使用运算符(+-*/)、分割符(括号)、字符a。
    五、总结本次课程设计是本周实验来难点最大的一次作业,首先需要温习LL(1)的知识,如何消除左递归,区别二义性文法,以及对文法的分析。在实验的过程中,最重要的还是要理顺思路,想好解决办法,这也是我经过不断实验总结出的自我思考的方法。然后就进入了编码阶段,此次编码也有一定的难度,在代码量以及代码的整体设计上都有了提升,也是最值得思考的地方。最后,通过实验报告的书写,以及参考资料的查找,对今后的学习和工作都有很大的帮助。
    2 评论 209 下载 2018-11-05 09:29:56 下载需要11点积分
  • 基于JAVA和TCP SOCKET实现的P2P的局域网即时通信系统

    一、设计要求
    掌握P2P原理
    实现一个图形用户界面局域网内的消息系统。
    功能:建立一个局域网内的简单的P2P消息系统,程序既是服务器又是客户,服务器端口(自拟服务器端口号并选定)

    用户注册及对等方列表的获取:对等方A启动后,用户设置自己的信息(用户名,所在组);扫描网段中在线的对等方(服务器端口打开),向所有在线对等方的服务端口发送消息,接收方接收到消息后,把对等方A加入到自己的用户列表中,并发应答消息;对等方A把回应消息的其它对等方加入用户列表。双方交换的消息格式自己根据需要定义,至少包括用户名、IP地址发送消息和文件:用户在列表中选择用户,与用户建立TCP连接,发送文件或消息
    用户界面:界面上包括对等方列表;消息显示列表;消息输入框;文件传输进程显示及操作按钮或菜单

    二、软件开发工具及运行环境
    软件开发工具

    编程语言:Java开发环境:EclipseJDK版本:1.8操作系统:windows 10
    运行环境

    操作系统无关性:Windows、Linux、Mac OS X下安装了Java的运行环境JRE即可运行

    三、程序开发的基础知识3.1 学习Socket和TCP的基本原理和通信机制3.1.1 TCP连接电脑能够使用联网功能是因为电脑底层实现了TCP/IP协议,可以使电脑终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。
    建立起一个TCP连接需要经过“三次握手”:

    第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
    握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”。(过程就不细写了,就是服务器和客户端交互,最终确定断开)
    3.2 SOCKET原理3.2.1 套接字(socket)概念套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
    应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
    3.2.2 建立socket连接建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
    套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

    服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求
    客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求
    连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求

    3.3 SOCKET连接与TCP连接创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
    3.4 主要运用知识
    Java socket编程Java GUI编程Java继承和事件绑定Java异常与捕获
    四、总体设计4.1 设计思路4.1.1 经典的TCP通信服务器客户端架构服务器有一个服务器等待用户连接的线程,该线程循环等待客户端的TCP连接请求。一旦用ServerSocket.accept()捕捉到了连接请求,就为该TCP连接分配一个客户服务线程,通过该消息传递线程服务器与客户端通信。服务器发送消息通过该客户服务线程的方法在主线程完成,而接收消息全部在客户服务线程中循环接收并处理。
    客户机能发起一个向服务器的socket连接请求,一旦收到服务器成功响应连接请求,客户机便为这个socket分配一个消息接收线程,否则关闭该socket。和服务器任务分配类似,发送消息作为非常用方法在主线程中完成,而接收消息在消息接收线程中不停刷新并作相应处理。
    4.2 统一ASCII码级文本传输协议为了实现客户机对服务器命令的响应、服务器对客户机需求的解读以及客户机与客户机之间的消息传递,我为服务器和客户端之间通信定义了一组文本传输协议。协议属于变长文本传输协议,用@作为各字段分隔符,所有消息的首节一定是消息类型,方便解析。协议定义了以下按发送方分类的消息格式:


    4.3 MVC分层模式Model-View-Controller是经典的应用程序开发设计模式,它讲究数据管理、界面显示和用户交互、程序维护管理分别封装在MVC三种类中,够成松耦合关系。本次课程设计中我也利用MVC的设计思路,独立了Model类User用于保存客户机用户信息,DefaultListModel模型类用于储存在线用户队列;将View单独放在一个包中,Controller监听用户操作事件,反映给Model类处理并在View中更新。
    MVC的思想即是M和V之间不要直接产生联系,业务逻辑均封装在MC中,而V仅仅负责显示。本实验为V类绑定了各自的Listener监听用户操作,在C中完成业务逻辑处理,保存并更新User和DefaultListModel,最后再显示到UI界面上。
    4.4 concurrentHashMap管理线程队列和用户列表concurrentHashMap是java.util.concurrent包中定义的多线程安全的哈希表,利用哈希表管理线程队列和用户列表可以快速索引,多线程安全也保证了多个用户服务线程之间共享资源的数据一致性。
    4.5 程序流程图
    4.6 关键数据结构4.6.1 User模型类
    构造方法
    有两个,一个是用独立的name和IP实例化一个User,另一个是用name%IP拼接而成的字符串实例化User
    只读字段
    name和ipAddr均是private的,给他们配置一个只读的getter
    description()用户描述
    返回name%IP拼接而成的字符串,用以代表一个独立的用户

    4.6.2 ServerView类
    UI相关的方法
    构造函数中的initUI()大部分是设置UI界面,其中用到了GridLayout和BorderLayout。用serviceUISetting(false)把所有连接状态才起作用的button和textField全部关闭了(false改为true开启他们,并关闭所有设置相关的button和textField)

    4.6.3 ServerMain类
    startServer()开启服务器方法———startButton绑定
    先检查maxClientNum和port的合法输入,如果不合法弹出出错窗口并退出。接着初始化管理客户服务线程队列的并发哈希表clientServiceThreads,初始化监听客户机连接请求的serverSocket,并且初始化和开启一个监听连接请求的线程。最后有一些差错处理以及服务器log日志
    请求监听线程ServerThread类
    isRunning作为线程运行标志位控制线程存活,线程start后会调用的函数run()里完成了监听逻辑。如果开启则一直循环,serverSocket.accept()是阻塞的,线程不会运行直到有其他线程/进程向其请求Socket连接。这也是我下面提到的一个bug的原因:accept()阻塞了线程它一直在等待,仅仅用标志位来结束线程并不能使之跳出阻塞状态(还没有循环到下一次while的判断),因此我在closeThread()中强行关闭serverSocket会报出一个异常!
    收到连接请求后accept()返回一个socket,这个socket用于和请求连接的客户机通信。至此时TCP建立连接3次握手已经完成,全部被serverSocket类封装起来了。获取了通信socket之后检查服务器在线人数是否已满,向客户机发送一个登陆成功或失败的消息。若在线人数未满连接成功,则为客户机分配一个clientServiceThread线程专门用于发送和接受客户机的TCP包。
    监听客户机消息的ClientServiceThread线程类
    该类比较庞大,我挑重点介绍。
    关键字段
    private Socket socket;private User user;private BufferedReader reader;private PrintWriter writer;private boolean isRunning;
    分别保存了通信socket、当前连接用户Model、绑定在socket输入流上的BufferedReader、绑定在socket输出流上的PrintWriter以及线程运行控制标志位isRunning。reader用来读取客户机消息输入,readLine方法也是阻塞的,直到客户机有消息发送过来。writer有一个写缓冲区,有flush()函数强制发送消息并刷新缓冲区,我把写消息封装在sendMessage(String)中。
    初始化
    初始化中先绑定reader和writer到socket响应流,在判断用户socket请求发送的消息格式是否正确(不正确线程将不能执行)。接着向所有已上线的用户通知一遍这个新用户上线了,发送通知需要遍历整个服务线程队列并发送文本传输协议中定义的各式的通知。注意到这时候该服务线程并没有加入到服务线程队列中,是在初始化完成之后加入的。
    通知了其他用户这个新客户机上线后,再告诉该客户机现在已经有哪些用户在线了,这也是用协议中的格式发送通知即可。这里用到了StringBuffer类,多字符串连接时该类比String的+的效率要高。
    线程run
    收到客户机消息后判断消息类型,若是LOGOUT通知客户机下线,则向所有其他客户端进程发送该用户下线的信息,并删除model类里的该用户对象和线程队列里的该线程。
    如果是消息则交与dispatchMessage(String)方法专门分发消息。
    分发消息方法dispatchMessage(String)
    该方法解析MSG类消息的to字段,根据to字段选择是将消息发给特定用户还是直接群发。发给特定用户的话根据to字段(userDescription)做索引,快速从服务线程队列找出服务该用户客户机的线程来发送信息。
    其他
    绑定时间如stopServer关闭服务器和sendAll群发消息都比较直白便省略介绍,主要需要注意一下其中的差错控制。关闭服务器还需要更新UI控制逻辑。
    说明
    ServerMain类虽然通过ClientServiceThread里的writer发送消息,并且也是调用封装在这个Thread内部类中的,但是调用writer来sendMessage并不是一定在该线程内完成的(该线程内指的是run()里的while循环内部),sendMessage是非阻塞的我们没有必要专门在线程中执行。ClientServiceThread主要工作是收听各个客户端向服务器发送的消息。

    4.6.4 ClientView类Client和Server稍微有点不一样,只有一个辅助线程MessageThread用于接收服务器消息。由于只需要绑定在一个socket上,所以writer和reader只有一个,是直接属于Client实例的字段。

    UI相关方法
    构造函数里的init和Server中几乎完全一样,这部分属于代码复用。注意需要多绑定一个监听器:
    javax.swing.event.ListSelectionListener类用来监听用户选择JList框里的条目,JList框里固定一个所有人的项(点击选中表示消息发送给所有人,默认发送给所有人,目标对象下线后也是自动把对象转变成所有人),其他则是在线用户。点击这些列表项时触发一个选择事件,通过判断index来判断用户的选择,并更新模型记录sendTarget和UI中messageToLabel显示的text。

    4.6.5 ClientMain类
    connect连接到服务器
    差错检测这里没有判断IP地址合法性,判断也不是很麻烦。用户输入合法时,根据服务器IP地址和端口实例化一个socket,这个socket用于将来和服务器通信。获取客户机本地IP地址并用这个IP地址实例化,通过socket给服务器发送一条自己用户信息(name和IP)的消息表示请求。发送完毕后立即开启MessageThread等待服务器的回应。
    MessageThread接受服务器消息线程
    reader.readLine()阻塞读取服务器消息。一直忘记介绍StringTokenizer类,这里说明一下。StringTokenizer类通过一个String和一个分割字符串实例或一个tokenizer,通过分割得到一系列记号流通过tokenizer.nextToken()获取这些记号字符串。不难发现其作用和String.split(String)一样也是做字符串分割,但是其效率显著优于split方法(百度搜索两者比较会有较详细的性能分析)。
    根据tokenizer返回的记号流我们来判断消息类型:
    服务器关闭:向服务器发送一个下线信息,关闭socket, write和read,清空记录Model,最后退出线程。
    服务器错误:log错误类型,啥也不干进入下一轮循环。
    登陆信息:
    成功:log成功,进入下一轮循环。
    失败:log失败,关闭socket, write和read,清空记录Model,最后退出线程。

    五、关键代码 private class ClientServiceThread extends Thread { private Socket socket; private User user; private BufferedReader reader; private PrintWriter writer; private boolean isRunning; private synchronized boolean init() { try { reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new PrintWriter(socket.getOutputStream()); String info = reader.readLine(); StringTokenizer tokenizer = new StringTokenizer(info, "@"); String type = tokenizer.nextToken(); if (!type.equals("LOGIN")) { sendMessage("ERROR@MESSAGE_TYPE"); return false; } user = new User(tokenizer.nextToken()); sendMessage("LOGIN@SUCCESS@" + user.description() + "与服务器连接成功!"); int clientNum = clientServiceThreads.size(); if (clientNum > 0) { //告诉该客户端还有谁在线 StringBuffer buffer = new StringBuffer(); buffer.append("@"); for (Map.Entry<String, ClientServiceThread> entry : clientServiceThreads.entrySet()) { ClientServiceThread serviceThread = entry.getValue(); buffer.append(serviceThread.getUser().description() + "@"); //告诉其他用户此用户在线 serviceThread.sendMessage("USER@ADD@" + user.description()); } sendMessage("USER@LIST@" + clientNum + buffer.toString()); } return true; } catch(Exception e) { e.printStackTrace(); return false; } } public ClientServiceThread(Socket socket) { this.socket = socket; this.isRunning = init(); if (!this.isRunning) { logMessage("服务线程开启失败!"); } } public void run() { while (isRunning) { try { String message = reader.readLine(); // System.out.println("recieve message: " + message); if (message.equals("LOGOUT")) { logMessage(user.description() + "下线..."); int clientNum = clientServiceThreads.size(); //告诉其他用户该用户已经下线 for (Map.Entry<String, ClientServiceThread> entry : clientServiceThreads.entrySet()) { entry.getValue().sendMessage("USER@DELETE@" + user.description()); } //移除该用户以及服务器线程 listModel.removeElement(user.getName()); clientServiceThreads.remove(user.description()); close(); return; } else { //发送消息 dispatchMessage(message); } } catch(Exception e) { e.printStackTrace(); } } } public void dispatchMessage(String message) { StringTokenizer tokenizer = new StringTokenizer(message, "@"); String type = tokenizer.nextToken(); if (!type.equals("MSG")) { sendMessage("ERROR@MESSAGE_TYPE"); return; } String to = tokenizer.nextToken(); String from = tokenizer.nextToken(); String content = tokenizer.nextToken(); logMessage(from + "->" + to + ": " + content); if (to.equals("ALL")) { //send to everyone for (Map.Entry<String, ClientServiceThread> entry : clientServiceThreads.entrySet()) { entry.getValue().sendMessage(message); } } else { //发送给某一个人 if (clientServiceThreads.containsKey(to)) { clientServiceThreads.get(to).sendMessage(message); } else { sendMessage("ERROR@INVALID_USER"); } } } public void close() throws IOException { this.isRunning = false; this.reader.close(); this.writer.close(); this.socket.close(); } public void sendMessage(String message) { writer.println(message); writer.flush(); } public User getUser() { return user; } }
    六、测试6.1 运行ServerMain和ClientMain本程序支持单服务器多客户端,我打开了1个Server和2个Client作为演示。进程开启后界面如下:


    服务器和客户端在未开启/连接的状态下无法发送消息只能修改配置,而在开启/连接状态下无法修改配置只能发送消息,UI逻辑均由开启/连接状态决定。
    服务器配置中可以修改监听端口和人数上限,开启后接收所有来自客户端的消息,模仿解析包动作进行转发,服务器可以群发消息。
    客户端可以设置自己的名字、服务器IP和服务器端口(默认均是192.168.1.154本地IP和端口6666)。客户端连接后可以在在线用户列表中选择所有人或其他在线用户发送消息。其他用户上下线信息在在线用户列表和聊天消息框中都有提示。
    6.2 开启服务器并登陆客户端下图是第二位登陆的用户杨劲登录时,从服务器接受其他用户,显示在左侧在线用户列表中。

    6.3 服务器群发消息
    6.4 客户端群发消息客户端 选择 所有人,并发送消息:

    客户端 收到了消息,并标记为群发:

    6.5 客户端发送私聊消息:客户端 和 客户端 私聊:


    6.6 客户端下线客户端下线时所有在线用户收到其下线消息,在线列表中不再出现此用户:
    客户端 下线,通知服务器,服务器转发其下线消息:

    客户端 收到 下线通知,左侧在线列表中已经没有下线的用户了:

    七、开发过程中遇到的问题及解决办法本次完成这个课程设计还是花了不少精力和时间,由于对于Java网络编程这一块并不是很擅长,开发过程中老是遇到各种大大小小的问题,例如一开始不太清楚tokenizer.nextToken()的意思以及用法, 在参考了一些书籍以及百度各种关于网络编程的文章,才得以顺利完成本次课设。
    八、程序中待解决的问题及改进方向程序可能还存在不少小bug,后期有时间再进行修复。目前发现的一个Bug是服务器关闭时,我关了服务器接收Socket请求的线程并close了该ServerSocket,但是该线程仍然继续执行了一次ServerSocket.accept()。我尝试用了synchronized方法并判断ServerSocket是否关闭,但这个异常还是会出现。我捕捉了该异常,仅仅printStackTrace而没有做其他错误处理,幸运的是这小Bug并不影响服务器关闭,所有客户端都能正确的接收服务器关闭的消息。希望以后有时间能把这个错误给改过来。
    6 评论 146 下载 2018-11-29 09:34:27 下载需要13点积分
  • 基于C++实现的运动会统分系统

    一、需求分析本系统主要是运动会分数统计方案设计。运动会分数统计方案适合采用结构体数组,为了实现系统功能,主要应实现以下几部分:比赛成绩输入、比赛成绩输出、查询比赛成绩和调用统计结果,进入菜单界面后,需要输入学校编号,项目编号,取得的名次个数,以及哪些名次,并且应该提供键盘式选择菜单实现功能选择。由于运动会分数统计需要处理大量的数据,所以在运行期间,为了避免在运行大量数据时出错,并且系统能够在很短的时间内将运行结果稳定准确输出,就需要系统达到安全性能好,可靠性高,稳定性强,处理数据迅速等特点。
    程序执行命令包括:

    设置运动会相关参数
    输入比赛项目名称和学校名称
    输入各学校比赛成绩以及学校成绩的查询操作

    测试数据:

    二、概要设计按照课题要求,在设计时将本系统分为输入项目成绩、查看学校成绩、查看项目成绩、输入学校和项目名称、设置几个功能模块,并且将对录入的分数按照各项成绩得分以及团体总分排序。系统定义数据时使用结构体和结构体数组来存储信息数据,输入基本信息后由系统统计总分的内容并全部存入文件file中,在排序输出中使用归并排序进行不同关键字的排序,查询函数采用顺序表的查找来完成。
    2.1 学校的抽象数据类型ADT School{ 数据对象:D={ai|ai(-SchoolSet,i=1,2,3,…,n,0<n<=20} 数据关系:R1={<ai-1,ai>|ai-1,ai(-D,i=2,3,…n} 基本操作: firstUsed(&S) 操作结果:将学校结构体中的数据置空,清零。 saveToFile (&S) 操作结果:将学校结构体中的数据存储到文件中。 readFromFile(&S) 操作结果:将文件中的学校结构体数据读取到内存中。 merge_sort($S,option) 操作结果:将学校结构体数组按照条件排序。 schoolInput(&S) 操作结果:将学校信息输入到学校结构体中。 showSchool(&S) 操作结果:将学校结构体中的数据输出。}ADT School
    2.2 比赛项目的抽象数据类型ADT Sport{数据对象:D={ai|ai(-SportsSet,i=1,2,3,…,n,0<n<=20} 数据关系:R1={<ai-1,ai>|ai-1,ai(-D,i=2,3,…n} 基本操作: firstUsed(&S) 操作结果:将比赛项目结构体中的数据置空,清零。 saveToFile (&S) 操作结果:将比赛项目结构体中的数据存储到文件中。 readFromFile(&S) 操作结果:将文件中的比赛项目结构体数据读取到内存中。 inputScores (&S) 操作结果:将比赛项目信息输入到学校结构体中。 showSports (&S) 操作结果:将比赛项目结构体中的数据输出。}ADT Sport
    由于本程序中的学校和比赛项目数据类型呈平行关系且他们在程序运行期间反复使用,故设计为全局变量,减少传参导致系统性能的降低;又由于二则的操作几近相同,故把二者的操作放在一起,同时执行,因此,抽象数据类型的设计与代码优化后的结果略有差异。
    2.3 本程序包含六个模块2.3.1 主程序模块Void main(){ 初始化; While(1){ 接收命令; 处理命令; If(退出)break;}
    2.3.2 输入项目成绩模块实现对运动会比赛成绩的输入操作。
    2.3.3 查看学校成绩模块实现对学校的比赛成绩的排名查看。
    2.3.4 查看项目成绩模块实现对项目的成绩查看。
    2.3.5 输入学校和项目名称模块实现对学校和比赛项目的名称输入。
    2.3.6 输入学校和项目名称模块实现对学校和比赛项目的名称输入。
    2.4 模块调用图
    三、具体设计3.1 头文件声明和全局变量定义#include <stdio.h>#include<windows.h>#include<stdlib.h>#include<conio.h>#include<string.h>#define default_num 3 ///默认获奖名次数目,取3或5int n=20; ///最大学校数目int m=10; ///最大男子项目数目int w=10; ///最大女子项目数目School schools[25]; ///学校结构体数组School temp[25]; ///用来缓存排序列表学校数据School temp1[25]; ///排序缓存数组Sport sports[50]; ///比赛项目结构体数组int three[3]={5,3,2}; ///获得前三名的学校积分int five[5]={7,5,3,2,1}; ///获得前五名的学校积分
    3.2 函数声明///从数据文件读取存有的运动会成绩信息void readFromFile(); ///将输入到内存中的数据存到文件中void saveToFile(); ///程序文件数据清零void firstUsed(); ///初始化操作,在此文件操作,进行打开程序的读取初始化操作void initialization(); ///按照不同的参数对学校数组进行排序,采用归并排序void merge_sort(School temp[],int st,int en,int option,School temp1[]); ///输入学校成绩并统计void schoolInput(int schoolnum,int award_num,int i,int pos); ///输入项目成绩菜单void inputScores(); ///输出学校菜单void showSchool(); ///输出查找项目菜单void showSports(); ///输入学校和项目名称菜单void InputName(); ///设置菜单void setting(); ///主菜单的操作void menu();
    3.3 数据结构定义3.3.1 运动项目数据表运动会系统先制定本次运动会所需的参赛项目。本数据表根据要求设计存储每个项目的编号、项目名称、要取的名次、各个名次获奖学校。用于对以后项目情况的统计已及查询。其中项目编号直接用运动项目数组下标+1表示,name和win_school由输入信息输入,award_num由设置选项输入。
    typedef struct Sport{ char name[20]; ///项目名称 int win_school[5]; ///获奖前三或前五学校的编号 int award_num; ///这个运动项目取名次的数目,取3或5} Sport; ///存放项目信息Sport sports[50]; ///定义运动项目数组
    3.3.2 学校数据表本数据表根据要求储存了各个参赛学校的总体情况,包括学校的编号、学校名称、各个项目得分、学校总分、男子团体总分、女子团体总分。其中学校编号由学校数组下标+1表示,学校名称,各项目得分由输入信息输入,学校总分,男子团体总分,女子团体总分由系统自动统计。
    注:学校总分,男子团体总分,女子团体总分存储在一个长度为3 的数组当中,作此设计是为了实现对学校的按照特定参数排序操作中,用传入一个下标参数的形式实现对排序条件的控制。
    typedef struct School{ int total_score[3];///下标0表示学校总分,1表示男子总分,2表示女子总分,方便之后的排序输出而存在数组中 char name[20]; ///学校名称 int scores[50]; ///各个项目得分} School;School schools[25]; ///定义学校数组
    四、运行结果4.1 主菜单界面
    4.2 设置运动会相关参数
    4.3 输入学校和项目名称
    4.4 输入运动会比赛成绩
    4.5 查看学校运动会成绩
    4.6 查看项目比赛成绩
    五、调试分析
    学校数组和比赛项目数组的数据一开始出现不同步的现象,两个数组之间的数据没有对应关系,这是导致一开始程序运行结果与预期结果不一致的主要问题
    对于数据文件的储存,刚开始没有考虑程序执行一半退出系统前要先把数据保存再退出,导致操作数据丢失。对于文件操作,文件的内容及时保存是避免这个错误的可靠方法
    本程序的模块跳转之间的文件保存、跳转之间的数据传递、模块跳转的合理性也是一开始设计上不足所在,这些问题经过调试也得到解决,因此,在以后的设计中,应该要把细节方面考虑清楚,减少对问题的考虑疏忽导致程序产生难以找到的错误
    关于中英文的字符串长度和对齐问题:经过调试得知中文一个字符的长度是2,这在学校成绩输出表格的对齐问题上有重要作用
    程序代码的时空分析:由于本程序中的学校数组和比赛项目数组长度都是常数阶,故本程序的时间复杂度为O(1),空间复杂度也为O(1)

    六、程序数据的文件存储形式本程序将程序数据分为三个txt文件保存,分别是schools.txt, sports.txt, setting.txt。分别保存学校数组,运动项目数组和设置参数的数据。其中学校数组和运动项目数组按照结构体的成块储存,每块保存着单个结构体的所有参数数据,而设置参数则按照设置菜单的顺序直接存储各个参数。该储存结构可通过附件查看得到。
    七、关于非法数据的输入在本程序中,不论整体非法数据还是局部非法数据,在数据的输入过程中对数据直接进行检测,只要遇到一个非法数据,即提醒用户重新输入,知道输入的数据合法为止,例如以下情况:

    八、用户使用说明本程序的运行环境为Code::Blocks13.12,执行文件为SportsMeeting.exe,在进入该程序软件打开文档,然后对本程序进行调试,调试完毕,用户可根据需求从主菜单选择相应的功能,调用功能函数对用户需求进行单独运算。
    本程序通过对实际操作的考虑,设计成在程序执行前先进行对运动会相关参数的设定,再输入学校、项目名称,最后再输入成绩进行分数统计,其中,参数设定必须先执行,之后的操作可由用户喜好决定。
    1 评论 55 下载 2018-10-31 11:14:43 下载需要11点积分
  • 基于java的Ping程序的设计和实现

    一 需求分析
    已知参数:目的节点IP地址或主机名
    设计要求:通过原始套接字编程,模拟Ping命令,实现其基本功能,即输入一个IP地址或一段IP地址的范围,分别测试其中每个IP地址所对应主机的可达性,并返回耗时、生存时间等参数,并统计成功发送和回送的Ping报文

    初始化WindowsSockets网络环境
    解析命令行参数,构造目的端socket地址
    定义IP、ICMP报文
    接收ICMP差错报文并进行解析

    程序实现主要用到Java网络包中的类InetAddress

    二 程序设计2.1 设计思路ping 程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。ping 使用的是ICMP协议,它发送ICMP回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在一定时间内收到应答,则认为主机可达。
    ICMP协议通过IP协议发送的,IP协议是一种无连接的,不可靠的数据包协议。因此,保证数据送达的工作应该由其他的模块来完成。其中一个重要的模块就是ICMP(网络控制报文)协议。
    当传送IP数据包发生错误,比如主机不可达,路由不可达等等,ICMP协议将会把错误信息封包,然后传送回给主机。给主机一个处理错误的机会,这也就是为什么说建立在IP层以上的协议是可能做到安全的原因。ICMP数据包由8bit的错误类型和8bit的代码和16bit的校验和组成。而前16bit就组成了ICMP所要传递的信息。
    PING利用ICMP协议包来侦测另一个主机是否可达。其原理是用类型码为0的ICMP发请求,受到请求的主机则用类型码为8的ICMP回应。ping程序来计算间隔时间,并计算有多少个包被送达。用户就可以判断网络大致的情况。
    本程序使用java语言来实现ping的功能,可以有三种方法:

    第一种是用Java 1.5,java.net包中的InetAddress实现ICMP ping的功能,虽然代码简单,但不可靠,在Linux系统下会不可靠
    第二种是使用java调用cmd命令,这种方式最简单,可以把ping的过程显示在本地
    第三种也是使用java调用控制台的ping命令,具体的思路是这样的:通过程序调用类似“ping 127.0.0.1 -n10 -w 4”的命令,这命令会执行ping十次,如果通顺则会输出类似“来自127.0.0.1的回复: 字节=32 时间<1ms TTL=64”的文本(具体数字根据实际情况会有变化),其中中文是根据环境本地化的,有些机器上的中文部分是英文,但不论是中英文环境,后面的“<1ms TTL=62”字样总是固定的,它表明一次ping的结果是能通的。如果这个字样出现的次数等于10次即测试的次数,则说明127.0.0.1是百分之百能连通的。技术上具体调用dos命令用Runtime.getRuntime().exec实现,查看字符串是否符合格式用正则表达式实现。

    本程序选用的是使用第三种方法。
    2.2 系统结构流程
    三 程序实现3.1 开发环境
    操作系统:Windows 8
    编程环境:Eclipse
    编程语言:Java

    3.2 程序架构
    3.3 关键代码public Ping(String ip){ this.ip=ip; runtime=Runtime.getRuntime();// 将要执行的ping命令,此命令是windows格式的命 pattern=Pattern.compile("(\\d+)ms\\s+TTL=(\\d+)",Pattern.CASE_INSENSITIVE); // 通过 compile 静态方法实例化 Pattern 对象 ,构造用来匹配耗时和TTl的正则表达式 ttl=0; usage=0;}public boolean ping(){ try { //直接调用系统的ping命令,求要发送的回显请求数和等待每次回复的超时时间(ms) Process p=runtime.exec("ping "+ip+" -n 1 -w "+TIME_OUT); if(p==null)return false; //从键盘接受一行输入的ip地址或IP段 BufferedReader buff=new BufferedReader(newInputStreamReader(p.getInputStream())); String line; Matcher m; while((line=buff.readLine())!=null){ m=pattern.matcher(line); if(m.find()){ usage=Integer.parseInt(m.group(1)); ttl=Integer.parseInt(m.group(2)); return true; } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false;}
    四 运行测试打开程序
    点击文件夹中的ping.jar文件,打开即为ping程序的界面:

    输入数据
    在请输入IP或IP段右边的文本框中输入您要Ping的IP地址或者IP地址段或者主机名:

    运行程序
    点击Ping按钮运行程序,会在文本域中输出相应的结果:

    退出程序
    点击程序右上角的红叉,关闭程序。
    五 参考文献[1] 《计算机网络》,谢希仁,电子工业出版社
    [2] Andrew S. Tanenbaum,《ComputerNetworks》,潘爱民译,清华大学出版社
    [3] 计算机网络课程设计, 吴功宜 ,机械工业出版社
    [4] 《计算机网络课程设计》,王勇等编著,清华大学出版社,2009-06
    [5]《网络编程技术及应用》,谭献海,清华大学出版社
    4 评论 40 下载 2018-11-05 09:08:41 下载需要12点积分
  • 基于Python的Pygame库实现的仿微信游戏中的飞机大战小游戏

    1 游戏简介不知大家是否还记得当时微信上风靡一时的打飞机小游戏,通过控制我方飞机的上下左右移动,发射子弹来击毁敌机,增加得分。这是一款简单操作易上手又很有趣味性的游戏,我使用python作为基本语言,利用pygame仿照微信版本完成了这款低配版飞机大战游戏。
    2 程序功能我方飞机会按时的不断发射子弹,玩家通过上下左右的方向键来躲避迎面而来的敌机,并利用子弹消灭他们。敌方分为小中大三种机型,有不同的飞行速度和生命值,小飞机一发子弹消灭,得分1000,中飞机八发子弹消灭,得分3000,大飞机十六发子弹消灭,得分8000,分数显示在屏幕的左下角。我方飞机有三条命,每次新生的飞机有三秒的安全期,游戏结束后,屏幕会显示“Game Over”字样和玩家的最终得分。
    3 设计思路3.1 主函数先写主函数,引入pygame模块,初始化游戏,设置屏幕大小和标题,将背景图片载入,编写主函数,用screen.blit函数将背景图拷贝到屏幕上,再显示到窗口上,效果如下所示:

    3.2 我方飞机模块再写我方飞机模块。

    建立类MyPlane,继承自Sprite类,初始化基类,加载图片,获得飞机所在的矩形模块,设置游戏开始时飞机所在的初始位置(预留下方状态栏)及每次按动方向键飞机移动的像素。定义飞机移动的四个方向函数,确保在移动的过程中不会超过边界。修改主函数,先导入mainplane模块,在主函数中生成我方飞机并绘制,然后导入key_pressed模块,检测用户按下了方向键的哪个按钮并调用MyPlane类的move函数做出对应反应。
    效果如下图所示:



    效果1
    效果2









    3.3 敌机模块继续写敌机模块。

    分为三类,小型、中型和大型敌机,全部继承自Sprite类,同我方飞机进行类似定义,不同的是敌机的生成是随机的因此要引入random模块,小中大型敌机的出现频率和移动速度不同,其余的进行复制修改操作。然后改写主函数。创建一个包含三类敌机的数据组,再分别创建三个不同类型的敌机组,编写三个函数,分别实现生成敌机并将其加入总机组和对应敌机组的操作,不同类型的敌机每次生成的个数不同。为了不让敌机之间进行覆盖操作,应该按大中小的顺序进行敌机的绘制,直接将对应类型机组中生成的飞机按顺序生成绘制即可。
    效果如下图所示:



    效果图1
    效果图2









    3.4 碰撞检测给我机和敌机四种对象建立destroy列表,用list储存每个对象爆炸的四个图像。给每个对象赋予active属性,判断是否还存活,若碰撞则依次播放毁灭画面,之后重新在顶部生成。
    然后进行碰撞检测的相关代码实现,调用Sprite模块中的spritecollide函数,传递我机和敌机组两个动画精灵,碰撞后返回敌机组中发生碰撞的敌机列表。若列表值不为空则我方飞机和碰撞敌机的状态都为死亡,播放毁灭动画,效果如下图所示:



    效果图1
    效果图2









    紧接着发现,我机和敌机的碰撞范围是图片所在的矩形框,要进行完美的碰撞检测需要用到sprite模块中的from_surface函数,它会将图片中非透明的部分返回为一个mask值,我们直接用每个对象的mask来进行碰撞检测,在主函数中调用pygame的collide_mask函数即可。
    3.5 发射子弹给我机装备上子弹。初始化普通子弹类时需传入参数表示子弹的发射位置,与我机敌机相类似,在类中定义子弹的图片,速度,是否存活,移动方式等属性,随后在main文件中导入bullet模块,先生成空的子弹组,需要每次装填四颗子弹,用append函数向子弹组中添加Bullet类,在飞机的顶端中部生成子弹。为了控制飞机的发射子弹速度和射程,需要每10帧发射一次子弹,利用delay每帧自减操作来实现,随后判断子弹是否碰撞敌机,与上步骤类似,利用mask值来完成检测并绘制相应的毁灭操作,效果如下图所示:



    效果图1
    效果图2









    为了区分小中大敌机,让他们不会都被1发子弹击毁,需要给敌机设置一个energy属性,表示他们的血量。主函数中,如果击中的是中大型敌机,那么energy减一,当energy为0时敌机被消灭,若击中小型敌机,则直接毁灭。
    3.6 生命值显示接下来实现分数和剩余生命值的显示。规则为击中小型敌机1000分,中型敌机3000分,大型敌机8000分,字体调用render函数,会自动将字符串渲染为一个pygame的surface对象,并将其显示在屏幕上。设置一个为life的变量,其中有加载表示生命的图标,表示生命的数量,我机每次毁灭时生命数会减一,生命值为0时游戏结束,效果如下所示:



    效果图1
    效果图2
    效果图3










    3.7 游戏难度调节为了增加游戏的挑战性和趣味性,给游戏设置了难度递增的环节,根据用户的得分,敌机的出现频率和移动速度都会有一定程度的改变。直接调用生成敌机时定义的add函数在组中加入对应数量的敌机,并编写定义增加敌机速度的函数,在难度上升时加快敌机的运行速度,图片效果显示不出太大差异,但可以大概看出敌机数量增加:

    3.8对游戏进行了美化和优化。增加了敌机坠毁,我机坠毁,子弹发射,游戏升级等的游戏音效,优化了敌机和我机运动画面等,提高游戏颜值,游戏基本完成。
    4 总结因为在此前完全没有接触过python语言的相关知识,所以一切都得从最根基的地方开始学起。因为有了C语言作为打底基础,于是用了将近三天的时间成功初步掌握了python的基本编程语言要求和语法。与C不同的是,python是根据语句块的不同缩进来进行代码的执行和判断,这就导致平时编程习惯较差,不注意语句代码规范的我吃了很大的亏。在基本了解了python语法的基础上,得知python为游戏开发项目提供了专门的库pygame,在下载安装好pygame库并且在对比鉴别了网上的各种游戏代码后,确定了自己的选题——伪微信版飞机大战。
    Pygame为用户提供了非常多简便的函数和模块,在跟着游戏教程一点点学习制作的过程中收获了很多乐趣和知识,跟以往学习风格都不同的自学过程,有点不知所措,有点手忙脚乱,但是在得到最终成果的时候,却是无与伦比的骄傲与成就感。
    3 评论 211 下载 2018-10-06 21:56:06 下载需要15点积分
  • 基于Qt和sqlite数据库实现的银行管理系统

    一、项目技术路线说明本系统主要是使用了Qt开发所需的基础知识,用qt自带的环境来设计ui界面。首先用到了sqlite来建立数据库,建立三张表分别存放用户信息,管理员系统,用户冻结的账号信息。使用全局变量存放当前用户名,以方便管理用户信息。
    使用结构体存放要读取或者读入的数据,使用vector容器以动态数组形式来存放读取到的多组数据。使用QProgressDialog 来显示进度条效果。窗口之间的转换是使用了show和hide函数来实现的。还用到了sql与qt连接的基本操作,读取,插入,更新,删除…对于余额的储存采用了double的方式,但是由于text文本框不支持数字的写入,所以采用了QString::number()、QString().arg的方式来实现数字到字符串的转换和数字的写入。对于用户的挂失操作,解决方法是将用户信息表中的改用户信息删掉,然后将该信息写入到另一张专门记录挂失账号的表中,实现冻结操作,当用户要解冻时,再反向改回数据。
    二、项目需求分析2.1 项目介绍这是一个银行管理系统,主要面向对象是客户们。集成了用户的登录注册,忘记密码,存款,取款,转账,查看余额,查询账户明细(可以查看到用户的基本信息以及存款、取款、转账记录、时间),销户,挂失(冻结账号),解冻账号等功能。以及管理员的登录注册,还有跟用户差不多的基本功能。
    2.2 功能需求
    需要登录界面,注册(也就是客户开户)界面,找回密码界面。实现用户登录需求
    需要一个功能界面,实现用户的存款,取款,转账,查看余额,查询账户明细(可以查看到用户的基本信息以及存款、取款、转账记录、时间),销户,挂失(冻结账号)等的功能
    需要一个帮助菜单,在登录页面为用户提供“关于我们”的信息,还有忘记密码和解冻账号的操作

    三、系统分析与设计3.1 本程序需解决的关键技术问题
    窗体间的互相传值,这里采用的是定义全局变量解决的
    sql与qt的连接交互,将数据用数据库进行操作,存储等
    数据库多组数据的读取,这里采用了结构体加vector容器的方式来实现的
    账号的挂失问题,这里采用了两张表存数据的方式解决冻结和解冻问题

    3.2 程序流程
    3.3 功能模块3.3.1 存款功能
    3.3.2 取款功能
    3.3.3 转账功能
    3.3.4 账户明细
    3.3.5 修改密码
    3.3.6 挂失功能
    3.3.7 解冻账号
    四、程序设计与实现4.1 工程文件组织结构









    目录1
    目录2
    目录3



    4.2 程序设计
    Loginwin类:该类用来设计登录界面和登录的验证等操作,里面还包括了一个帮助菜单,有找回密码和解冻账号的功能,同时登录界面通过该类还可以跳转到注册界面
    userRegister类:该类是用来进行客户账号的开户功能,可以随机生成一个账号,并保证该账号和用户名不重复,并把客户输入的数据存到用户信息的表中
    manageRegister类:该类就是生成管理员的注册界面,实现管理员账号的注册
    userWin类:这个是展示用户功能的界面,并使用槽函数让用户点击时可以跳转到相应的界面,大致功能有存款,取款,转账,修改密码,注销,挂失,销户,查询账户明细等
    manageWin类:大致和客户界面的功能差不多,只是用法稍有区别
    deposit_money类:该类是用于客户存款的,设计的方法就是将用户输入的金额与用户原余额进行相加,然后再将总额存入用户的余额,从而实现了用户的存款操作
    get_monry类:该类就与deposit_money正好相反,将原金额减去用户输入的金额,如果结果小于0则报错并提醒,反之则取款成功,并将结果更新原来的数据
    transfer_money类:该类是用于客户转账操作的,设计思路是将该用户的余额减去用户输入的要转账的金额,如果不小于0则更新到原信息表中,并且对要转入的账号进行操作,将要转入的金额与要转入的账号的余额进行相加,并更新到信息表中
    lookWin类:该类是客户用于查询账户明细的,用户输入用户名和密码后,界面左边会显示用户的基本信息,右边则会展示用户的存款,取款,以及转账的记录和时间
    destroy类:该类是用于客户进行销户操作的,设计思路是将用户信息表中的该用户的信息删掉
    revise_password类:该类是用于客户来进行修改密码操作的,设计思路是根据用户输入的信息找到该账号的信息了,然后将该账号的密码修改并更新原表中的数据
    report_card类:该类是用于客户在银行卡遗失的情况下,防止卡内信息丢失,采取的冻结账号的功能。,当信息输入正确,并点击冻结账号后,用户信息表中的该用户信息就会被删掉,并把该账号的所有信息存到另外一张表中
    unfreeze_card类:该类是用于客户来进行解冻账号操作的,当信息输入无误并匹配,就会激活解冻账号操作,该用户的信息将会被恢复

    五、系统测试5.1 测试概要
    测试工具:Qt creater
    测试时间:60分钟
    测试目的:测试该系统的所有功能能否按照预期进行

    5.2 测试环境与配置


    CPU
    英特尔Core i5-4200@1.80Hz 双核




    内存
    8GB


    硬盘
    1TB


    网络配置
    因特网


    运行环境
    windows 10


    开发工具
    Qt creater



    5.3 测试方法和工具
    测试方法:对项目文件直接编译运行,输入数次不同的样本数据以运行系统,记录数据后对数据进行分析,并与预期结果进行比较,如出现偏差则找出错误源头并对源代码进行修正,如无误差则多次重复以保证正确率,当出现看不懂的bug时,通过百度谷歌找到解决方案
    测试工具:Qt creater

    5.4 系统功能分解
    用户登录注册功能
    管理员登录注册功能
    首页的帮助功能
    用户主界面的九大功能
    管理员主界面的基本功能

    5.5 测试内容5.5.1 功能性测试用户登录界面

    用户注册界面

    用户主界面

    存款界面

    取款界面

    转账界面

    账户明细

    修改密码

    挂失功能

    解冻账号

    5.5.2 性能测试压力测试以及稳定性测试过程中无异常发生,一切正常。
    1 评论 3 下载 2020-06-25 12:42:07 下载需要16点积分
显示 60 到 75 ,共 15 条
eject