2020年秋冬学期计算机学院大三的专业选修课Java 应用技术笔记、总结、报告
爬取了http://www.yuedu88.com/longzu1/ <龙族一>的链接, 下载该网页中所有链接的指定内容,去除广告等无关内容,组合成单一文件。主要作广度搜索,深度暂为1.
多线程和并行程序设计 30章 demo代码 在文件 TaskThreadDemo.java中, 可以用来学习
Main 方法: 给定url,使用时修改url,即可爬取不同网站的小说.调用spiderURL方法获得每一章的url, searchThroughUrlFile方法搜索并处理
spiderURL方法:爬取该页的所有html写入Spider_content文件, 正则提取目录链接到Spider_URL文件.
searchThroughUrlFile方法:
然后从Spider_URL文件读取url, 每一行读入tempStr,如果有http://www.yuedu88.com/longzu1/的前缀,那么很可能就是需要的url, 那就访问它爬取html,同时三个写入:
①直接写入Spider_all文件,
②string处理后写入Spider_simple文件,
③调用readTextAndLinkAndTitle方法写入Spider_ByParaser文件
如果不是,那就下一行.
每一行都读完了,那就关闭其他文件,关闭网络.
readTextAndLinkAndTitle 方法:利用htmlparser来解析html文本,然后写入给定的文件中.
- 内容不对 -> 仔细查看具体内容, 是被覆盖了还是追加. 多尝试, 是否加一个回车符就可以显示的更好.
- 异常有问题-> 有很完整的异常栈, 就关键字查询异常,或者问老师问助教问同学. 还有就是把可能出错的地方注释掉, 看具体是哪里有问题
1、.htm/.txt/.java文件:原样文本传送。 2、.jzup:在第一次访问时将文件转化成"<文件名>_jzup.java",手工或自动编译成相应的类,传送该类的执行结果。以后则直接传送"<文件名>_jzup.class"的运行结果。(类似Tomcat,.jzup参考.jsp的语法) 3、其它文件按二进制传送。 (拓展是asp jsp ,就特殊处理, 其他拓展名,就传输字节流.) 4、文件不存在:按html协议发送404消息。(直接发送错误信息就可以.)
首先,写httpserver 类, request封装请求信息的类, response响应请求的类, 第二步 在webroot中创建ajax.html 文件, 这个网页上有一个button,当点击这个 button 的时候,访问了服务端的 ajax_info.txt. 第三步 hello.jsp 利用 method="post" , 可以输入账号密码,账号密码不会直接显示在地址栏,可以发送到服务器,密码在服务器端显示.服务器发送login.jsp给客户端,未解析jsp
Java的数据库操作有三重境界:
- 直接数据库操作,数据库连接、SQL语句等。
- 对应数据库中的表建一个类,以类的方式隔离数据库操作。
- 对于任意指定的表自动生成相应的类的Java程序。实现基本的数据库查询、显示与增、删、改等常规操作。 1)对数据库的直接操作;建立连接、SQL语句等。12.15 2)对指定表建一个类,以类的方式实现对数据库操作的隔离。12.16 3)对于任意指定表可自动产生相应的类的程序[程序2)]。12.18 -12.19 4)1到2个对于不同的表,由3)产生的自动类实例。为其它程序员提供数据库编程的工具的程序。
数据库是固定的, 就先建立链接,sql 选择不同的表, 然后提取表的metadata元数据即可建立对应的类.vector 存储people类, 每个类存储一行的数据. 在12.16日完成通过类的方法进行插入操作
完成对应表产生java文件并自动编译成类. 产生2个自动类实例.
对于任意指定的表自动生成相应的类的Java程序。实现基本的数据库查询、显示与增add()、删delete()、改setxx()等常规操作。
自动生成类,可以删除, 插入,显示. 可以自动产生较复杂的sql语句.
完成多输入的类生成.
本课程设计选题为“网上聊天室”。
系统主要功能是服务器转发信息,实现
①登录, 根据数据库读取信息, 判断密码是否正确
②注册用户加入到数据库.
③登录后通知其他客户端
④离开后通知其他客户端 ⑤帮助指引,便于使用者更快地掌握操作方法。
基于 javafx 的图形界面绘制,包括文本框\密码框\按钮等组件, 所有用户需输入账号、密码登录进入系统;进入系统后可查看其他用户是否在线, 和其他用户发信息.
1.Adapter
类适配器----我们使用Myclient来调用实现好的FangTclient。修改FangTclient代码可能会出现无法意料的错误。这时我可以用Adapter模式去适配FangTclient, 通过Adapter模式,我可以继续多态地使用接口,而避免因修改封装好代码而导致的错误, 不用考虑是如何实现displayIt()的; 保证绝对不修改现有的类,只是在外面加一层代码.
2.对象适配器---委托模式
把各个按钮的处理交给各个handler类的实例,而不是start中创建匿名类来处理,有利于代码可读性.
3.Singleton模式
保证任何情况下只有一个服务器实例.不会不小心用new创建实例,定义了获取唯一一个实例的static方法.如下所示
private static** FtServer *ftServer* = **new** FtServer();
高并发怎么办?哪里会先出问题?
如果你的系统内处理的是较为复杂的一些业务逻辑,是那种重业务逻辑的系统的话,是比较耗费CPU的。
遇到的困难和解决问题的方法
-
用tcp实现, 客户端发送信息可以通过发送按钮实现主动控制, 但是对于输入流中有多少信息是不可控的,接收信息怎么不会卡住?
如果服务器在一个客户端连接成功后,并没有一条信息发送给客户端,客户端的读取欢迎信息的语句无法读取到内容,就被阻塞住,由于是单线程,甚至整个程序都会被卡住。
多个客户端也没办法同时通讯.
解决方法: 我们要让服务器和客户端互相约定通信规则,否则就可能有问题,Thread 类来实现客户端多线程接收, runnable 类实现run方法 ,当登录成功的时候 我们用线程池, 维护若干个线程, 有新任务,分配一个空闲线程. 我们并发小, 可以采用动态调整线程池, 实际中, 线程用光了, 可以用阻塞队列,阻塞队列满了,可能直接拒绝.否则会out of memory OOM 把服务端和客户端通信的功能由一个线程来处理,就不会阻塞主进程的执行.
-
代码太长了 , 一开始是用匿名类, 在start()方法中注册注件处理程序。事件处理程序接口的实现.例如btRegst.setOnAction(new BtnRegstHandler());
class BtnRegstHandler implements EventHandler{}, 在类中重载handle.
-
怎么保证等待的时候, 客户端发送登录信号, 服务器可以正确接收?
用http协议, 可以用java后台给指定接口发送json数据.
用TCP也很简单,信号先行.任何时候他收到的都是信号. 两边约定好, 第一个发送的就是信号.
方法: 服务器run(), writeInt(1); 告诉用户1可以开始了.
然后while(true){
接收信号 ,如果是login
用户名 = datainput.readint, 密码 = datainput.readString();
服务器检查用户名和密码,
登录成功给他writeint(continue) 信号.break出去.
否则等待下一个信号
}
下面就是
while( !socket.isClosed() && fromClient != null) {
就一直收发.
}
我的处理是登录后,while(), 互相发送字符串.
这里有几个点要注意, 第1个问题是要正确区分长、短连接。所谓的长连接是一经建立就永久保持。短连接就是在以下场景下,准备数据—>建立连接— >发送数据—>关闭连接。要判断啥时候用长连接,啥时候用短连接.
第2个问题是对长连接的维护。所谓的维护包括两个方面,首先是检测对方的主动断连(既调用 Socket的close方法),其次是检测对方的宕机、异常退出及网络不通。这是一个健壮的通信程序必须具备的。检测对方的主动断连很简单,主要一方主动断连,另一方如果在进行读操作,则此时的返回值-1,一旦检测到对方断连,则应该主动关闭己方的连接(调用Socket的close方法)。
而检测对方,通常方法是用“心跳”,也就是双方周期性的发送数据给对方,同时也从对方接收“心跳”,如果连续几个周期都没有收到对方心跳,则可以判断对方或者宕机或者异常推出或者网络不通,此时也需要主动关闭己方连接,如果是客户端可在延迟一定时间后重新发起连接。
第3个问题是处理效率问题。不管是客户端还是服务器,如果是长连接一个程序至少需要两个线程,一个用于接收数据,一个用于发送心跳,写数据不需要专门的线程, 还需Worker线程用于进行消息的处理,也就是说接收线程仅仅负责接收数据,然后再分发给Worker进行数据的处理。如果是短连接,则不需要发送心跳的线程,如果是服务器还需要一个专门的线程负责进行连接请求的监听。这些是一个通信程序的整体要求.
-
思考了很久怎么让服务器主动发送给客户端, 因为客户端之前并不知道服务器要发送? 解决方法是用户连接时,立即向服务器发送自己的唯一ID,服务器端将ID和对应的socket用map存储. 向客户端发送消息时,就利用之前保存的socket 新建一个output.如下面的例子:
``Socket tempSocket = (Socket) entry.getKey();
同一个客户端开两个websocket连接或者多个客户端连,其实效果是一样的,既然多个客户端可以连,一个客户端开多个连接也可以.
新开一个客户端B ,出现问题有:
\1. 本来是服务器发送给A信息, 但是A收不到
\2. A 发送信息, 服务器可以接收到, 但是返回给了B,
\3. A 继续发送信息, 服务器不可以接收到.此时B发送, 服务器也不可以接收到
\4. 不能通知A已经上线, 而是通知B已经上线
解决方法:
登录后新建一个thread接收. 因为inputstream是全局变量,后一个登录了,全局变量就改了, 所以别的客户端收不到. 把inputstream 放在handler里就可以了
将客户端接收信息的功能集中给线程处理,实现多线程同步进行。
对共享资源的读写存在线程并发安全的问题,例如HashMap、HaspSet等都不是线程安全的,可以通过synchronized关键字进行加锁,但还有更方便的方案:可以直接使用Java标准库的java.util.concurrent包提供的线程安全的集合。例如HashMap的线程安全是 ConcurrentHashMap 上锁,jdk1.8后锁粒度变小,使用红黑树来优化链表,get不用加锁,HashSet的线程安全Set是CopyOnWriteArraySet。
-
在阿里巴巴图标库找了图标, 设置了客户端的大icon和发送按钮的icon.
-
退出时客户端做了处理, 一开始它有很多异常, 例如:
退出客户端时有EOF异常:
服务器也有java.net.SocketException: Socket closed
然后都对这些异常进行了判断和处理, 让客户端可以正常退出不抛出异常.
- 在前后端分离方面的思考, 一个是利用服务器返回信息来判断并更新客户端UI 而不是在连接按钮中设置客户端的变化. 确保操作成功再变化按钮, 减少重复setbutton的代码,增加程序的可读性.另一个是adjustStyle方法 把前端的工作分离出start();使用baseClient父类作为UI调整, 这样可以通过同样的UI写别的业务代码.
最大的收获是加深了对socket, 服务器和客户端多线程交互的理解,让我知道了服务器和客户端交互有哪些技术难点.
比如用writeint,writeUTF来定义通讯的每一步的话,不仅冗长而且容易出错, 服务器多了一个writeInt可能客户端就收不到,进而明白了http数据包的巨大优势. 花费时间比较多的地方一个是曾经想把它改成http协议,但是由于对http认识不足,于是目前没有实现; 数据库和服务器的交互不是非常难, 做下来是比较顺利的一部分,因为之前已经实现了类操作数据库, 也让我体会到了java类的好处.还学习了javafx的GUI和事件处理,之前一直不太明白事件处理和while循环到底是怎么一个顺序, 这次也在这个上面思考了很多时间,有了更深刻的理解.
\1. 与客户端的连接方式, 可以改进为HTTP 协议,加入解释器模式或者Web Socket 协议,更加方便,不用登录的时候写writeInt.
\2. 一次登录bye后, 然后在客户端可以开启重新登录.
\3. 服务器的run方法过长,有足足三页屏幕, 或许可以找到方法让他更易读.
\4. 可以找一个更好的GUI框架. 现有的也可以创建一个列表,像qq一样,登录一个用户, 显示一个按钮,按下按钮即可通讯.现在的UI太不紧凑了.注册可以变成一个按钮然后弹出,没必要4个输入框.