Java 中的网络编程

时间:2021-09-06 20:14:02

java将ip地址封装成了一个对象Inet4Address,他有两个子类对象Inet4Address, Inet6Address 分别对应的是ipv4 ,ipv6。Inet4Address
没有构造方法,因此不能实例化对象。通过以下方法得到该类对象。
static InetAddress[] getAllByName(String host)
在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。

  1. static InetAddress[] getAllByName(String host)
    在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。
  2. static InetAddress getByAddress(byte[] addr)
    在给定原始 IP 地址的情况下,返回 InetAddress 对象。
  3. static InetAddress getByAddress(String host, byte[] addr)
    根据提供的主机名和 IP 地址创建 InetAddress。
  4. static InetAddress getByName(String host)
    在给定主机名的情况下确定主机的 IP 地址。
  5. static InetAddress getLocalHost()
    返回本地主机。
    常用的是第一个,第四个和第五个。不过这五个方法都会抛出UnknownHostException - 如果找不到 host 的 IP 地址或者ip长度非法等。
    该类的常用方法:
    String getHostAddress()
    返回 IP 地址字符串(以文本表现形式)。
    String getHostName()
    获取此 IP 地址的主机名。

代码示例:

InetAddress ia = InetAddress.getByName("www.baidu.com");
//InetAddress ia = InetAddress.getByName("172.16.32.254");
System.out.println("address:"+ia.getHostAddress());
System.out.println("name:"+ia.getHostName());

传输协议:
UDP:飞秋聊天,网络视频会议,桌面共享等

  1. 将数据及目的封装成数据包中,不需要建立连接。
  2. 每个数据包的大小限制在64k内
  3. 因无连接,是不可靠协议
  4. 不需要建立连接,速度快

TCP:打电话,下载是TCP不能丢失数据。

  1. 建立连接,形成传输数据的通道
  2. 在连接中进行大数据量传输
  3. 通过三次握手完成连接,是可靠协议
  4. 必须建立连接,效率会稍低

Socket:
Socket就是为网络服务提供的一种机制,通信的两端都有Socket
网络通讯其实就是Socket间的通讯,数据在两个Socket间通过IO传输。
下面我们来讲解一下UDP的使用。
需求:通过udp传输方式,将一段文字数据发送出去。定义一个udp发送端。
思路:

  1. 建立updsocket服务。
  2. 提供数据,并将数据封装到数据包中。
  3. 通过socket服务的发送功能,将数据包发出去。
  4. 关闭资源。

代码示例:

class  UdpSend
{
public static void main(String[] args) throws Exception
{
//1,创建udp服务。通过DatagramSocket对象。绑定发送端端口是8888
DatagramSocket ds = new DatagramSocket(8888);

//2,确定数据,并封装成数据包。指定接受端的ip和端口
byte[] buf = "UDP,你好! ".getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);

//3,通过socket服务,将已有的数据包发送出去。通过send方法。
ds.send(dp);

//4,关闭资源。

ds.close();

}
}

需求:
定义一个应用程序,用于接收udp协议传输的数据并处理的。
思路:

  1. 定义udp socket服务。通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标识。方便于明确哪些数据过来该应用程序可以处理。
  2. 定义一个数据包,因为要存储接收到的字节数据。因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
  3. 通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
  4. 通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。
  5. 关闭资源。

代码示例:

class  UdpRece
{
public static void main(String[] args) throws Exception
{
//1,创建udp socket,指定接收端的端口号。
DatagramSocket ds = new DatagramSocket(10000);
while(true)
{
//2,定义数据包。用于存储数据。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);

//3,通过服务的receive方法将收到数据存入数据包中。
ds.receive(dp);//阻塞式方法。


//4,通过数据包的方法获取其中的数据。
String ip = dp.getAddress().getHostAddress();

String data = new String(dp.getData(),0,dp.getLength());

int port = dp.getPort();

System.out.println(ip+"::"+data+"::"+port);

}
//5,关闭资源
//ds.close();

}
}

