一、Java网络编程
网络编程在如今这样的网络时代是十分重要的,Java语言提供了丰富的类库来支持网络编程。这里将重点介绍Java.net中的类,充分了解认识Java网络编程的原理并深入学习各模块。在学习Java网络编程之前首先需要具备一定的网络知识:网络的层次结构,常见的网络协议(TCP/IP),IP地址端口号等等。需要学习了解这些内容,可以参考前面的文章。
二、主机地址和IP地址
在进行网络访问时,每个主机都有IP地址,也有主机名(域名),为了便于处理与之相关的问题,Java.net包中提供了地址获取类InetAddress,当然还有对应的子类Inet4Address,Inet6Address,分别对应IPv4和IPv6。这里介绍InetAddress的基本用法。
1、InetAddress对象
InetAddress类是没有显式的构造函数的,所以只能通过它的静态函数来获取对象,InetAddress主要就有两个重要的变量,一个是IP地址,一个就是对应的域名(主机名)。因此下面的函数都是通过这两个变量来实现对象建立的,几个函数分别为:
static InetAddress getByName(String host)
static InetAddress[] getAllByName(String host)
static InetAddress getByAddress(byte[] addr)
static InetAddress getByAddress(String host,byte[] addr)
static InetAddress getLocalHost()
对应的意思分别是:1、通过域名获取对象(的ip)2、通过域名获取对象(所有ip)3、通过ip获取对象(的域名)4、通过域名和ip对象5、获取本机对象。第二个与第一个区别就在于它会获取多个对象,因为有些域名是有多个IP地址的。
2、成员函数
InetAddress类中实现了一些常用的函数:
boolean isReachable(int timeout):地址是否可达,timeout为测试时间
String getHostName():获取此IP地址的主机名
byte[] getAddress():返回原始IP地址(字节数组形式)
String getHostAddress():返回IP地址(文本形式)
下面的部分代码测试了一些函数,可以更直观的了解这些函数的应用:
package net;
import java.net.Inet4Address;
import java.net.InetAddress;
public class NetTest {
public static void main(String[] args) throws Exception{
byte[] ip = {119,75,(byte) 218,70};
InetAddress ia = InetAddress.getByName("www.baidu.com");
InetAddress ia2 = InetAddress.getByAddress("www.baidu.com",ip);
InetAddress[] ia3 = InetAddress.getAllByName("www.baidu.com");
System.out.println("ia ip:"+ia.getHostAddress());
System.out.println("ia name:"+ia.getHostName());
System.out.println("ia2 ip:"+ia2.getHostAddress());
System.out.println("ia2 name:"+ia2.getHostName());
System.out.println("ia3 length:"+ia3.length);
System.out.println("ia3 byte[]:"+ia3[0].getAddress());
System.out.println("ia3 ip1:"+ia3[0].getHostAddress());
System.out.println("ia3 ip2: "+ia3[1].getHostAddress());
System.out.println("local name:"+InetAddress.getLocalHost().getHostName());
System.out.println("local ip:"+InetAddress.getLocalHost().getHostAddress());
}
}
//输出结果
ia ip:119.75.217.109
ia name:www.baidu.com
ia2 ip:119.75.218.70
ia2 name:www.baidu.com
ia3 length:2
ia3 byte[]:[B@79b7d13e
ia3 ip1:119.75.217.109
ia3 ip2:119.75.218.70
local name:PC-201511271651
local ip:192.168.1.110
三、URLDecoder和URLEncoder
在URL中有时候会有参杂有中文,但是当把这个URL赋值到代码编辑窗口时,中文部分显示的又不是中文,而是类似于%E5%88%98%E8%AF的一个形式,其实这里涉及到一些字符的编码转换,URLDecoder和URLEncoder两个类就是用来做转换的,其中没有定义构造函数,分别包含静态的decode()方法和encode()方法,可以直接通过类名来调用。
String decode(String s, String enc):s用enc解码方式解码
String encode(String s, String enc):s用enc编码方式编码
关于其中涉及的application/x-www-form-urlencoded等可以参考:
http://www.aikaiyuan.com/6324.html
package net;
import java.net.*;
public class NetTest {
public static void main(String[] args) throws Exception{
//下面是一个完整的url,其中的中文被编码了,下面取出进行解码
String str = "http://image.baidu.com/search/index?tn="
+ "baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-"
+ "1&fr=&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab="
+ "0&fb=0&width=&height=&face=0&istype=2&ie=utf-8"
+ "&word=%E5%88%98%E8%AF%97%E8%AF%97&oq=%E5%88%98"
+ "%E8%AF%97%E8%AF%97&rsp=-1";
String str0 = "%E5%88%98%E8%AF%97%E8%AF%97"; //中文部分
String str1 = URLDecoder.decode(str0,"utf-8"); //解码
String str2 = URLEncoder.encode(str1,"utf-8"); //编码
System.out.println("str1: "+str1);
System.out.println("str2: "+str2);
}
}
//结果
str1: 刘诗诗
str2: %E5%88%98%E8%AF%97%E8%AF%97
四、URL、URLConnection、HttpURLConnection
1、URL
url即统一资源定位器,它指向网络资源的目录或文件,可以简单的视为网址,url主要有协议名,主机名,端口号,资源等组成。例如:http://img.ivsky.com/img/tupian/t/201602/16/shiweiyan-004.jpg,它的协议是http,中间是主机路径,后面为文件名,没有端口号(默认80)。
Java中的URL类就是这样一个处理url的类,它的对象有协议,主机,端口,文件等多个变量。下面介绍它的构造函数,URL有很多个构造函数,可以通过传出相应的字段来创建,最常用的就是直接传入一个url了,下面给出两个基本的函数:
public URL(String spec)
public URL(String protocol, String host, String file)
在创建了对象之后,类中提供了许多函数来获取类成员,下面的函数看名字就知道什么意思了,都是获取url对象相应的字段:
String getQuery()
String getPath()
String getUserInfo()
int getPort()
String getProtocol()
String getHost()
通过例子看看就行了:
public class NetTest {
public static void main(String[] args) throws Exception{
URL url1 = new URL("http://blog.csdn.net/anialy/article/details/8364652");
URL url2 = new URL("http","img.ivsky.com/img/tupian/t/201602/16/","shiweiyan-004.jpg");
System.out.println("url1.protocol: "+url1.getProtocol());
System.out.println("url1.host: "+url1.getHost());
System.out.println("url1.path: "+url1.getPath());
System.out.println("url1.port: "+url1.getPort());
System.out.println("url1.file: "+url1.getFile());
System.out.println("url1.query: "+url1.getQuery());
System.out.println("url1.userinfo: "+url1.getUserInfo());
System.out.println("url1.default protocol: "+url1.getDefaultPort());
System.out.println(url1);
System.out.println(url2);
//URLConnection;
//HttpURLConnection
}
}
//输出
url1.protocol: http
url1.host: blog.csdn.net
url1.path: /anialy/article/details/8364652
url1.port: -1
url1.file: /anialy/article/details/8364652
url1.query: null
url1.userinfo: null
url1.default protocol: 80
http://blog.csdn.net/anialy/article/details/8364652
http://img.ivsky.com/img/tupian/t/201602/16/shiweiyan-004.jpg
类URL中还有函数URLConnection openConnection()用来建立一个连接对象。openStream来打开一个输入流读取数据 ,实际上就是URLConnection中的getInputStream(),下面都会讲。
2、URLConnection
URLConnection类是一个抽象类,它是所有应用程序和URL通信连接类的超类,典型的一个就是HttpURLConnection类,它就是类 URLConnection的一个子类。常用的一种用法就是:URLConnection urlConnection = url.openConnection();建立一个连接,至于urlConnection是抽象类实例的解释:这是多态性的一种用法,也叫面向接口编程,urlConnection只是一个引用。具体可以参考这里。
类URLConnection中定义了许多连接通信方面的函数,主要在其子类中得到应用,这里介绍一下常见函数:
设置连接属性:
setDoOutput(true);// 使用 URL 连接进行输出
setDoInput(true);// 使用 URL 连接进行输入
setUseCaches(false);// 忽略缓存
setRequestMethod(“POST”);//设置URL请求方法 (属于子类HttpURLConnection)
设置请求属性:
setRequestProperty(String key, String value)
key:”Content-Type”,”Connection”,”Charset”,user-agent…..
其他:
connect():打开一个url对应的连接
OutputStream getOutputStream():返回连接输出流
InputStream getInputStream():返回连接输入流
3、HttpURLConnection
HttpURLConnection类是URLConnection的一个子类,用于支持有关http协议的连接。下面实现一个向服务器发送GET请求并获取响应信息的例子:
package net;
import java.io.*;
import java.net.*;
import java.util.List;
import java.util.Map;
public class NetTest {
public static void main(String[] args) throws Exception{
String urlstr = "http://www.csdn.net/";
//System.out.println(url.getProtocol());
URL url = new URL(urlstr); //建立URL对象
//建立一个http连接对象
HttpURLConnection huc = (HttpURLConnection) url.openConnection();
//设置请求头属性(字段可以参考浏览器,这个就是模拟浏览器向服务器发送请求)
huc.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
huc.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0");
huc.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
huc.connect(); //获取连接
//输出获取到的响应头部信息(按字段)
Map<String, List<String>> map = huc.getHeaderFields();
for(String key:map.keySet())
System.out.println(key+":"+map.get(key));
}
}
//输出
null:[HTTP/1.1 200 OK]
ETag:["571c264a-18db2"]
Vary:[Accept-Encoding, Accept-Encoding]
Date:[Sun, 24 Apr 2016 01:55:40 GMT]
Content-Length:[101810]
Last-Modified:[Sun, 24 Apr 2016 01:50:02 GMT]
Keep-Alive:[timeout=20]
Accept-Ranges:[bytes]
Connection:[keep-alive]
Content-Type:[text/html; charset=utf-8]
Server:[openresty]
有关相关函数或参数等应用说明可以参考这里参数详解。
下面实现下载网页源代码的例子,传入一个ULR,可以获取到该网页的源代码内容:
package net;
import java.io.*;
import java.net.*;
public class NetTest {
public static void main(String[] args) throws Exception{
String urlstr = "http://www.csdn.net/";
URL url = new URL(urlstr); //建立URL对象
//建立一个http连接对象
HttpURLConnection huc = (HttpURLConnection) url.openConnection();
huc.connect(); //获取连接(不重要,下面一般会隐式执行)
//从连接返回一个输入流
InputStream is = huc.getInputStream();
/**
* 这里必须添加"utf-8",不然网页中文部分是乱码的,
* InputStreamReader就是将byte类型的is按照"utf-8"方式转换成字符类型
*/
BufferedReader buffer = new BufferedReader(new InputStreamReader(is,"utf-8"));
StringBuffer bs = new StringBuffer();
String str = null;
while((str=buffer.readLine())!=null){ //读取响应信息
bs.append(str).append("\n");
}
System.out.println(bs);
}
}
上面的输出结果就是网页的源代码了,和直接在浏览器打开的源代码是一样的。可以利用这个代码分析做一些爬虫之类的都可以。
五、SocketSever、Socket
这一节主要简答介绍有关tcp通信的内容,即socket编程,这是进行可靠通信的主要内容,用于建立客户端和服务器端的连接,因此也可以叫做客户端-服务器编程。
1、Socket
Socket类是Java中用于建立连接端口的一个类,即通过Socket实例建立一个通信接口用于与服务器通信。多用于客户端,常见两种创建方式:
Socket(String host, int port)
Socket(InetAddress address, int port)
都是要输入主机名和端口号,第二种的主机名是通过InetAddress类对象传递的。在Socket中还有两个重要的流处理函数:
InputStream getInputStream()
OutputStream getOutputStream()
两个函数额作用用于给Socket对象返回一个输入/输出流对象,用于进行连接端的信息传输。在Socket中还封装了许多相关处理的函数,这里不再详述。
2、SeverSocket
这个类是用于服务器创建一个监听Socket,用来监听来自客户端的请求信息。创建方式:
ServerSocket(int port) //针对特定端口的Socket
当服务器创建了一个Socket之后,需要开始工作,即监听来自客户端的信息并响应,这里应用到的函数就是accept(),它用来监听来自客户端的连接并返回一个Socket对象来处理该连接。
3、Server-Client
下面代码通过创建Socket实现客户端和服务器端的连接并实现互相发送信息的功能:
//客户端
import java.io.*;
import java.net.*;
public class Client {
public static void main(String[] args) throws IOException{
//获取本机地址
InetAddress ip = InetAddress.getLocalHost();
System.out.println(ip.getHostName());
System.out.println(ip.getHostAddress());
//利用本机地址和端口号20000创建一个socket访问服务器
Socket socket = new Socket(ip,20000);
//接收服务器信息(处理流)
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
System.out.println("服务器的数据:"+line);
//向服务器发送数据
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("你好,这里是客户端");
ps.close();
br.close();
}
}
//服务器端
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) throws IOException{
//创建一个serversocket,端口号20000
ServerSocket sever = new ServerSocket(20000);
while(true){
System.out.println("waiting!"); //等待连接
Socket socket = sever.accept(); //获取到连接返回一个socket
//获取客户端地址
InetAddress address = socket.getInetAddress();
System.out.println("主机名:"+address.getHostName());
System.out.println("ip地址:"+address.getHostAddress());
//向客户端发送数据(应用处理流)
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("你好,这里是服务器");
//接收客户端数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
System.out.println("客户端的数据:"+line);
br.close(); //这里会关闭socket
ps.close();
}
}
}
上面就是实现Socket通信的简单代码,客户端和服务器端相互独立。这里再介绍如何在eclipse中测试这个代码,这里建立两个工程,分别为客户端和服务器端工程,在eclipse中都为打开状态,首先点击到服务器工程所在页面,运行,这时服务器就开始监听连接了,然后打开客户端页面,运行,这时客户端就会运行并向服务器发送数据。虽然在同一台电脑上操作,但是地址等都是真实的,因此可以很好的模拟出实际的运行效果。通过控制台窗口可以看到各自的输出信息:
//服务器端
waiting!
主机名:PC-201511271651.lan
ip地址:192.168.1.110
客户端的数据:你好,这里是客户端
waiting!
//客户端
PC-201511271651
192.168.1.110
服务器的数据:你好,这里是服务器
这里分别输出了各自收到对方发送的信息,并且显示的地址和主机名也是一样的,可见数据的传输通道是建立且正确的。需要注意的是上面的代码中并没有直接关闭socket,因为在对应socket的处理流执行关闭(close)的过程中,sockct是会随之关闭的,因此在数据没有完全传输完之前不要将流关闭,不然socket也会关闭。如在上面的客户端若是在读取完服务器信息后马上关闭流br,那么流ps就不能再向服务器发送数据了,因为这时socket已经关闭,连接断开了是不能发送的。
上面我们分别看了客户端和服务器端控制台窗口消息,在eclipse中这两个消息是显示在不同的控制台窗口的,在eclipse中可以切换窗口(或者可以这样理解:在eclipse中可以同时运行多个工程,每个工程是独立的,有各自的控制台),方式: