《疯狂java讲义》学习(47):Java网络基础

版权声明:本文为博主原创文章,如若转载请注明出处 https://blog.csdn.net/tonydz0523/article/details/87531139

1.网络编程的基础知识

1.1 网络基础知识

所谓计算机网络,就是把分布在不同地里区域的计算机与专门的外部设备用通信线路连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源。
计算机网络是现代通信技术与计算机技术相结合的产物,计算机网络可以提供一下一些主要功能:

  • 资源共享。
  • 信息传输与集中处理。
  • 均衡负荷与分布处理。
  • 综合信息服务。

计算机网络的品种很多,根据各种不同的分类原则,可以得到各种不同类型的计算机网络。计算机网络通常是按照规模大小和延伸范围来分类的,常见的划分为:局域网(LAN)、城域网(MAN)、广域网(WAN)。Internet可以视为世界上最大的广域网。
如果按照网络的拓扑结构来划分,可以分为星型网络、总线网络、环线网络、树型网络、星型环线网络等;如果按照网络的传输介质来划分,可以分为双绞线网、同轴电缆网、光纤网和卫星网等。
计算机网络中实现通信必须有一些约定,这些约定被称为通信协议。通信协议负责对传输速率、传输代码、代码结构、传输控制步骤、出错控制等制定处理标准。为了让两个节点之间能进行信息交换。
通信协议通常有三部分组成:一是语义部分,用于决定双方对话的类型;二是语法部分,用于决定双方对话的格式;三是变换规则,用于决定通信双方的应答关系。
国际标准化组织ISO于l978年提出“开放系统互连参考模型”,即著名的OSI(Open System Interconnection)。
开放系统互连参考模型力求将网络简化,并以模块化的方式来设计网络。
开放系统互连参考模型把计算机网络分成物理层、数据链路层、网络层、传输层、会话层、表示层、应用层七层,受到计算机界和通信业的极大关注。通过十多年的发展和推进,OSI模式已成为各种计算机网络结构的参考标准。
前面介绍通过协议是网络通信的基础,IP协议则是一种非常重要的通信协议。IP(Internet Protocol)协议又称互联网协议,是支持网间互联的数据报协议。它提供网间连接的完善功能,包括IP数据报规定互联网络范围内的地址格式。

经常与IP协议放在一起的还有TCP(Transmission Control Protocol)协议,即传输控制协议,它规定一种可靠的数据信息传递服务。虽然IP和TCP这两个协议功能不尽相同,也可以分开单独使用,但它们是在同一个时期作为一个协议来设计的,并且在功能上也是互补的。因此实际使用中常常把这两个协议统称为TCP/IP协议,TCP/IP协议最早出现在UNIX操作系统中,现在几乎所有的操作系统都支持TCP/IP协议,因此TCP/IP协议也是Internet中最常用的基础协议。
按TCP/IP协议模型,网络通常被分为一个四层模型,这个四层模型和前面的OSI七层模型有大致的对应关系:
在这里插入图片描述

1.2 IP地址和端口号

IP地址用于唯一地标识网络中的一个通信实体,这个通信实体既可以是一台主机,也可以是一台打印机,或者是路由器的某一个端口。而在基于IP协议网络中传输的数据包,都必须使用IP地址来进行标识。
IP地址是数字型的,IP地址是一个32位(32bit)整数,但通常为了便于记忆,通常把它分成4个8位的二进制数,每8位之间用圆点隔开,每个8位整数可以转换成一个0~255的十进制整数,因此我们看到的IP地址常常是这种形式:202.9.128.88。
NIC(Internet Network Information Center)统一负责全球Internet IP地址的规划、管理,而Inter NIC、APNIC、RIPE三大网络信息中心具体负责美国及其他地区的IP地址分配。其中APNIC负责亚太地区的IP管理,我国申请IP地址也要通过APNIC,APNIC的总部设在日本东京大学。
IP地址被分成了A、B、C、D、E五类,每个类别的网络标识和主机标识各有规则。

  • A类:10.0.0.0 ~ 10.255.255.255
  • B类:172.16.0.0 ~ 172.31.255.255
  • C类:192.168.0.0 ~192.168.255.255

IP地址用于唯一地标识网络上的一个通信实体,但一个通信实体可以有多个通信程序同时提供网络服务,此时还需要使用端口。
端口是一个16位的整数,用于表示数据交给哪个通信程序处理。因此,端口就是应用程序与外界交流的出入口,它是一种抽象的软件结构,包括一些数据结构和I/O(基本输入/输出)缓冲区。
不同的应用程序处理不同端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口号可以从0到65535,通常将它分为如下三类。

  • 公认端口(Well Known Ports):从0到1023,它们紧密绑定(Binding)一些特定的服务。
  • 注册端口(Registered Ports):从1024到49151,它们松散地绑定一些服务。应用程序通常应该使用这个范围内的端口。
  • 动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535,这些端口是应用程序使用的动态端口,应用程序一般不会主动使用这些端口。

如果把IP地址理解为某个人所在地方的地址(包括街道和门牌号),但仅有地址还是找不到这个人,还需要知道他所在的房号才可以找到这个人。因此如果我们认为应用程序是人,而计算机网络充当类似邮递员的角色,当一个程序需要发送数据时,需要指定目的地的IP地址和端口,如果指定了正确的IP地址和端口号,计算机网络就可以将数据送给该IP地址和端口所对应的程序。

2.Java的基本网络支持

Java为网络支持提供了java.net包,该包下的URL和URLConnection等类提供了以编程方式访问Web服务的功能,而URLDecoder和URLEncoder则提供了普通字符串和application/x-www-form-urlencoded MIME字符串相互转换的静态方法。

2.1 使用InetAddress

Java提供了InetAddress类来代表IP地址,InetAddress下还有两个子类:Inet4Address、Inet6Address,他们分别代表Internet Protocol version 4(IPv4)地址和Internet Protocol version 6(IPv6)地址。
InetAddress类没有提供构造器,而是提供了如下两个静态方法来获取InetAddress实例。

  • getByName(String host):根据主机获取对应的InetAddress对象。
  • getByAddress(byte[] addr):根据原始IP地址来获取对应的InetAddress对象。
    +ddress还提供了如下三个方法来获取InetAddress实例对应的IP地址和主机名。
  • String getCanonicalHostName():获取此IP地址的全限定域名。
  • String getHostAddress():返回该InetAddress实例对应的IP地址字符串(以字符串形式)。
  • String getHostName():获取此IP地址的主机名。

除此之外,InetAddress类还提供了一个getLocalHost()方法来获取本机IP地址对应的InetAddress实例。
InetAddress类还提供了一个isReachable()方法,用于测试是否可以到达该地址。该方法将尽最大努力试图到达主机,但防火墙和服务器配置可能阻塞请求,使得它在访问某些特定的端口时处于不可达状态。如果可以获得权限,典型的实现将使用ICMP ECHO REQUEST;否则它将试图在目标主机的端口7(Echo)上建立TCP连接。下面程序测试了InetAddress类的简单用法。

import java.net.InetAddress;

public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 根据主机名来获取对应的InetAddress实例
        InetAddress ip=InetAddress.getByName("www.baidu.com");
        // 判断是否可达
        System.out.println("baidu是否可达:" + ip.isReachable(2000));
        // 获取该InetAddress实例的IP字符串
        System.out.println(ip.getHostAddress());
        // 根据原始IP地址来获取对应的InetAddress实例
        InetAddress local=InetAddress.getByAddress(
                new byte[]{127,0,0,1});
        System.out.println("本机是否可达:" + local.isReachable(5000));
        // 获取该InetAddress实例对应的全限定域名
        System.out.println(local.getCanonicalHostName());
    }
}

运行程序,结果如下:

blog是否可达:true
61.135.169.121
本机是否可达:true
127.0.0.1

上面程序简单地示范了InetAddress类的几个方法的用法,InetAddress类本身并没有提供太多功能,它代表一个IP地址对象,是网络通信的基础。

2.2 使用URLDecoder和URLEncoder

URLDecoder和URLEncoder用于完成普通字符串和application/x-www-form-urlencoded MIME字符串之间的相互转换。可能有读者觉得后一个字符串非常专业,以为又是什么特别高深的知识,其实不是。
在百度搜索“泛泛之素”的话,会出现下图中的乱码:
在这里插入图片描述
实际上这不是乱码,这就是所谓的application/x-www-form-urlencoded MIME字符串。
当URL地址里包含非西欧字符的字符串时,系统会将这些非西欧字符串转换成上图的特殊字符串。编程过程中可能涉及普通字符串和这种特殊字符串的相关转换,这就需要使用URLDecoder和URLEncoder类。

  • URLDecoder类包含一个decode(String s,String enc)静态方法,它可以将看上去是乱码的特殊字符串转换成普通字符串。
  • URLEncoder类包含一个encode(String s,String enc)静态方法,它可以将普通字符串转换成application/x-www-form-urlencoded MIME字符串。