键盘录入发送数据:

class  UdpSend2
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket();

BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));

String line = null;

while((line=bufr.readLine())!=null)
{
if("886".equals(line))
break;

byte[] buf = line.getBytes();

DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10001);

ds.send(dp);
}
ds.close();
}
}



class UdpRece2
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket(10001);

while(true)
{
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);

ds.receive(dp);

String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());


System.out.println(ip+"::"+data);
}
}
}

编写一个聊天程序。
有收数据的部分,和发数据的部分。这两部分需要同时执行。那就需要用到多线程技术。一个线程控制收,一个线程控制发。
因为收和发动作是不一致的,所以要定义两个run方法。
而且这两个方法要封装到不同的类中。
代码示例:

import java.io.*;
import java.net.*;
class Send implements Runnable
{

private DatagramSocket ds;
public Send(DatagramSocket ds)
{
this.ds = ds;
}


public void run()
{
try
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

String line = null;

while((line=bufr.readLine())!=null)
{


byte[] buf = line.getBytes();

DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10002);

ds.send(dp);

if("886".equals(line))
break;
}
}
catch (Exception e)
{
throw new RuntimeException("发送端失败");
}
}
}

class Rece implements Runnable
{


private DatagramSocket ds;
public Rece(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
try
{
while(true)
{
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);

ds.receive(dp);


String ip = dp.getAddress().getHostAddress();

String data = new String(dp.getData(),0,dp.getLength());

if("886".equals(data))
{
System.out.println(ip+"....离开聊天室");
break;
}


System.out.println(ip+":"+data);
}
}
catch (Exception e)
{
throw new RuntimeException("接收端失败");
}
}
}


class ChatDemo
{

public static void main(String[] args) throws Exception
{
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10002);

new Thread(new Send(sendSocket)).start();
new Thread(new Rece(receSocket)).start();

}
}

下面我们来讲解一下TCP的使用。
tcp分客户端和服务端。客户端对应的对象是Socket。服务端对应的对象是ServerSocket。
客户端:Socket该对象建立时,就可以去连接指定主机。因为tcp是面向连接的。所以在建立socket服务时,就要有服务端存在,并连接成功。形成通路后,在该通道进行数据的传输。
服务端:

  1. 建立服务端的socket服务。ServerSocket(); 并监听一个端口。
  2. 获取连接过来的客户端对象。通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法阻塞式的。
  3. 客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。并打印在控制台。
  4. 关闭服务端。(可选)

需求:给服务端发送给一个文本数据。
代码示例:

class  TcpClient
{
public static void main(String[] args) throws Exception
{
//创建客户端的socket服务。指定目的主机和端口
Socket s = new Socket("192.168.1.254",10003);

//为了发送数据,应该获取socket流中的输出流。
OutputStream out = s.getOutputStream();

out.write("tcp ge men lai le ".getBytes());


s.close();
}
}

class TcpServer
{
public static void main(String[] args) throws Exception
{
//建立服务端socket服务。并监听一个端口。
ServerSocket ss = new ServerSocket(10003);

//通过accept方法获取连接过来的客户端对象。
while(true)
{
Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".....connected");

//获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据。
InputStream in = s.getInputStream();

byte[] buf = new byte[1024];
int len = in.read(buf);

System.out.println(new String(buf,0,len));

s.close();//关闭客户端.
}
//ss.close();
}
}

需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息
代码示例:

class TcpClient2 
{
public static void main(String[] args)throws Exception
{
Socket s = new Socket("192.168.1.254",10004);

OutputStream out = s.getOutputStream();

out.write("服务端,你好".getBytes());


InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String(buf,0,len));

s.close();
}
}


class TcpServer2
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10004);

Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String(buf,0,len));


OutputStream out = s.getOutputStream();


Thread.sleep(10000);
out.write("哥们收到,你也好".getBytes());

s.close();

ss.close();
}
}

练习:建立一个文本转换服务器。客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。

分析:
客户端:既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
源:键盘录入。
目的:网络设备,网络输出流。
选择什么流:操作的是文本数据。可以选择字符流。都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
代码示例:

class  TransClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.254",10005);


//定义读取键盘数据的流对象。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));


//定义目的,将数据写入到socket输出流。发给服务端。
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);



//定义一个socket读取流,读取服务端返回的大写信息。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));

String line = null;

while((line=bufr.readLine())!=null)
{//读到回车符才会返回数据
if("over".equals(line))
break;

out.println(line);//自动刷新
// bufOut.write(line);
// bufOut.newLine();
// bufOut.flush();

String str =bufIn.readLine();
System.out.println("server:"+str);

}

bufr.close();
s.close();//加了结束标记-1,导致服务端在读readLine 底层的read读到了-1 跳出循环服务端也结束


}
}
/*

服务端:
源:socket读取流。
目的:socket输出流。
都是文本,装饰。



*/


class TransServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10005);

Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");

//读取socket读取流中的数据。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));

//目的。socket输出流。将大写数据写入到socket输出流,并发送给客户端。
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

PrintWriter out = new PrintWriter(s.getOutputStream(),true);

String line = null;
while((line=bufIn.readLine())!=null)
{ //读到回车符才会返回数据

System.out.println(line);

out.println(line.toUpperCase());
// bufOut.write(line.toUpperCase());
// bufOut.newLine();
// bufOut.flush();
}

s.close();
ss.close();

}
}
/*
该例子出现的问题。
现象:客户端和服务端都在莫名的等待。
为什么呢?
因为客户端和服务端都有阻塞式方法。这些方法么没有读到结束标记。那么就一直等
而导致两端,都在等待。


*/

练习:文本文件的上传

class  TextClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.254",10006);

BufferedReader bufr =
new BufferedReader(new FileReader("IPDemo.java"));



PrintWriter out = new PrintWriter(s.getOutputStream(),true);


String line = null;
while((line=bufr.readLine())!=null)
{
out.println(line);
}

s.shutdownOutput();//关闭客户端的输出流。相当于给流中加入一个结束标记-1.


BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

String str = bufIn.readLine();
System.out.println(str);

bufr.close();

s.close();
}
}
class TextServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10006);

Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");


BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

PrintWriter out = new PrintWriter(new FileWriter("server.txt"),true);

String line = null;

while((line=bufIn.readLine())!=null)
{

out.println(line);
}

PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上传成功");


out.close();
s.close();
ss.close();

}
}

练习:上传图片

import java.io.*;
import java.net.*;
class PicClient
{
public static void main(String[] args)throws Exception
{
Socket s = new Socket("192.168.1.254",10007);

FileInputStream fis = new FileInputStream("c:\\1.bmp");

OutputStream out = s.getOutputStream();

byte[] buf = new byte[1024];

int len = 0;

while((len=fis.read(buf))!=-1)
{
out.write(buf,0,len);
}

//告诉服务端数据已写完
s.shutdownOutput();

InputStream in = s.getInputStream();

byte[] bufIn = new byte[1024];

int num = in.read(bufIn);
System.out.println(new String(bufIn,0,num));

fis.close();
s.close();
}
}


class PicServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10007);

Socket s = ss.accept();

InputStream in = s.getInputStream();

FileOutputStream fos = new FileOutputStream("server.bmp");

byte[] buf = new byte[1024];

int len = 0;
while((len=in.read(buf))!=-1)
{
fos.write(buf,0,len);
}

OutputStream out = s.getOutputStream();

out.write("上传成功".getBytes());

fos.close();

s.close();

ss.close();
}
}

上面例子服务端有个局限性。当A客户端连接上以后,被服务端获取到,服务端执行具体流程。这时B客户端连接,只有等待。因为服务端还没有处理完A客户端的请求,还有循环回来执行下次accept方法。所以暂时获取不到B客户端对象。
那么为了可以让多个客户端同时并发访问服务端,最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。
如何定义线程呢?
只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。
下面给出完整示例:

class  PicClient
{
public static void main(String[] args)throws Exception
{
if(args.length!=1)
{
System.out.println("请选择一个jpg格式的图片");
return ;
}
File file = new File(args[0]);
if(!(file.exists() && file.isFile()))
{
System.out.println("该文件有问题,要么补存在,要么不是文件");
return ;

}

if(!file.getName().endsWith(".jpg"))
{
System.out.println("图片格式错误,请重新选择");
return ;
}

if(file.length()>1024*1024*5)
{
System.out.println("文件过大,没安好心");
return ;
}
Socket s = new Socket("192.168.1.254",10007);

FileInputStream fis = new FileInputStream(file);

OutputStream out = s.getOutputStream();

byte[] buf = new byte[1024];

int len = 0;

while((len=fis.read(buf))!=-1)
{
out.write(buf,0,len);
}

//告诉服务端数据已写完
s.shutdownOutput();

InputStream in = s.getInputStream();

byte[] bufIn = new byte[1024];

int num = in.read(bufIn);
System.out.println(new String(bufIn,0,num));

fis.close();
s.close();
}
}
class PicThread implements Runnable
{

private Socket s;
PicThread(Socket s)
{
this.s = s;
}
public void run()
{

int count = 1;
String ip = s.getInetAddress().getHostAddress();
try
{
System.out.println(ip+"....connected");

InputStream in = s.getInputStream();

File dir = new File("d:\\pic");

File file = new File(dir,ip+"("+(count)+")"+".jpg");

while(file.exists())
file = new File(dir,ip+"("+(count++)+")"+".jpg");

FileOutputStream fos = new FileOutputStream(file);

byte[] buf = new byte[1024];

int len = 0;
while((len=in.read(buf))!=-1)
{
fos.write(buf,0,len);
}

OutputStream out = s.getOutputStream();

out.write("上传成功".getBytes());

fos.close();

s.close();
}
catch (Exception e)
{
throw new RuntimeException(ip+"上传失败");
}
}
}



class PicServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10007);

while(true)
{
Socket s = ss.accept();

new Thread(new PicThread(s)).start();


}
}
}

练习:客户端通过键盘录入用户名。服务端对这个用户名进行校验。
如果该用户存在,在服务端显示xxx,已登陆。并在客户端显示 xxx,欢迎光临。如果该用户存在,在服务端显示xxx,尝试登陆。
并在客户端显示 xxx,该用户不存在。最多就登录三次。
代码示例:

class LoginClient
{

public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.254",10008);

BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));

PrintWriter out = new PrintWriter(s.getOutputStream(),true);

BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));


for(int x=0; x<3; x++)
{
String line = bufr.readLine();
if(line==null)
break;
out.println(line);

String info = bufIn.readLine();
System.out.println("info:"+info);
if(info.contains("欢迎"))
break;

}

bufr.close();
s.close();
}
}


class UserThread implements Runnable
{

private Socket s;
UserThread(Socket s)
{
this.s = s;
}
public void run()
{
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
try
{
for(int x=0; x<3; x++)
{
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

String name = bufIn.readLine();
if(name==null)
break;

BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));

PrintWriter out = new PrintWriter(s.getOutputStream(),true);

String line = null;

boolean flag = false;
while((line=bufr.readLine())!=null)
{
if(line.equals(name))
{
flag = true;
break;
}
}

if(flag)
{
System.out.println(name+",已登录");
out.println(name+",欢迎光临");
break;
}
else
{
System.out.println(name+",尝试登录");
out.println(name+",用户名不存在");
}


}
s.close();
}
catch (Exception e)
{
throw new RuntimeException(ip+"校验失败");
}
}
}
class LoginServer
{

public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10008);

while(true)
{
Socket s = ss.accept();

new Thread(new UserThread(s)).start();
}
}
}

