学习目标:掌握IP地址与域名的关系,访问互联资源的方法,发送HTTP请求到服务器。
IP地址与域名
学习网络编程的第一步就是要认识IP地址,IP地址是计算机在网络的唯一标识,在互联网中,如果一台计算机要与另一台计算机通讯,就必须要有自己的IP地址,类似于每个人都要有自己的手机号码一样。
IP地址使用32位的二进制数据表示。例如:某一台计算机的IP地址为:
11001010 1110100 00000000 00000001
为了方便记忆,将IP地址的32位二进制数分为4段,每段8位用十进制整数表示,上面的IP地址可以表示为:
202.116.0.1
这样计算机的IP地址就比较容易记忆了。
IP地址由网络地址和主机地址构成。网络地址表示网络中的某一个网段,主机地址表示网段上的某一个主机。
多台计算机通过通信线路和通信设备连接在一起就构成了计算机网络,世界上最大的网络就是互联网,互联网之外还有很多大大小小、规模不一的网络,如校园网、社会机构和企业的内部网等等,这些网络一般都与互联网连接。
国际互联网工程任务组(简称IETF)为了让IP地址能够表示规模不一的网络,制定了IP编址方案。该方案将IP地址地址空间划分为A、B、C、D、E五种类型,每类有不同长度的网络地址和主机地址。

