Java 自定义客户端与服务器
一:浏览器如何请求数据基本原理
基本原理:
当浏览器输入地址向服务器请求数据时,实际上浏览器会在内部建立一个Socket对象,把http请求报文转变成byet[]字节,然后调用Socket的方法把这些数据发送到服务器的,例如请求www/baidu.com的域名来会解析成对应的IP地址,解析成:http://202.108.22.5 :80/login.aspx 浏览器在解析到IP地址后,做的第二步就是对指定的URL进行HTTP封装,把URL封装成http报文,浏览器把URL封装成HTTP报文后,第三步做的事情就是将这个HTTP请求报文发送到服务器
浏览器在解析IP地址时流程:
在网络上访问网站,要首先通过DNS服务器把网络域名(www.XXXX.com)解析成61.XXX.XXX.XXX的IP地址后,我们的计算机才能访问。要是对于每个域名请求我们都要等待域名服务器解析后返回IP信息,这样访问网络的效率就会降低,而Hosts文件就能提高解析效率。根据Windows系统规定,在进行DNS请求以前,Windows系统会先检查自己的Hosts文件中是否有这个地址映射关系,如果有则调用这个 IP地址映射,如果没有再向已知的DNS服务器提出域名解析。也就是说Hosts的请求级别比DNS高。
如何修改Host文件:
找到电脑中C:\WINDOWS\system32\drivers\etc\hosts文件,在文件末尾加入如下图所示的ip与域名:
二:在浏览器访问本地(Tomcat)服务器
1:打开tomcat =》点击在tomcat主目录下的apache-tomcat-7.0.50\bin中的startup.bat
2:在浏览器输入本地的浏览地址http://localhost:8888/myweb/ ==》我这里修改了端口为8888 一般是8080 就可以看到效果了
三:在浏览器中访问自定义的服务器
服务器代码:==》注意最后一定要关闭Socket..
package Demo1; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class Demo1 { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(12346); //获取客户端对象 Socket s = ss.accept(); // BufferedReader br = new BufferedReader(new InputStreamReader( // s.getInputStream())); // // String line = null; // // while ((line = br.readLine()) != null) // { // System.out.println(line);//这里不能用这样的方法,会导致服务器回发不了数据集,浏览器并没有Socket结束标记 // } //读取浏览器发送的数据 InputStream in = s.getInputStream(); byte[] b = new byte[1024]; int len =in.read(b); System.out.println(new String(b,0,len)); //给浏览器发送数据 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); pw.println("欢迎光临李鹏的服务器"); pw.close(); // 给浏览器发送数据 /* OutputStream out = s.getOutputStream(); out.write("欢迎访问".getBytes()); out.flush();*/ in.close(); s.close(); ss.close(); } }
在浏览器输入:http://localhost:12345/
就可以看到我们的服务器端给客户端发送了数据,如图:
而服务器端也接受到了浏览器传过来的数据,如图:
这样我们就可以理解他们的工作原理了...
四:在eclipse下访问Tomcat服务器
如果不知道如何写和他一样的http://localhost:8888/myweb/mail.html 的头信息文件时,可以利用上面第三种的方式在浏览器输入:http://localhost:12345/myweb/mail.html ,可以看到浏览器中提交信息
GET /myweb/mail.html HTTP/1.1
Host: localhost:12345 ==》将这里的12345修改为8888
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8 ==》别忘了加两行空格,否则有错
然后在自己定义的浏览器中,给服务器写入数据,再读取服务器的数据
客户端代码:
package Demo1; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; public class Demo1 { public static void main(String[] args) throws UnknownHostException, IOException { Socket s = new Socket(InetAddress.getByName("10.2.156.26"),8888); PrintWriter pw = new PrintWriter(s.getOutputStream(),true); pw.println("GET /myweb/mail.html HTTP/1.1"); pw.println("Host: localhost:8888"); pw.println("Connection: keep-alive"); pw.println("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); pw.println("User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36"); pw.println("Accept-Encoding: gzip,deflate,sdch"); pw.println("Accept-Language: zh-CN,zh;q=0.8"); pw.println(""); pw.println(""); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line = null; while((line = br.readLine())!=null) { System.out.println(line); } br.close(); pw.close(); s.close(); } }
结果:
其中:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"995-1369649706000"
Last-Modified: Mon, 27 May 2013 10:15:06 GMT
Content-Type: text/html
Content-Length: 995
Date: Mon, 10 Aug 2015 12:34:35 GMT
这些是响应的头信息,在浏览器看不到的,而其他代码就由浏览器接收到然后解析成为了一个页面
五:高级的访问服务器的方法
通过封装为URL对象来访问网页
java代码:
package Demo1; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; public class Demo1 { public static void main(String[] args) throws IOException { String path = "http://10.2.156.26:8888/myweb/mail.html?name=aa"; URL url = new URL(path); /*System.out.println(url.getProtocol());//http System.out.println(url.getHost()); //10.2.156.26 System.out.println(url.getPort()); //8888 System.out.println(url.getPath());// /myweb/mail.html System.out.println(url.getFile());// /myweb/mail.html?name=aa */ URLConnection conn = url.openConnection(); //读取服务器端返回的数据 InputStream in = conn.getInputStream(); byte[] arr = new byte[1024]; int len = in.read(arr); System.out.println(new String(arr,0,len)); } }
结果:
可以看到这里是没有响应的头信息的..
六:综合应用==》爬虫(获取到网页中所有的邮箱)
代码:
public class Demo1 { /** * 实现网页爬虫 */ public static void main(String[] args) throws IOException { // getEmails(); getEmail(); } // 从网络上的网页获取邮箱 public static void getEmail() throws IOException { String path = "http://localhost:8888/myweb/mail.html"; URL url = new URL(path); URLConnection conn = url.openConnection(); InputStream in = conn.getInputStream();// 读取服务器端返回的mail.html文件的数据 BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8")); String regex = "\\w+@\\w+(\\.\\w+)+"; Pattern p = Pattern.compile(regex); String line = null; while ((line = br.readLine()) != null) { // 使用Matcher对象从读取的一行中获取符合邮箱规则的内容 //System.out.println(line); Matcher m = p.matcher(line); while (m.find()) { System.out.println(m.group()); } } } // 从本地文件中获取所有的邮箱 public static void getEmails() throws IOException { // 创建读取本地文件的读取流 BufferedReader br = new BufferedReader( new FileReader("file\\mail.html")); String regex = "\\w+@\\w+(\\.\\w+)+"; Pattern p = Pattern.compile(regex); String line = null; while ((line = br.readLine()) != null) { // 使用Matcher对象从读取的一行中获取符合邮箱规则的内容 Matcher m = p.matcher(line); while (m.find()) { System.out.println(m.group()); } } br.close(); } }