接下来我们演示几种客户端服务端的组合:

  1. 客户端:浏览器 (telnet) 服务端:自定义。
  2. 客户端:浏览器。服务端:Tomcat服务器。
  3. 客户端:自定义。(图形界面)服务端:Tomcat服务器。

第一种、客户端:浏览器 (telnet) 服务端:自定义 。
服务端代码示例:

ServerSocket ss = new ServerSocket(11000);

Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress());

InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String(buf,0,len));


PrintWriter out = new PrintWriter(s.getOutputStream(),true);

out.println("<font color='red'>客户端你好</font>");

s.close();

ss.close();

当我们在浏览器地址栏中输入http://localhost:11000 的时候页面会显示我们返回的数据并且将html代码解析,出现”客户端你好”的红色字体。

第二种:客户端:浏览器,服务端:Tomcat服务器
我们启动tomcat服务器,在浏览器地址栏中输入http://localhost:8080 这是浏览器会显示tomcat服务页面。

第三种:客户端:自定义浏览器,服务端:Tomcat服务器
客户端代码:

Socket s = new Socket("192.168.1.254",8080);

PrintWriter out = new PrintWriter(s.getOutputStream(),true);

out.println("GET /myweb/demo.html HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: zh-cn");
out.println("Host: 192.168.1.254:11000");
out.println("Connection: closed");

out.println();
out.println();

BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));

String line = null;

while((line=bufr.readLine())!=null)
{
System.out.println(line);
}

s.close();

我们模仿浏览器给tomcat服务发送数据,会发送请求头。请求头的一般格式为 :
GET /myweb/demo.html HTTP/1.1
Accept: application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, i
mage/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application */ *
/msword, application/QVOD, application/QVOD,
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0
.50727)
Host: 192.168.1.254:11000
Connection: Keep-Alive
我们也模仿这个给服务端发送数据,但是我们得到返回的数据其实是包含了响应头 + 空行+返回的包体内容。
为什么在浏览器中就没有响应头而,我么自己用Socket得到的返回结果就包含有响应头信息呢?这是因为我么所用的Socket 和ServerSocket 是传输层的,而浏览器是应用层的。因此浏览器讲传输层的东西层层解包,最后显示的是响应包体的内容。下面我们将介绍java在应用层的对象,我们得到的就只有包体内容了。

URL:表示一个统一资源标识符 (URI) 引用。 URI 是统一资源标识符,而 URL 是统一资源定位符。因此,笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。
主要常用方法:
String getFile()
获取此 URL 的文件名。
String getHost()
获取此 URL 的主机名(如果适用)。
String getPath()
获取此 URL 的路径部分。
int getPort()
获取此 URL 的端口号。
String getProtocol()
获取此 URL 的协议名称。
String getQuery()
获取此 URL 的查询部
代码示例:

URL url = new URL("http://192.168.1.254:10010/myweb/demo.html?name=haha&age=30");


System.out.println("getProtocol() :"+url.getProtocol());//http
System.out.println("getHost() :"+url.getHost());//192.168.1.254
System.out.println("getPort() :"+url.getPort());// 10010如果我们用的是默认端口80的话此处是-1
System.out.println("getPath() :"+url.getPath());// /myweb/demo.html
System.out.println("getFile() :"+url.getFile());// /myweb/demo.html?name=haha&age=30
System.out.println("getQuery() :"+url.getQuery());// name=haha&age=30

URLConnection:这个就是在应用层上的对象。
使用代码示例:

URL url = new URL("http://192.168.1.254:8080/myweb/demo.html");

URLConnection conn = url.openConnection();
System.out.println(conn);

InputStream in = conn.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String(buf,0,len));

上面代码的输出就不会含有响应头等信息啦。我们接下来对URLConnection做一些介绍。URLConnection是一个抽象类,我们常用的或者url.openConnection();返回的其实是他的子类HttpURLConnection。
代码示例:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tdr.mbs.service.external.flow.bean.FlowType;
import com.tdr.mbs.service.external.flow.cons.IFlowConstant;
import com.tdr.mbs.utils.common.MD5Util;

