JavaEE基础第14章网络编程

第14章网络编程

等到JavaWeb阶段全是网络编程的内容

网络编程概述

一、网络编程中有两个主要的问题:

1.如何准确地定位网络上一台或多台主机;定位主机上的特定应用

2.找到主机后如何可靠高效地进行数据传输

二、网络编程中的两个要素:

1.对于问题一:IP 和 端口号

2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)

IP和端口号的理解

三、通信要素一:IP和端口号

1.IP:唯一的标识Internet上的计算机(通信实体)

2.在Java中使用InetAddress类代表IP。一个InetAddress类的对象就表示一个IP地址。

3.IP分类:IPv4 和 IPv6 ; 万维网 和 局域网

4.域名:www.baidu.com www.sina.com

5.本机的回路地址:127.0.0.1 对应着:localhost

6.如何实例化InetAddress:两个方法:getByName(String host)、getLocalHost(). 两个常用方法:getHostName() / getHostAddress()

7.端口号:正在计算机上运行的进程。

​ 要求:不同的进程有不同的端口号

​ 范围:被规定为一个16位的整数 0~ 65535

8.端口号与IP地址的组合得出一个网络套接字:Socket

InetAddress类

InetAddress类实例化,InetAddress类没有对外暴露构造器,将构造器私有化了,需要调用静态方法返回对象。

一个InetAddress类的对象就表示一个IP地址

InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取InetAddress实例

  • public static InetAddress getLocalHost()
  • public static InetAddress getByName(String host)

InetAddress提供了如下几个常用的方法

  • public String getHostAddress():返回 IP 地址字符串(以文本表现形式)。
  • public String getHostName():获取此 IP 地址的主机名
  • public boolean isReachable(int timeout):测试是否可以达到该地址
@Test
public void test1(){
    try {
        InetAddress inetAddress1 = InetAddress.getByName("220.181.38.251");
        System.out.println(inetAddress1);

        InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
        System.out.println(inetAddress2);

        InetAddress inetAddress3 = InetAddress.getByName("127.0.0.1");
        System.out.println(inetAddress3);

        //获取本机的IP地址
        InetAddress inetAddress4 = InetAddress.getLocalHost();
        System.out.println(inetAddress4);

        //获取域名
        System.out.println(inetAddress2.getHostName());
        //获取IP地址
        System.out.println(inetAddress2.getHostAddress());
        
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
}

Internet上的主机有两种方式表示地址:

  • 域名(hostName):www.baidu.com
  • IP 地址(hostAddress):220.181.38.251

**InetAddress类主要表示IP地址,**两个子类:Inet4Address、Inet6Address。
InetAddress 类对象含有一个 Internet 主机地址的域名和IP 地址:www.baidu.com 和220.181.38.251。
域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析

域名解析的过程:

在这里插入图片描述

域名解析时会先去寻找本机的hosts文件查看是否有对应的域名IP地址。若没有再提交给DNS(域名服务器)。可以手动在hosts文件中添加域名和IP地址的映射关系。

hosts文件的作用:

其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。对于要经常访问的网站,我们可以通过在Hosts中配置域名和IP的映射关系,提高域名解析速度。由于有了映射关系,当我们输入域名计算机就能很快解析出IP,而不用请求网络上的DNS服务器。清空hosts文件对系统正常运行并没有影响。

在命令提示符下输入ping + 域名可以得到对应的IP地址

在这里插入图片描述

端口号补充

端口号标识正在计算机上运行的进程(程序)

  • 不同的进程有不同的端口号
  • 被规定为一个 16 位的整数 0~65535。

端口分类:

  • 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)
  • 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
  • 动态/私有端口:49152~65535。

端口号与IP地址的组合得出一个网络套接字:Socket

在这里插入图片描述

网络通信协议

TCP协议:

  • 使用TCP协议前,须先建立TCP连接,形成传输数据通道
  • 传输前,采用“三次握手”方式,点对点通信,是可靠的
  • TCP协议进行通信的两个应用进程:客户端、服务端。
  • 在连接中可进行大数据量的传输
  • 传输完毕,需释放已建立的连接,效率低

UDP协议:

  • 将数据、源、目的封装成数据包,不需要建立连接
  • 每个数据报的大小限制在64K内
  • 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
  • 可以广播发送
  • 发送数据结束时无需释放资源,开销小,速度快

在这里插入图片描述

在这里插入图片描述

TCP网络编程例子

TCP网络编程创建Socket对象,指明服务器端的ip和端口号,因为TCP需要先建立连接,所以在创建对象时就需要指明ip和端口号。

先启动服务器端,再启动客户端。

例一、客户端发送内容给服务端,服务端将内容打印到控制台上。

客户端:

1.创建Socket对象,指明服务器端的ip和端口号

2.获取一个输出流,用于输出数据

3.写出数据的操作

4.资源的关闭

服务器端:

1.创建服务器端的ServerSocket,指明自己的端口号

2.调用accept()表示接收来自于客服端的socket

3.获取输入流

4.获取输入流中的数据

5.资源的关闭

注意:套接字socket也需要关闭;熟悉使用ByteArrayOutputStream,理解其底层原理。理解出现乱码的原因。

    /*
    网络编程例子1
     */
    //客户端
    @Test
    public void client(){
        Socket socket = null;
        OutputStream outputStream = null;
        try {
            //1.创建Socket对象,指明服务器端的ip和端口号
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            socket = new Socket(inetAddress,8899);
            //2.获取一个输出流,用于输出数据
            outputStream = socket.getOutputStream();
            //3.写出数据的操作
            outputStream.write("你好我是客户端".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源的关闭
            try {
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();//socket套接字也需要关闭
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //服务器端
    @Test
    public void server(){
        ServerSocket serverSocket = null;
        Socket socket = null;//接收
        InputStream inputStream = null;
        ByteArrayOutputStream bas = null;
        try {
            //1.创建服务器端的ServerSocket,指明自己的端口号
            serverSocket = new ServerSocket(8899);
            //2.调用accept()表示接收来自于客服端的socket
            socket = serverSocket.accept();
            //3.获取输入流
            inputStream = socket.getInputStream();
        /*
        不建议这样写,因为这是字节流,处理中文时可能会乱码。
        出现乱码的原因,在UTF-8中中文占3个字节,如果在装进byte[]数组时,正好将一个中文3个字节给分开了,
        那么这个装满的数组在转成字符串(解码)时就会出现乱码,接着后面的都会出现错乱。
        byte[] buffer = new byte[20];
        int len;
        while ((len = inputStream.read(buffer)) != -1){
            String str = new String(buffer,0,len);
            System.out.println(str);
        }
         */
            //采用ByteArrayOutputStream
            byte[] buffer = new byte[20];
            int len;
            //4.获取输入流中的数据
            bas = new ByteArrayOutputStream();
            while ((len = inputStream.read(buffer)) != -1){
                /*
                ByteArrayOutputStream内部是一个可变长的char[]数组,调用write时可以先将数据写入数组,
                这个时候不会解码,等全部装完转成字符串统一进行解码,这样就不会出现乱码了。
                 */
                bas.write(buffer,0,len);
            }
            System.out.println(bas.toString());//转换要放在循环外
            System.out.println("收到了来自:" + socket.getInetAddress().getHostAddress() + "的数据");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5.资源的关闭
            try {
                if (bas != null)
                    bas.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (inputStream != null)
                    inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (serverSocket != null)
                    serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

例二、客户端发送文件给服务端,服务端将文件保存在本地。

    /*
    例二
     */
    @Test
    public void client1() {
        Socket socket = null;
        OutputStream os = null;
        FileInputStream fis = null;
        try {
            //1.创建Socket对象,指明服务器端的ip和端口号
            socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);
            //2.获取一个输出流,用于输出数据
            os = socket.getOutputStream();
            //3.获取一个输入流,实现从哪个文件读数据
            fis = new FileInputStream("图片1.jpg");
            //4.具体实现步骤
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5.资源关闭
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null)
                    os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void server1(){
        ServerSocket ss = null;
        Socket socket = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            //1.创建服务器端的ServerSocket,指明自己的端口号
            ss = new ServerSocket(9090);
            //2.调用accept()表示接收来自于客服端的socket
            socket = ss.accept();
            //3.获取输入流
            is = socket.getInputStream();
            //4.将数据写入本地
            fos = new FileOutputStream("图片4.jpg");
            //5.实现步骤
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //6.关闭资源
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (ss != null)
                    ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

例三:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。并关闭相应的连接。

学习服务器端是怎么给客户端反馈的。客户端在数据传输完毕后需要调用shutdownOutput()方法告诉服务器端数据传输完毕。

    /*
    例三
     */
    @Test
    public void client2() {
        Socket socket = null;
        OutputStream os = null;
        FileInputStream fis = null;
        InputStream is = null;
        ByteArrayOutputStream bas = null;
        try {
            //1.创建Socket对象,指明服务器端的ip和端口号
            socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);
            //2.获取一个输出流,用于输出数据
            os = socket.getOutputStream();
            //3.获取一个输入流,实现从哪个文件读数据
            fis = new FileInputStream("图片1.jpg");
            //4.具体实现步骤
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            //关闭数据的输出
            socket.shutdownOutput();

            //5.接收来自服务器端的反馈。必须在关闭连接之前接收
            is = socket.getInputStream();
            bas = new ByteArrayOutputStream();
            byte[] buffer1 = new byte[1024];
            int len1;
            while ((len1 = is.read(buffer1)) != -1){
                bas.write(buffer1,0,len1);
            }
            System.out.println(bas.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //6.资源关闭
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null)
                    os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bas != null)
                    bas.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void server2(){
        ServerSocket ss = null;
        Socket socket = null;
        InputStream is = null;
        FileOutputStream fos = null;
        OutputStream os = null;
        try {
            //1.创建服务器端的ServerSocket,指明自己的端口号
            ss = new ServerSocket(9090);
            //2.调用accept()表示接收来自于客服端的socket
            socket = ss.accept();
            //3.获取输入流
            is = socket.getInputStream();
            //4.将数据写入本地
            fos = new FileOutputStream("图片4.jpg");
            //5.实现步骤
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
            /*
            解释:read()是一个阻塞式的方法,就是如果没有明确告诉传输完毕那么就不会退出循环。
            比如读取一个文件时,读到文件末尾返回值为-1,说明传输完毕,但是在网络编程中,客户端什么时候才算
            传完呢。如果客户端关闭了,说明客户端传输完毕,但是现在客户端还在等待服务器端的反馈,因此客户端还没有
            进行关闭,所以必须想一个办法告诉服务器端客户端已经传输完成,这样才能退出循环,否则一直在循环中,
            程序无法继续执行。
            仅需在客户端调用shutdownOutput()方法告诉服务器端数据传输完毕。
             */

            //6.服务器端给予客户端反馈
            //这时候这个socket已经是接收到的从客户端发送过来的socket了,就是要给这个客户端反馈的
            os = socket.getOutputStream();
            os.write("照片已经收到!".getBytes());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //7.关闭资源
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (ss != null)
                    ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null)
                    os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

IO流中read()是一个阻塞式的方法,就是如果没有明确告诉传输完毕那么就不会退出循环。比如读取一个文件时,读到文件末尾返回值为-1,说明传输完毕,但是在网络编程中,客户端什么时候才算传完呢。如果客户端关闭了,说明客户端传输完毕,但是现在客户端还在等待服务器端的反馈,因此客户端还没有进行关闭,所以必须想一个办法告诉服务器端客户端已经传输完成,这样才能退出循环,否则一直在循环中,程序无法继续执行。仅需在客户端调用shutdownOutput()方法告诉服务器端数据传输完毕。

而在 NIO 中就是非阻塞式了,功能更强大,等学习到框架时会细说。

配置Tomcat

Tomcat配置,使用等到JavaWeb细讲。

UDP网络编程例子

UDP需要创建DatagramSocket对象,此时不需要指明ip和端口号,因为UDP是不可靠连接,ip和端口号放在数据报中。

    //UDP网络编程例子
    @Test
    public void sender(){
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket();

            String str = "我是UDP";
            byte[] buffer = str.getBytes();
            InetAddress localAddress = InetAddress.getLocalHost();
            DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length,localAddress,9090);

            socket.send(packet);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null)
                socket.close();
        }

    }


    @Test
    public void receiver(){
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(9090);

            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

            socket.receive(packet);

            System.out.println(new String(packet.getData(),0,packet.getLength()));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null)
                socket.close();
        }
    }

URL网络编程

1.URL:统一资源定位符,对应着互联网的某一资源地址

2.格式:

http://localhost:8080/examples/beauty.jpg?username=Tom

协议 主机名 端口号 资源地址 参数列表

    @Test
    public void test2() throws MalformedURLException {
        URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");
        System.out.println(url.getProtocol());//获取该URL的协议名
        System.out.println(url.getHost());//获取该URL的主机名
        System.out.println(url.getPort());//获取该URL的端口号
        System.out.println(url.getPath());//获取该URL的文件路径
        System.out.println(url.getFile());//获取该URL的文件名
        System.out.println(url.getQuery());//获取该URL的查询名
    }

URL网络编程实现从Tomcat服务器端下载数据

这里只是简单用一下,实际上并不会这么操作。在JavaWeb中会讲从浏览器端下载数据。

把Tomcat服务器启动起来,相应的文件资源放在对应的目录下,运行程序即可完成下载。(实际开发中不会用这样的)

    @Test
    public void test3() {
        HttpURLConnection urlConnection = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.connect();//获取与服务器端的连接
            is = urlConnection.getInputStream();
            fos = new FileOutputStream("beauty.jpg");
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (urlConnection != null)
                urlConnection.disconnect();
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值