httpclient乱码问题浅析2

上次说到了httpclient的编码识别机制是从Http头提取信息。但是它的源码我看的云里雾里,也没有怎么验证过。后来终于发现给出HTTP头的代码。

 

通过这个分析HTTP头,发现确实有不少网站并不返回charset信息的。

先拿百度举例,百度的信息是挺全的:

 

 

Date: Mon, 25 Oct 2010 06:32:11 GMT

Server: BWS/1.0 

Content-Length: 6218

Content-Type: text/html;charset=gb2312

Cache-Control: private

Expires: Mon, 25 Oct 2010 06:32:11 GMT

Set-Cookie: BAIDUID=0BC71212D53FA68D5095FDD0F21B4986:FG=1; expires=Mon, 25-Oct-40 06:32:11 GMT; path=/; domain=.baidu.com

P3P: CP=” OTI DSP COR IVA OUR IND COM “

Connection: Keep-Alive

 

然后拿个另外的大网站www.hudong.com,发现就连这个网站信息也不全:

 

Server: nginx/0.8.50

Date: Mon, 25 Oct 2010 06:33:42 GMT

Content-Type: text/html

Content-Length: 153774

Last-Modified: Mon, 25 Oct 2010 06:31:40 GMT

Connection: close

Accept-Ranges: bytes

看来光是用httpclient自己来识别网站编码是行不通的,只能通过判断 meta 里面的东西来设定了。但问题是这样一来,就要把刚刚摘下来的字符串转码。这对我来说还是比较困难的,得继续研究。

 

 

 

 

HttpClient乱码问题浅析(1)

最近做项目的时候用到httpclient这个东西,但是乱码问题一直困扰着我。

我使用httpclient4进行html的读取,使用的代码如下:

这么一来,htmltext就是网页的内容了。

此处我并没有使用httpget.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, “UTF-8”);这样的代码来进行编码的设定,按照httpclient3的情况,默认是ISO-8859-1,所以按理一定会乱码。然而实际上很多网站不会乱码。说明这个包是有一定的识别编码的方法的。

看了httpcore的源码,确实发现了它能够识别网站编码。它是根据http头返回的信息来识别的。

 

 

大多数正规网站是会返回这个http头的,但是不排除某些野鸡网站没有返回。我碰到的乱码网站无一例外是国内的一些野鸡网站。

现在还没有解决这个问题,我觉得解决思路大致有两种方法。一种是把default charset改成gb2312,因为我的项目针对的是国内网站,这些野鸡网站一般不会去用UTF-8来编码的。当然这个方法局限性很大。另一种方法是在第一种方法的基础上,先将网站内容下载下来,然后用正则表达式去找charset=xxx的语句,根据这个再进行编码转化,这样对于国外的野鸡网站也应该有效果。但是如果碰到连charset也没写的网站,那就没办法了。

 

[转]Java的多进程运行模式分析

一般我们在java中运行其它类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个java虚拟机实例在运行。而有的时候,我们需要通过java代码启动多个java子进程。这样做虽然占用了一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其它的子进程。

在Java中我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec方法执行java classname。如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛出一个IOException错误。下面让我们来看一个简单的例子。

通过java Test_Exec运行程序后,发现在C盘多了个Test1.txt文件,但在控制台中并未出现”被调用成功!”的输出信息。因此可以断定,Test已经被执行成功,但因为某种原因,Test的输出信息未在Test_Exec的控制台中输出。这个原因也很简单,因为使用exec建立的是Test_Exec的子进程,这个子进程并没有自己的控制台,因此,它并不会输出任何信息。

如果要输出子进程的输出信息,可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出,在父进程中就是输入),然后将子进程中的输出流从父进程的控制台输出。具体的实现代码如下如示:

从上面的代码可以看出,在Test_Exec_Out.java中通过按行读取子进程的输出信息,然后在Test_Exec_Out中按每行进行输出。 上面讨论的是如何得到子进程的输出信息。那么,除了输出信息,还有输入信息。既然子进程没有自己的控制台,那么输入信息也得由父进程提供。我们可以通过Process的getOutputStream方法来为子进程提供输入信息(即由父进程向子进程输入信息,而不是由控制台输入信息)。我们可以看看如下的代码:

从以上代码可以看出,Test1得到由Test_Exec_In发过来的信息,并将其输出。当你不加bw.flash()和bw.close()时,信息将无法到达子进程,也就是说子进程进入阻塞状态,但由于父进程已经退出了,因此,子进程也跟着退出了。如果要证明这一点,可以在最后加上System.in.read(),然后通过任务管理器(在windows下)查看java进程,你会发现如果加上bw.flush()和bw.close(),只有一个java进程存在,如果去掉它们,就有两个java进程存在。这是因为,如果将信息传给Test2,在得到信息后,Test2就退出了。在这里有一点需要说明一下,exec的执行是异步的,并不会因为执行的某个程序阻塞而停止执行下面的代码。因此,可以在运行test2后,仍可以执行下面的代码。
exec方法经过了多次的重载。上面使用的只是它的一种重载。它还可以将命令和参数分开,如exec(“java.test2”)可以写成exec(“java”, “test2”)。exec还可以通过指定的环境变量运行不同配置的java虚拟机。

除了使用Runtime的exec方法建立子进程外,还可以通过ProcessBuilder建立子进程。ProcessBuilder的使用方法如下:

在建立子进程上,ProcessBuilder和Runtime类似,不同的ProcessBuilder使用start()方法启动子进程,而Runtime使用exec方法启动子进程。得到Process后,它们的操作就完全一样的。

ProcessBuilder和Runtime一样,也可设置可执行文件的环境信息、工作目录等。下面的例子描述了如何使用ProcessBuilder设置这些信息。

基于socket(TCP)的聊天程序(C/S结构)

这个是我们网络与通信的作业。当初看作业题的时候感觉这题是要我们熟悉TCP连接,不过后面跟了很多很烦的要求,比如一定要有GUI,要验证用户登录,要给出当前用户的列表,要能给所有人发信息,要能给指定的人发信息。然后我又用java作为语言,socket的类本来就封装好的,所以整个主题就偏掉了,更像是java多线程编程联系。而且我觉得,我整一个编程菜鸟。程序结构乱七八糟,算法乱七八糟,而且编程很磨叽,编了好几天才完成这个程序。多线程的问题确实搞的我头很大,有时候不得不动用一些山寨方法避免问题。

看了别人编的程序,大多只有两个文件,一个server,一个client。我本来为了程序结构清晰一点,多建了几个,结果到了后面越来越烦了。

整个C/S的运作方式是这样的:server建立之后,等待client连接。client启动之后,连接到服务器的socket,第一步是向服务器发送用户名和密码。服务器连接后的第一步是产生一个监听线程,然后监听到客户端发过来的用户名密码做验证,如果验证通过,则向客户端发送connected的信号,然后将该socket加入到一个arraylist中方便以后管理,如果没通过,就向客户端发送failed信号。客户端在接收到connected信号之后,开启新的监听线程监听服务器发来的别的信息。客户端能做到向所有人发送一个信息以及向特定的一个人发送信息。用的方法很山寨,就是在信息前面加个头,比如说sta:或者stu:,分别表示向所有人发送和向某人发送。服务器接收到这个信息之后,先要解析信息的首部,然后再执行相应的操作。

我之前为了让程序层次分明一点,把客户端分为两层,一层是核心层,就是ClientKernel这个文件,它封装了所有客户端的功能,和服务器通信的实体也是它,另外一层是客户端实体,只要调用一下ClientKernel的函数就可以实现功能了,这样做的好处是可移植性,可以做命令行的客户端,也可以做GUI的客户端,其实是模仿twitter4j这样的库,不过后来发现做的很烂啊。另外,为了做到有新用户登录或者用户退出时,所有用户的userlist列表都能自动刷新,而客户端实体又和kernel分离,就用了一个接口起到类似回调函数的作用。

另外,我的英文很差,所以几乎所有的变量名都是乱起的。另另外,我对java的异常捕获不是很了解,所以都是瞎捕获的。另另另外,在线程方面我可能遇到了一点问题,为了避免这些问题,有些io流创建了就没有关闭,这个实在很难解决。

下面给出代码:

Server.java:

 

UserController.java:

User.java:

 

ClientKernel.java:

 

ClientOpt.java(这个名字起错了,应该是指实体客户端要有的操作):

 

 

 

GUI代码太长,不给出了。