/**
* 流量充值工具类 (聚合数据)
*
* @author CLMA
*
*/

public class FlowRechargeUtil implements IFlowConstant {

private static final Logger LOG = LoggerFactory.getLogger(FlowRechargeUtil.class);

public static final String DEF_CHATSET = "UTF-8";
public static final int DEF_CONN_TIMEOUT = 30000;
public static final int DEF_READ_TIMEOUT = 30000;
public static String userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";
/** 全部流量套餐地址 **/
public static final String REQUEST1_URL = "http://v.juhe.cn/flow/list";
/** 按号码检查流量套餐地址 **/
public static final String REQUEST2_URL = "http://v.juhe.cn/flow/telcheck";
/** 流量充值地址 **/
public static final String REQUEST3_URL = "http://v.juhe.cn/flow/recharge";
/** 订单查询地址 **/
public static final String REQUEST4_URL = "http://v.juhe.cn/flow/batchquery";

// 配置您申请的KEY
public static String appkey;
public static String openId;

/**
* 全部流量套餐列表
*
* @return
*/

public static String getFlowList() {
LOG.info("enter getFlowList");
String result = null;
Map<String, Object> params = new HashMap<String, Object>();// 请求参数
params.put("key", appkey);// 应用APPKEY(应用详细页查询)
try {
result = net(REQUEST1_URL, params, "POST");
if (LOG.isDebugEnabled()) {
LOG.debug("get product result:{}", result);
}
} catch (Exception e) {
e.printStackTrace();
LOG.error("get url error", e);
}
return result;
}

/**
* 检测号码支持的流量套餐
*
* @param phone
* @return
*/

public static String getFlowListByPhone(String phone) {
LOG.info("enter getFlowListByPhone. phone:{}", phone);
String result = null;
Map<String, Object> params = new HashMap<String, Object>();// 请求参数
params.put("phone", phone);// 要查询的手机号码
params.put("key", appkey);// 应用APPKEY(应用详细页查询)
try {
result = net(REQUEST2_URL, params, "POST");
if (LOG.isDebugEnabled()) {
LOG.debug("get product result:{}", result);
}
} catch (Exception e) {
e.printStackTrace();
LOG.error("get url error", e);
}
return result;
}

/**
* 提交流量充值
*
* @param phone
* 需要充值流量的手机号码
* @param pid
* 流量套餐ID
* @param orderid
* 自定义订单号,8-32字母数字组合
*/

public static String flowRecharge(String phone, String pid, String orderid) {
LOG.info("enter flowRecharge. phone:{},pid:{},orderid:{}", phone, pid, orderid);
String result = null;
Map<String, Object> params = new HashMap<String, Object>();// 请求参数
params.put("phone", phone);// 需要充值流量的手机号码
params.put("pid", pid);// 流量套餐ID
params.put("orderid", orderid);// 自定义订单号,8-32字母数字组合
params.put("key", appkey);// 应用APPKEY(应用详细页查询)
String sign = MD5Util.getMD5(
new StringBuilder().append(openId).append(appkey).append(phone).append(pid).append(orderid).toString())
.toLowerCase();
params.put("sign", sign);// 校验值,md5(<b>OpenID</b>+key+phone+pid+orderid),结果转为小写
try {
result = net(REQUEST3_URL, params, "POST");
if (LOG.isDebugEnabled()) {
LOG.debug("get product result:{}", result);
}
} catch (Exception e) {
e.printStackTrace();
LOG.error("get url error", e);
result = ERROR_STRING;
}
return result;
}

/**
* 订单状态查询
*
* @param orderid
* 订单号
* @return
*/

public static String checkOrderStatus(String orderid) {
LOG.info("enter checkOrderStatus. orderid:{}", orderid);
String result = null;
Map<String, Object> params = new HashMap<String, Object>();// 请求参数
params.put("orderid", orderid);// 用户订单号,多个以英文逗号隔开,最大支持50组
params.put("key", appkey);// 应用APPKEY(应用详细页查询)
try {
result = net(REQUEST4_URL, params, "POST");
if (LOG.isDebugEnabled()) {
LOG.debug("get product result:{}", result);
}
} catch (Exception e) {
e.printStackTrace();
LOG.error("get url error", e);
}
return result;
}

// 5.充值订单列表
public static void getRequest5() {
String result = null;
String url = "http://v.juhe.cn/flow/orderlist";// 请求接口地址
Map<String, Object> params = new HashMap<String, Object>();// 请求参数
params.put("pagesize", "");// 每页返回条数,最大200,默认50
params.put("page", "");// 页数,默认1
params.put("phone", "");// 指定要查询的手机号码
params.put("key", appkey);// 应用APPKEY(应用详细页查询)
try {
result = net(url, params, "GET");
JSONObject object = JSONObject.parseObject(result);
if (object.getInteger("error_code") == 0) {
System.out.println(object.get("result"));
} else {
System.out.println(object.get("error_code") + ":" + object.get("reason"));
}
} catch (Exception e) {
e.printStackTrace();
}
}

// 6.运营商状态查询
public static void getRequest6() {
String result = null;
String url = "http://v.juhe.cn/flow/operatorstate";// 请求接口地址
Map<String, Object> params = new HashMap<String, Object>();// 请求参数
params.put("key", appkey);// 应用APPKEY(应用详细页查询)

try {
result = net(url, params, "GET");
JSONObject object = JSONObject.parseObject(result);
if (object.getInteger("error_code") == 0) {
System.out.println(object.get("result"));
} else {
System.out.println(object.get("error_code") + ":" + object.get("reason"));
}
} catch (Exception e) {
e.printStackTrace();
}
}

/**
*
* @param strUrl
* 请求地址
* @param params
* 请求参数
* @param method
* 请求方法
* @return 网络请求字符串
* @throws Exception
*/

public static String net(String strUrl, Map<String, Object> params, String method) {
HttpURLConnection conn = null;
BufferedReader reader = null;
String rs = null;
try {
StringBuffer sb = new StringBuffer();
if (method == null || method.equals("GET")) {
strUrl = strUrl + "?" + urlencode(params);
}
URL url = new URL(strUrl);
conn = (HttpURLConnection) url.openConnection();
if (method == null || method.equals("GET")) {
conn.setRequestMethod("GET");
} else {
conn.setRequestMethod("POST");
conn.setDoOutput(true);
}
conn.setRequestProperty("User-agent", userAgent);
conn.setUseCaches(false);
conn.setConnectTimeout(DEF_CONN_TIMEOUT);
conn.setReadTimeout(DEF_READ_TIMEOUT);
conn.setInstanceFollowRedirects(false);
conn.connect();
if (params != null && method.equals("POST")) {
try {
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.writeBytes(urlencode(params));
} catch (Exception e) {
// TODO: handle exception
// throw new RuntimeException();
}
}
InputStream is = conn.getInputStream();
reader = new BufferedReader(new InputStreamReader(is, DEF_CHATSET));
String strRead = null;
while ((strRead = reader.readLine()) != null) {
sb.append(strRead);
}
rs = sb.toString();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
LOG.error("reader.close id error", e);
}
}
if (conn != null) {
conn.disconnect();
}
}
return rs;
}

// 将map型转为请求参数型
public static String urlencode(Map<String, Object> data) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> i : data.entrySet()) {
try {
sb.append(i.getKey()).append("=").append(URLEncoder.encode(i.getValue() + "", "UTF-8")).append("&");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return sb.toString();
}

public static void main(String[] args) {
checkOrderStatus("10000000");
}

public static String getAppkey() {
return appkey;
}

public static void setAppkey(String appkey) {
FlowRechargeUtil.appkey = appkey;
}

public static String getOpenId() {
return openId;
}

public static void setOpenId(String openId) {
FlowRechargeUtil.openId = openId;
}
}