按照IP编址方案,每类的IP地址范围如下:
A类IP地址范围:1.0.0.0~126.255.255.255
B类IP地址范围:128.0.0.0~191.255.255.255
C类IP地址范围:192.0.0.1~223.255.255.255
D类IP地址范围:224.0.0.0~239.255.255.255
E类IP地址范围:240.0.0.0~255.255.255.255
现在IP地址编址方案有IPV4和IPV6两个版本,当前使用最多的是IPV4版本,IPV6的IP地址长度是128位,比IPV4能够容纳更多的网络和主机。
由于数字型的IP地址很难记忆。所以在互联网中,大多使用简单明了的、由字符串组成的、有规律的、容易记忆的名字来代表互联网上的主机,这就是域名,如www.baidu.com。
在Java程序中,如何操作IP地址呢?例如希望查看某一网站域名的IP地址是多少?本机的IP地址是多少?
Java提供了InetAddress类来操作IP地址,InetAddress类有两个子类分别是Inet4Address和Inet6Address,用于操作IPV4地址和IPV6地址。InetAddress类在java.net包内。
InetAddress类的常用方法说明如下:
● static InetAddress getByName(String host)
该方法返回与host主机名称相关的InetAddress 对象,通过InetAddress对象的getHostAddress()方法可以获取host主机的IP地址。主机名可以是机器名,也可以是域名如java.sun.com。
● static InetAddress getLocalHost()
该方法返回本地主机的InetAddress对象。
● String getHostName()
返回此IP地址的主机名。如果此InetAddress是使用主机名创建的,则将记住并返回此主机名;否则,将执行反向名称查找,并根据系统配置的名称查找服务返回结果。
● boolean isReachable(int timeout)
该方法测试该IP地址是否可访问。方法会尽最大努力尝试访问主机,如果访问成功返回true,否则返回false。
案例1:建立InetAddressTest类,输出www.baidu.com域名的IP地址,输出本机IP地址。
新建项目PUnit16,在PUnit16项目新建ip包,在ip包下新建InetAddressTest类。代码如下:
package ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressTest {
public static void main(String[] args) {
try {
// 输出域名www.baidu.com的IP地址
InetAddress adderss1 = InetAddress.getByName("www.baidu.com");
System.out.println("www.baidu.com域名的IP地址为:" + adderss1.getHostAddress());
// 输出本机IP地址
InetAddress adderss2 = InetAddress.getLocalHost();
System.out.println("本机IP地址为:" + adderss2.getHostAddress());
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
InetAddressTest程序调用InetAddress类的静态方法getByName()方法获取指定域名的IP地址,调用InetAddress类的静态方法getLocalHost()获取本机的IP地址。InetAddress类在java.net包内,需要使用import语句导入该类。
程序执行结果如下图所示:

访问互联网资源
程序要访问互联网资源,就需要用到URL。URL是Uniform Resource Locator的简称(统一资源定位),它是互联网资源的地址,它实际上就是一个互联网资源的访问路径。
例如下面的互联网资源访问路径就是一个URL:
http://www.milihua.com/v9.apk
如果希望下载编程训练营app,可以在浏览器地址栏中输入上面的URL地址,就可以下载编程训练营app到本地。
URL资源可以通过浏览器直接访问,当然也可以使用Java程序直接访问URL资源,使用程序访问URL资源的前提是URL资源能够允许外部程序访问。
在 java.net 包中包含了专门用来处理 URL 的类,该类的名称就是URL,程序可以通过URL类获取 URL 的相关信息、访问互联网上的资源。使用URL类和InputStream类可以读取URL资源。 URL类常用方法说明如下:
● URL(String spec)
构造方法,根据指定spec创建一个URL对象。Spec为能够解析为URL的字符串。
● InputStream openStream()
该方法打开指向此URL的连接并返回一个用于从该连接读取的InputStream。
● URLConnection openConnection()
该方法返回一个URLConnection实例对象,该实例对象表示到由URL引用的远程对象的连接。URLConnection实例在创建时并没有建立实际的网络连接。只有在调用URLConnection.connect()时才会连接远程对象。如果对于URL的协议(如HTTP或JAR),存在一个公共的、专用的URL连接子类,属于以下包之一或它们的子包中的一个:JavaLang.JavaIO、JavaUTL、Java.net,返回的连接将是该子类。例如,对于HTTP,将返回HttpURLConnection,对于JAR,将返回JarURLConnection。
案例2:建立URLTest类,从指定网站读取特定网页文件,并将网页文件内容输出到控制台。
在ip包下新建URLTest类。代码如下:
package ip;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
public class URLTest {
public static void main(String[] args) {
try {
// 打开URL资源
URL url = new URL("http://milihua.com/hello.html");
// 获取输入流,读取URL内容
InputStream stream = url.openStream();
if( null != stream )
{
// 实例化文本缓存输入流,字符编码设置为UTF-8
BufferedReader rd = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));
// 调用readAll从输入流中读取内容
String content = readAll(rd);
System.out.print(content);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static String readAll(Reader rd) throws IOException {
// 实例化字符串生成器
StringBuilder sb = new StringBuilder();
int cp;
// 循环读取输入流的内容,返回-1表示读取完成
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
}
URLTest程序完成读取网页文件的任务。程序应用URL类访问hello.html网页资源,并获取hello.html网页资源的输入流。利用获取的输入流实例化BufferedReader对象,设置字符编码为UTF-8,hello.html网页资源的字符编码也是UTF-8,这样可以确保读取中文字符时不会产生乱码。程序的readAll()方法从Read输入流按字节循环读取,读取结束后返回一个字符串。
URLTest程序用到了URL类、InputStream类、BufferedReader类、InputStreamReader类,需要使用import语句导入这些类。另外,URL类和InputStream类都会抛出异常,程序需要处理这些异常。
发送HTTP请求到服务器
当Java程序需要向服务器发送请求或读取服务器数据时,使用URLConnection类是比较好的选择。URLConnection类封装了与服务器互动操作的方法,通过它可以建立与服务器的远程连接,检查服务器资源的属性,向服务器发送请求并接收服务器返回的数据。
URLConnection类是一个抽象类,它不能直接实例化为对象,但URL实例对象可以返回一个URLConnection类的子类对象。返回的子类对象与URL打开的网络资源采用的协议有关。如果是HTTP协议,URL实例对象会返回HttpURLConnection对象。如果是FTP协议,URL实例对象会返回FtpURLConnection对象。
URLConnection类常用方法说明如下:
● void connect()
该方法用于与远程资源建立连接。如果尚未建立此连接,则打开指向此URL引用的资源链接。如果在连接已打开时调用connect方法(由值为true的connected字段指示),则忽略该调用。
● void setRequestProperty(String key, String value)
该方法用于设置网络资源请求属性。Key参数为已知请求的关键字(例如,“Accept”),value是与key关联的值。
● void setReadTimeout(int timeout)
该方法用于设置读取网络资源数据超时时间(以毫秒为单位)。
● void setConnectTimeout(int timeout)
该方法用于设置连接网络资源的超时时间(以毫秒为单位)
● InputStream getInputStream()
该方法用于返回从这个打开的连接读取的输入流。
HttpURLConnection类常用方法说明如下:
● int getResponseCode()
该方法从HTTP响应消息获取状态代码。返回200表示请求响应成功,其它值表示请求响应失败。
● void setRequestMethod(String method)
该方法设置HTTP请求方法之一:GET/POST/HEAD/OPTIONS/PUT/DELETE/TRACE。默认是GET。
案例3:建立URLConnectionTest类,连接远程服务器,向服务器发送服务请求,并接收服务器的请求响应数据。
案例连接的远程服务器地址:
http://milihua.com/sum.aspx
该远程服务提供求两数和服务,客户端向该服务发送服务请求,并向该服务提交两个待求和的整数,该服务返回求和结果。
在ip包下新建URLConnectionTest类。代码如下:
package ip;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class URLConnectionTest {
public static void main(String[] args) {
HttpURLConnection connection = null;
InputStream stream = null;
BufferedReader reader = null;
String server = "http://milihua.com/sum.aspx?opone=30&optwo=60";
try {
// 实例化URL对象
URL url = new URL(server);
// 通过URL对象打开一个连接,显示转换为httpURLConnection类
connection = (HttpURLConnection) url.openConnection();
// 设置连接方式:get
connection.setRequestMethod("GET");
// 设置连接远程服务的超时时间:15000毫秒
connection.setConnectTimeout(15000);
// 设置读取远程服务返回的数据时间:60000毫秒
connection.setReadTimeout(60000);
// 建立连接,发送请求
connection.connect();
// 通过connection连接,获取输入流
if (connection.getResponseCode() == 200) {
// 从打开的连接获取输入流,接收返回数据
stream = connection.getInputStream();
// 实例化BufferedReader对象,并指定字符集
reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
// 实例化字符串生成器
StringBuffer sbf = new StringBuffer();
String temp = null;
// 从输入流读取数据
while ((temp = reader.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
// 返回内容输出到控制台
System.out.println("远程服务返回的数据:" + sbf.toString());
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != stream) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 关闭远程连接
connection.disconnect();
}
}
}
URLConnectionTest程序的主要功能是请求一个求两数和的远程服务,并向远程服务提交待求和的两个整数,远程服务计算两数的和,并将计算结果返回给程序,程序将计算结果输出到控制台。
程序使首先用URL对象定位和打开远程服务,并应用URL对象的openConnection()方法获取HttpURLConnection实例对象。然后应用HttpURLConnection实例对象分别设置远程服务的连接超时时间、服务响应超时时间、读取数据超时时间。再调用HttpURLConnection实例对象的connect()方法向远程服务发送请求,并调用HttpURLConnection实例对象的getResponseCode()方法判断远程服务是否响应成功,如果返回值是200,说明服务器响应成功。最后读取远程服务返回的数据,并将读取的数据输出到控制台。
TCP/IP网络协议
两台计算机之间进行通信,需要相互约定通信的方式。例如两台计算机之间如何进行物理连接(用线连接起来),通信的数据如何组织,在数据通信过程中,发送方和接收方如何保证传输的数据没有丢失、如何保证传输数据的安全。这种约定就是两台计算机之间的通信协议。
网络由多台计算机连接而成,也包括用于连接的通信线路和通信设备,而且网络覆盖的地理范围也非常广泛,网络有局域网和广域网,局域网的地理范围比较小,通常不超过几十公里,甚至只在一幢建筑或一个房间内。广域网的地理覆盖范围就非常广泛,可以说只要是通信线路到达的地方都能覆盖。
网络协议就是用来规范和约束网络内的计算机、通信设备和通信线路的相互连接、数据发送与接收、网络间的计算机寻址等内容。
TCP/IP 就是为使用互联网而开发制定的协议族。TCP/IP是一组协议,它不是一个单独的协议,它由网络接口层协议、网络层协议、传输层协议、应用层协议四组协议构成,每组协议又有多个协议组成。
由TCP/IP协议族构成了TCP/IP网络模式,该网络模式是层次结构,共分为四层,分别为应用层、传输层、网络层、数据链路层。如下图所示:

数据链路层协议包括SLIP协议、ARP(IP地址解析)等协议。
网络层协议实现不同计算机、不同操作系统、不同网络结构的互联,在互联网上有数以万计的计算机,每一台计算机在网络上通过为其分配的网络地址来表示自己,这个网络地址就是IP地址,所用的协议就是IP协议。
网络层协议还包括路由控制协议,互联网是路由器连接的网络组合而成,路由器根据路由控制表转发数据包,它根据所接收到数据包中目标计算机的IP地址与路由控制表的比较得出下一个应该接收的路由器。因此这个过程中一定要准确,不然有可能数据包无法到达目标计算机。
传输层协议包括TCP和UDP协议,这两个协议负责数据在网络间的传输和控制,也是编写网络应用程序的开发者应该了解的。
TCP协议提供可靠的从一端到另一端的传输服务,TCP把要传输的数据分成多个数据包,每个数据包都标上目标计算机的IP地址和序号。在传输之前,发送方先要发送信息给目标计算机,目标计算机回应后,才开始传输数据包,当目标计算机接收完成一个数据包后,会给发送方发送收到确认信息,如果发送方在规定的时间没有收到确认信息,发送方会重新发送该数据包。目标计算机接收完所有的数据包后,会按照数据包的序号把多个数据包组装起来。
UDP协议是无连接的通信协议,不保证可靠的数据传输,但能够向若干目标发送数据,目标计算机也可以接收多个发送方的数据。
应用层协议包括HTTP、FTP等协议。
HTTP协议由请求和响应构成,是一个标准的客户端/服务器模型。浏览器浏览网页采用的是HTTP协议,手机中微信等APP程序和服务器端的通信也是采用的HTTP协议。
FTP协议是互联网中使用最广泛的文件传输协议,用于计算机之间的文件传输。FTP最常见的应用就是通过FTP协议构建FTP服务器,客户端从FTP服务器下载文件,也可以上传文件到FTP服务器。
端口和套接字
当网络中的两台计算机进行通信时,除了确定计算机在网络中的IP地址外,还需要确定计算机中的一个端口,端口并不是实际的物理设备,它是一个应用程序,这个应用程序来负责两台计算机的通信。
一个IP地址标识了一台主机(服务器),主机可以提供多种服务,如web服务、ftp服务、远程桌面等。主机的每个服务都会等待客户端的连接,客户端如何区别这些服务呢?这就需要端口来区分了。
端口被规定为一个在0~65535之间的整数,这个整数和提供服务的应用程序关联。如web服务一般是80端口,ftp服务一般是21端口、远程桌面一般是3389端口。
当我们通过客户端浏览器访问一个网站时,在浏览器地址栏中输入该网站的网址,并不需要输入80端口号。这是因为web服务默认就是80端口号,当客户端以http协议访问主机时,主机会默认这是访问web服务。
在同一台计算机上端口号不能重复,否则,就会产生端口号冲突。程序员或计算机管理员在分配端口号时,需要遵循下面的规则:
(1)1~1023之间的端口号,是由ICANN来管理的,不能分配给用户自己的应用程序;
(3)1024~5000一般被TCP/IP程序作为临时端口号使用;
(4)分配的端口号不能发生冲突;
(4)应分配数值大于5000的端口号给用户开发的应用程序。
TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。套接字用(IP地址:端口号)表示,区分不同应用程序进程间的网络通信和连接,套接字主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。

在Java语言中,使用Socket类来创建套接字,使用Socket类在两台计算机之间进行通信时,是有主次之分的,一个称为服务器程序,一个称为客户端程序。在服务器端使用ServerSocket类创建套接字,主要是监听指定的端口,等待客户端的连接;在客户端使用Socket类创建套接字,用于连接服务器端的套接字。服务器端和客户端的套接字的IP地址和端口号要一致。

在服务器端创建ServerSocket对象,并绑定监听端口。调用ServerSocket对象的accept()方法监听客户端的请求。与客户端建立连接后,它会返回一个已连接的Socket对象,并通过输入流读取客户端发送的请求信息,然后通过输出流向客户端发送响应信息,最后关闭socket及相关资源。
在客户端创建Socket对象,需要指定连接服务器的地址和端口号,和服务器建立连接后,通过输出流向服务端发送请求信息,然后通过输入流获取服务器的响应信息,最后关闭socket及相关资源。