下面程序示范了如何将地址栏中的“乱码”转换成普通字符串,并示范了如何将普通字符串转换成application/x-www-form-urlencoded MIME字符串。

import java.net.URLDecoder;
import java.net.URLEncoder;

public class URLDecoderTest {
    public static void main(String[] args)
            throws Exception {
        // 将application/x-www-form-urlencoded字符串
        // 转换成普通字符串
        String keyWord= URLDecoder.decode("%E6%B3%9B%E6%B3%9B%E4%B9%8B%E7%B4%A0", "UTF-8");
        System.out.println(keyWord);
        // 将普通字符串转换成
        // application/x-www-form-urlencoded字符串
        String urlStr= URLEncoder.encode("泛泛之素" , "GBK");
        System.out.println(urlStr);
    }
}

运行程序,输出如下:

泛泛之素
%B7%BA%B7%BA%D6%AE%CB%D8

仅包含西欧字符的普通字符串和application/x-www-form-urlencoded MIME字符串无须转换,而包含中文字符的普通字符串则需要转换,转换方法是每个中文字符占2个字节,每个字节可以转换成2个十六进制的数字,所以每个中文字符将转换成“%XX%XX”的形式。当然,采用不同的字符集时,每个中文字符对应的字节数并不完全相同,所以使用URLEncoder和URLDecoder进行转换时也需要指定字符集。

2.3 使用URL和URLConnection

URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂对象的引用,例如对数据库或搜索引擎的查询。在通常情况下,URL可以由协议名、主机、端口和资源组成,即满足如下格式:

protocol://host:post/resourceName

例如如下的URL地址:

https://blog.csdn.net/tonydz0523?ref=toolbar

JDK中还提供了一个URI(Uniform Resource Identifiers)类,其实例代表一个统一资源标识符,Java的URI不能用于定位任何资源,它的唯一作用就是解析。与此对应的是, URL则包含一个可打开到达该资源的输入流,我们可以将URL理解成URI的特例

URL类提供了多个构造器用于创建URL对象,一旦获得了URL对象之后,就可以调用如下方法来访问该URL对应的资源:

  • String getFile():获取该URL的资源名。
  • String getHost():获取该URL的主机名。
  • String getPath():获取该URL的路径部分。
  • int getPort():获取该URL的端口号。
  • String getProtocol():获取该URL的协议名称。
  • String getQuery():获取该URL的查询字符串部分。
  • URLConnection openConnection():返回一个URLConnection对象,它代表了与URL所引用的远程对象的连接。
  • InputStream openStream():打开与此URL的连接,并返回一个用于读取该URL资源的InputStream。

URL对象中的前面几个方法都非常容易理解,而该对象提供的openStream()方法可以读取该URL资源的InputStream,通过该方法可以非常方便地读取远程资源——甚至实现多线程下载。如下程序实现一个多线程下载工具类:

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownUtil {
    // 定义下载资源的路径
    private String path;
    // 指定所下载的文件的保存位置
    private String targetFile;
    // 定义需要使用多少个线程下载资源
    private int threadNum;
    // 定义下载的线程对象
    private DownThread[] threads;
    // 定义下载的文件的总大小
    private int fileSize;

    public DownUtil(String path, String targetFile, int threadNum) {
        this.path = path;
        this.threadNum = threadNum;
        // 初始化threads数组
        threads = new DownThread[threadNum];
        this.targetFile = targetFile;
    }

    public void download() throws Exception {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5 * 1000);
        conn.setRequestMethod("GET");
        conn.setRequestProperty(
                "Accept",
                "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                        + "application/x-shockwave-flash, application/xaml+xml, "
                        + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                        + "application/x-ms-application, application/vnd.ms-excel, "
                        + "application/vnd.ms-powerpoint, application/msword, */*");
        conn.setRequestProperty("Accept-Language", "zh-CN");
        conn.setRequestProperty("Charset", "UTF-8");
        conn.setRequestProperty("Connection", "Keep-Alive");
        // 得到文件大小
        fileSize = conn.getContentLength();
        conn.disconnect();
        int currentPartSize = fileSize / threadNum + 1;
        RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
        // 设置本地文件的大小
        file.setLength(fileSize);
        file.close();
        for (int i = 0; i < threadNum; i++) {
            // 计算每个线程下载的开始位置
            int startPos = i * currentPartSize;
            // 每个线程使用一个RandomAccessFile进行下载
            RandomAccessFile currentPart = new RandomAccessFile(targetFile,
                    "rw");
            // 定位该线程的下载位置
            currentPart.seek(startPos);
            // 创建下载线程
            threads[i] = new DownThread(startPos, currentPartSize,
                    currentPart);
            // 启动下载线程
            threads[i].start();
        }
    }

    // 获取下载的完成百分比
    public double getCompleteRate() {
        // 统计多个线程已经下载的总大小
        int sumSize = 0;
        for (int i = 0; i < threadNum; i++) {
            sumSize += threads[i].length;
        }
        // 返回已经完成的百分比
        return sumSize * 1.0 / fileSize;
    }

    private class DownThread extends Thread {
        // 当前线程的下载位置
        private int startPos;
        // 定义当前线程负责下载的文件大小
        private int currentPartSize;
        // 当前线程需要下载的文件块
        private RandomAccessFile currentPart;
        // 定义该线程已下载的字节数
        public int length;

        public DownThread(int startPos, int currentPartSize,
                          RandomAccessFile currentPart) {
            this.startPos = startPos;
            this.currentPartSize = currentPartSize;
            this.currentPart = currentPart;
        }

        public void run() {
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setConnectTimeout(5 * 1000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty(
                        "Accept",
                        "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                                + "application/x-shockwave-flash, application/xaml+xml, "
                                + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                                + "application/x-ms-application, application/vnd.ms-excel, "
                                + "application/vnd.ms-powerpoint, application/msword, */*");
                conn.setRequestProperty("Accept-Language", "zh-CN");
                conn.setRequestProperty("Charset", "UTF-8");
                InputStream inStream = conn.getInputStream();
                // 跳过startPos个字节,表明该线程只下载自己负责的那部分文件
                inStream.skip(this.startPos);
                byte[] buffer = new byte[1024];
                int hasRead = 0;
                // 读取网络数据,并写入本地文件
                while (length < currentPartSize
                        && (hasRead = inStream.read(buffer)) != -1) {
                    currentPart.write(buffer, 0, hasRead);
                    // 累计该线程下载的总大小
                    length += hasRead;
                }
                currentPart.close();
                inStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

上面程序中定义了DownThread线程类,该线程负责读取从start开始,到end结束的所有字节数据,并写入RandomAccessFile对象。这个DownThread线程类的run()方法就是一个简单的输入、输出实现。
程序中DownUtils类中的download()方法负责按如下步骤来实现多线程下载:

  1. 创建URL对象。
  2. 获取指定URL对象所指向资源的大小(通过getContentLength()方法获得),此处用到了URLConnection类,该类代表Java应用程序和URL之间的通信链接。后面还有关于URLConnection更详细的介绍。
  3. 在本地磁盘上创建一个与网络资源具有相同大小的空文件。
  4. 计算每个线程应该下载网络资源的哪个部分(从哪个字节开始,到哪个字节结束)。
  5. 依次创建、启动多个线程来下载网络资源的指定部分。

有了上面的DownUtil工具类之后,接下来就可以在主程序中调用该工具类的down()方法执行下载,如下程序所示:

public class MultiThreadDown {
    public static void main(String[] args) throws Exception {
        // 初始化DownUtil对象
        final DownUtil downUtil=new DownUtil("https://avatar.csdnimg.cn/F/C/D/1_tonydz0523.jpg"
                , "face.jpg", 2);
        // 开始下载
         downUtil.download();
         new Thread() {
            public void run() {
                while(downUtil.getCompleteRate() < 1) {
                    // 每隔0.1秒查询一次任务的完成进度
                    // GUI程序中可根据该进度来绘制进度条
                    System.out.println("已完成:"
                            + downUtil.getCompleteRate());
                    try {
                        Thread.sleep(1000);
                    }
                    catch (Exception ex){}
                }
            }
        }.start();
    }
}

运行上面程序,可以看到下载了我博客的头像。
上面程序还用到URLConnection和HttpURLConnection对象,其中前者表示应用程序和URL之间的通信连接,后者表示与URL之间的HTTP连接。程序可以通过URLConnection实例向该URL发送请求、读取URL引用的资源。
通常创建一个和URL的连接,并发送请求、读取此URL引用的资源需要如下几个步骤。

  1. 通过调用URL对象的openConnection()方法来创建URLConnection对象。
  2. 设置URLConnection的参数和普通请求属性。
  3. 如果只是发送GET方式请求,则使用connect()方法建立和远程资源之间的实际连接即可;如果需要发送POST方式的请求,则需要获取URLConnection实例对应的输出流来发送请求参数。
  4. 远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源的数据。

在建立和远程资源的实际连接之前,程序可以通过如下方法来设置请求头字段。

  • setAllowUserInteraction():设置该URLConnection的allowUserInteraction请求头字段的值。
  • setDoInput():设置该URLConnection的doInput请求头字段的值。
  • setDoOutput():设置该URLConnection的doOutput请求头字段的值。
  • setIfModifiedSince():设置该URLConnection的ifModifiedSince请求头字段的值。
  • setUseCaches():设置该URLConnection的useCaches请求头字段的值。
    +外,还可以使用如下方法来设置或增加通用头字段。
  • setRequestProperty(String key, String value):设置该URLConnection的key请求头字段的值为value。如下代码所示:
conn.setRequestProperty("accept" , "*/*")
  • addRequestProperty(String key, String value):为该URLConnection的key请求头字段增加value值,该方法并不会覆盖原请求头字段的值,而是将新值追加到原请求头字段中。

当远程资源可用之后,程序可以使用以下方法来访问头字段和内容:

  • Object getContent():获取该URLConnection的内容。
  • String getHeaderField(String name):获取指定响应头字段的值。
  • getInputStream():返回该URLConnection对应的输入流,用于获取URLConnection响应的内容。
  • getOutputStream():返回该URLConnection对应的输出流,用于向URLConnection发送请求参数。

getHeaderField()方法用于根据响应头字段来返回对应的值。而某些头字段由于经常需要访问,所以Java提供了以下方法来访问特定响应头字段的值。

  • getContentEncoding():获取content-encoding响应头字段的值。
  • getContentLength():获取content-length响应头字段的值。
  • getContentType():获取content-type响应头字段的值。
  • getDate():获取date响应头字段的值。
  • getExpiration():获取expires响应头字段的值。
  • getLastModified():获取last-modified响应头字段的值。

如果既要使用输入流读取URLConnection响应的内容,又要使用输出流发送请求参数,则一定要先使用输出流,再使用输入流。

下面程序示范了如何向Web站点发送GET请求、POST请求,并从Web站点取得响应:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

public class GetPostTest {
    /**
    * 向指定URL发送GET方式的请求
    * @param url 发送请求的URL
    * @param param 请求参数,格式满足name1=value1&name2=value2的形式
    * @return URL代表远程资源的响应
    */
    public static String sendGet(String url , String param) {
        String result="";
        String urlName=url + "?" + param;
        try {
            URL realUrl=new URL(urlName);
            // 打开和URL之间的连接
            URLConnection conn=realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent"
                    , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            // 建立实际的连接conn.connect();            // 获取所有的响应头字段
            Map<String, List<String>> map=conn.getHeaderFields();
            // 遍历所有的响应头字段
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            try(
                    // 定义BufferedReader输入流来读取URL的响应
                    BufferedReader in=new BufferedReader(
                            new InputStreamReader(conn.getInputStream() , "utf-8"))) {
                String line;
                while ((line=in.readLine())!=null) {
                    result +="\n" + line;
                }
            }
        }
        catch(Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
        }
        return result;
    }
    /**
    * 向指定URL发送POST方式的请求
    * @param url 发送请求的URL
    * @param param 请求参数,格式应该满足name1=value1&name2=value2的形式
    * @return URL代表远程资源的响应
    */
    public static String sendPost(String url , String param) {
        String result="";
        try {
            URL realUrl=new URL(url);
            // 打开和URL之间的连接
            URLConnection conn=realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            // 发送POST请求必须设置如下两行
             conn.setDoOutput(true);conn.setDoInput(true);
             try(
            // 获取URLConnection对象对应的输出流
             PrintWriter out=new PrintWriter(conn.getOutputStream())){
            // 发送请求参数
             out.print(param);
            // flush输出流的缓冲
            out.flush();
        }
        try(
                // 定义BufferedReader输入流来读取URL的响应
                BufferedReader in=new BufferedReader(new InputStreamReader
                        (conn.getInputStream() , "utf-8"))) {
            String line;
            while ((line=in.readLine())!=null) {
                result +="\n" + line;
            }
        }
    }
        catch(Exception e) {
        System.out.println("发送POST请求出现异常!" + e);
        e.printStackTrace();
    }
        return result;
}
    // 提供主方法,测试发送GET请求和POST请求
    public static void main(String args[]) {
        // 发送GET请求
        String s=GetPostTest.sendGet("https://blog.csdn.net/tonydz0523?ref=toolbar"
                , null);
        System.out.println(s);
        // 发送POST请求
//        String s1=GetPostTest.sendPost("http://localhost:8888/abc/login.jsp"
//                , "name=crazyit.org&pass=leegang");
        System.out.println(s1);
    }
}

上面程序中发送GET请求时只需将请求参数放在URL字符串之后,以?隔开,程序直接调用URLConnection对象的connect()方法即可,如sendGet()方法中粗体字代码所示;如果程序要发送POST请求,则需要先设置doIn和doOut两个请求头字段的值,再使用URLConnection对应的输出流来发送请求参数,如sendPost()方法中粗体字代码所示。
不管是发送GET请求,还是发送POST请求,程序获取URLConnection响应的方式完全一样——如果程序可以确定远程响应是字符流,则可以使用字符流来读取;如果程序无法确定远程响应是字符流,则使用字节流读取即可。

3.Java编程实例

3.1获取自己的计算机名与IP地址

互联网中无数的服务器和计算机也不是处于杂乱无章的无序状态的。网络上的每一个主机都有唯一的地址,作为该主机在互联网上的唯一标志,这个地址叫作IP地址。本实例我们就通过一个简单的程序来帮助读者查看自己计算机的名称和IP地址

3.1.1

新建项目InterNameAndAddr,并在其中创建一个InterNameAndAddr.java文件。在该类的主方法中使用InetAddress类来获取网络地址解析和编码:

package InterNameAndAddr;

import java.io.*;
import java.net.*;
public class InterNameAndAddr{
    public static void main(String[] args){
        try{                       
            // 调用 InetAddress类中的方法会抛出异常,故进行捕获处理
            InetAddress ind = InetAddress.getLocalHost();
            // 通过计算机名获得IP地址,并将其转换成字符串输出
            System.out.println("主机的名称和IP地址为:"+ind.toString());
            String strName=ind.getHostName(); // 通过IP地址获得计算机名称
            System.out.println("主机名称:"+strName);
        } catch(Exception e){
            System.out.println("发生异常!");
        }
    }
}

IP地址在计算机内部的表现形式是一个32位的二进制数,实际表现为一个四点格式的数据,由点号(.)将数据分为4个数字,比如202.113.76.142。每个数字代表一个8位二进制数,总共32位,刚好是一个IP地址的位数。
用四点格式来表示一个IP地址,记忆起来很不方便,而且很容易记错。为了便于记忆, Internet提供了一种域名服务,将IP地址与某个域名对应起来,这种域名就是通常所说的网址。

3.2 根据URL获取因特网网页源文件

根据URL可以获取因特网网页源文件信息。通过URL对象的openStream()方法打开一个输入流从远程资源中读取信息,就像建立了一根水管,远程资源通过这根水管以数据流的形式到达用户端,用户端再从这个数据输入流中逐行读取HTML文档,并打印输出到屏幕上。

3.2.1

新建项目URLGetHtmlDemo,并在其中创建一个URLGetHtmlDemo.java文件。在该类的主方法中通过URL对象的openStream()方法打开一个输入流从远程资源中读取信息,再通过BufferedReader类方法将信息打印到控制台上。核心代码如下所示:


package URLGetHtmlDemo;

import java.net.*;
import java.io.*;
public class URLGetHtmlDemo {
    public static void main(String args[]) {
        URL url = null;
        try {
            url = new URL("https://blog.csdn.net/tonydz0523?ref=toolbar"); // 初始化URL对象
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        try {
            InputStream input = url.openStream();  // 得到数据输入流
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
            String info = reader.readLine();       // 读入数据
            while (info != null) {
                System.out.println(info);       // 输出结果至屏幕
                info = reader.readLine();       // 读取一行
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

当创建一个URL对象后,就可以使用URL类中的openStream()方法读取URL所描述的页面信息。该方法会打开到URL的连接,并返回一个用于从该连接读入的InputStream对象。该方法定义如下:

public final InputStream openStream()

可以通过该对象来读取URL所描述的页面信息。该方法返回一个InputStream对象使用该方法可能会抛出IOException异常。所以在使用时要对该异常进行处理。

猜你喜欢

转载自blog.csdn.net/tonydz0523/article/details/87531139
今日推荐