Java网络编程

网络编程概述

网络的发展,缩小了人与人之间的时空距离。比如:小李在广东,小张在美国,现在通过即时通信软件就可以聊天,就像是生活在隔壁一样。交流从未如此便捷!

寄信:

网络信件.png

计算机网络:

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络编程的目的:

实现数据交换和数据通信

想要达到这个效果需要什么?

  1. 如何准确定义到网络上的一台主机?

    • 通过IP地址+端口号,就可以确定一台主机,比如:192.168.1.1:80
    • 端口是用来区分计算机上的不同程序。
  2. 找到了这个主机,如何传输数据?

    • B/S架构,浏览器和服务器,比如:javaweb:网页编程
    • C/S架构,客户端和服务器,比如:网络编程,使用TCP/IP协议簇

网络通信的要素

如何实现网络通信?

  1. 需要通信双方的地址信息:
    • ip地址
    • 端口号

比如:192.168.1.5:80

  1. 通信规则:使用和遵循“网络通信协议

TCP/IP参考模型

网络参考模型.png

我们学习Java重点研究传输层:

​ 更具体来说是研究TCP、UDP!

网络参考模型2.png

小结:

  1. 网络编程中有两个主要的问题
    • 如何准确的定位到网络上的一台主机
      • IP地址 + 端口号
    • 找到主机之后如何进行通信
      • 通过相应的网络通信协议,建立通信
  2. 网络编程中的要素
    • IP地址 和 端口号
    • 网络通信协议
  3. 在Java中,万物皆对象,使用Java提供的对象,实现网络通信功能!

IP

java.net.InetAddress类

InetAddress类是用来表示互联网协议IP地址

ip地址:

  • 唯一定位到一台网络上的计算机
  • 127.0.0.1 是本机的局域网地址,也可以用localhost代替
  • ip地址的分类
    • ipv4 / ipv6
      • ipv4:由4个字节组成,共32位,范围是0~255,一共有42亿个IPv4地址,现已枯竭。如:192.168.1.6
      • ipv6:由8个无符号整数组成,共128位,一共有2128个IPv6地址。如:fe80::2999:2945:31c:838d%7
    • 公网(互联网)- 私网(局域网)
      • 公网分为ABCD类地址
      • 像192.168.xxx.xxx 是局域网,专门给家庭或组织机构内部使用。
  • 域名:是为了解决IP地址难记的问题,域名可以映射到IP地址,只需记住简单的域名就可以找到对应的IP地址了。

InetAddress类的常用方法:

getByName()、getLocalHost()、getAddress()、getCanonicalHostName()、getHostAddress()、getHostName()

package com.net.demo01;

import java.net.InetAddress;
import java.net.UnknownHostException;

/*
    使用InetAddress类的常用方法
 */
public class Demo01 {
    public static void main(String[] args) {
        try {
            //查询本机ip地址信息
            //创建InetAddress对象
            InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
            System.out.println("inetAddress1:" + inetAddress1); // /127.0.0.1
            InetAddress inetAddress2 = InetAddress.getByName("localhost");
            System.out.println("inetAddress2:" + inetAddress2); // localhost/127.0.0.1
            InetAddress inetAddress3 = InetAddress.getLocalHost();
            System.out.println("inetAddress3:" + inetAddress3); // simingdeMacBook-Pro.local/127.0.0.1

            //通过域名查询网站的ip地址
            InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com"); //不需要加端口号
            System.out.println("ipAddress:" + inetAddress4); // www.baidu.com/14.215.177.38

            //其他常用方法
            System.out.println("getAddress:" + inetAddress4.getAddress()); // [B@610455d6
            System.out.println("getCanonicalHostName:" + inetAddress4.getCanonicalHostName()); // 14.215.177.38,网站的规范名字
            System.out.println("getHostAddress:" + inetAddress4.getHostAddress()); // 14.215.177.38,ip地址
            System.out.println("getHostName:" + inetAddress4.getHostName()); // www.baidu.com,域名,或者自己电脑名字

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

端口

端口表示计算机上一个程序的进程:

  • 不同进程有不同的端口号!用来区分不同软件。

  • 端口号的范围:0~65535

  • TCP+UDP总端口数:65535*2,相同端口号在不同协议下不冲突,比如:TCP:80,UDP:80,这是可以的。

  • 端口分类

    • 公共端口 0~1023

      • HTTP:80
      • HTTPS:443
      • FTP:21
      • SSH:22
      • Telnet:23
    • 程序注册端口:1024~49151,有48128个端口,分配给用户或程序

      • Tomcat:8080
      • MySQL:3306
      • Redis:6379
    • 动态、私有:49152~65535,有16383个端口

      #CMD命令:
      netstat -ano //查看所有端口
      netstat -ano|findstr "9240" //查看指定的端口
      tasklist|findstr "9240" //查看指定端口的进程
      #使用任务管理器查看
      

      netstat常用命令

      #Linux 查看端口信息
      #netstat命令用来显示网络状态
      netstat -a  --列出所有端口
      netstat -at  --列出所有tcp端口
      netstat -t  --显示tcp连接的信息
      netstat -u  --显示udp连接的信息
      netstat -atn | grep ESTA  --列出ESTABLISHED状态的tcp端口
      netstat -an | grep 3307  --列出端口的3307的网络连接情况
      netstat -n   --看到各个连接的真实IP地址。
      netstat -l     --显示处于监听状态的连接信息。
      netstat -lt    --查看关于tcp连接的端口信息。
      netstat -s   --根据协议统计所有连接信息。
      netstat -r   --显示系统的核心路由信息。
      netstat -i    --显示系统的网络接口列表。
      netstat -ie   --显示系统各个网络接口的地址信息(相当于执行命令ifconfig)
      

      InetSocketAddress类

      InetSocketAddress类常用方法:

      getAddress()、getHostName()、getPort()

      package com.net.demo01;
      
      import java.net.InetSocketAddress;
      
      /*
          Socket,使用ip地址+端口
       */
      public class Demo02 {
          public static void main(String[] args) {
      
              //创建InetSocketAddress对象
              InetSocketAddress socketAddress1 = new InetSocketAddress("127.0.0.1", 8080);
              InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080);
              System.out.println(socketAddress1); // /127.0.0.1:8080
              System.out.println(socketAddress2); // localhost/127.0.0.1:8080
      
              System.out.println(socketAddress1.getAddress()); //地址 /127.0.0.1
              System.out.println(socketAddress1.getHostName()); //域名 localhost
              System.out.println(socketAddress1.getPort()); //端口 8080
          }
      }
      

通信端口要相同.png

通信协议【重要】

协议:就是约定,比如现在我们日常交流都是用普通话。

网络通信协议:控制管理,速率、传输码率、代码结构、传输控制......

网络通信协议非常复杂,我们把它们进行分层:

TCP/IP协议簇,由一堆协议组成

  • TCP:传输控制协议
  • UDP:用户数据报协议
  • IP:网际协议

网络参考模型1.png

TCP 和 UDP 对比

TCP:类似打电话

  • 连接,稳定

  • 三次握手,四次挥手

    //最少需要三次,才能保证稳定连接!
    A:你愁啥?
    B:瞅你咋地?
    A:不服干一场!
        
    //四次挥手
    A:我要走了!
    B:你真的要走了吗?
    B:你真的真的要走了吗?
    A:我真的走了!
    

说明:

  • TCP有客户端,服务端

  • 传输完成后释放连接,效率低

UDP:类似发短信

  • 不连接,不稳定
  • 客户端、服务端没有明确界限
  • 不管有没有准备好,都可以发给你...
    • 导弹攻击
    • DDoS:洪水攻击!饱和式打击

TCP

客户端程序

  1. 连接服务器,通过Socket
  2. 发送信息
package com.net.demo02;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

/*
    客户端程序
 */
public class TcpClientDemo01 {
    public static void main(String[] args) {
        Socket socket = null;
        OutputStream os = null;
        //1.需要知道服务器的ip地址,端口号
        try {
            InetAddress serverIP = InetAddress.getByName("127.0.0.1");
            int serverPort = 9999;
            //2.创建一个socket连接
            socket = new Socket(serverIP, serverPort);
            //3.发送信息,使用io流
            os = socket.getOutputStream();
            os.write("蚌埠住了".getBytes());
            
            //刷新管道
            os.flush();

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

服务端程序

  1. 设置服务器的端口 ServerSocket
  2. 等待用户的连接 accept
  3. 接收用户的信息
package com.net.demo02;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
    服务端程序
 */
public class TcpServerDemo01 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream bos = null;

        try {
            //1.创建一个ServerSocket对象,指定端口号
            serverSocket = new ServerSocket(9999);
            while (true) {
                //2.等待客户端连接
                socket = serverSocket.accept();
                //3.读取客户端的消息
                is = socket.getInputStream();

                //管道流
                bos = new ByteArrayOutputStream();
                byte[] bytes = new byte[1024]; //1KB
                int readCount = 0;
                while ((readCount = is.read(bytes)) != -1) {
                    bos.write(bytes, 0 ,readCount);
                }

                System.out.println(bos.toString());

                //刷新管道
                bos.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

文件上传

服务器端程序:

package com.net.demo02;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/*
    服务端程序-文件上传
 */
public class TcpServerDemo02 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null; //字节输入流对象
        FileOutputStream fos = null; //文件字节输出流对象
        try {
            //1.创建服务器端口
            serverSocket = new ServerSocket(9000);
            //2.监听客户端的连接
            socket = serverSocket.accept(); //阻塞式监听,会一直等待客户端连接
            //3.获取输入流
            is = socket.getInputStream();
            //4.文件输出
            fos = new FileOutputStream(new File("基础语法\\src\\com\\net\\demo02\\receive.jpg"));
          	//5.核心代码:一边读一边写
            byte[] bytes = new byte[1024];
            int readCount = 0;
            while ((readCount = is.read(bytes)) != -1) {
                fos.write(bytes, 0, readCount);
            }

            //通知客户端我接收完毕了
            OutputStream os = socket.getOutputStream();
            os.write("服务器已接收完毕,客户端可以断开了".getBytes());

            //刷新管道
            fos.flush();
          
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端程序:

package com.net.demo02;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

/*
    客户端程序-文件上传
 */
public class TcpClientDemo02 {
    public static void main(String[] args) {
        Socket socket = null;
        OutputStream os = null; //字节输出流对象
        FileInputStream fis = null; //文件字节输入流对象
        try {
            //1.创建一个Socket连接
            socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
            //2.创建一个输出流
            os = socket.getOutputStream();
            //3.读取文件
            fis = new FileInputStream(new File("基础语法\\src\\com\\net\\ia_3800000581.jpg"));
            //4.写出文件
            byte[] bytes = new byte[1024];
            int readCount = 0;
            while ((readCount = fis.read(bytes)) != -1) {
                os.write(bytes, 0, readCount);
            }

            //通知服务器,我已经写完了
            socket.shutdownOutput(); //我已经传输完了!

            //确定服务器接收完毕,才能断开连接
            InputStream is = socket.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            byte[] bytes2 = new byte[1024];
            int readCount2 = 0;
            while ((readCount2 = is.read(bytes2)) != -1) {
                baos.write(bytes2, 0, readCount2);
            }
            System.out.println(baos.toString());

            //刷新管道
            os.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Tomcat

客户端

  • 浏览器 B
  • 自定义 C

服务器

  • Tomcat服务器 S Java后台开发!
  • 自定义 S

UDP

发短信:不用连接,但需要知道对方的地址。

java.net.DatagramPacket类

使用DatagramPacket类

发送消息

发送端程序:

package com.net.demo03;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
    UDP-发送端
    不需要连接接收端(无连接)
 */
public class UdpClientDemo01 {
    public static void main(String[] args) throws Exception {
        //1.创建一个Socket
        DatagramSocket socket = new DatagramSocket();
        //2.建一个包DatagramPacket
      	//准备消息数据
        String msg = "你好,服务器!";

        //发送给谁
        InetAddress localhost = InetAddress.getByName("localhost");
        int serverPort = 9090;

        //格式:消息,数据长度的起始,要发送给谁
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, serverPort);

        //3.发送包
        socket.send(packet);

        //4.关闭流
        socket.close();
    }
}

接收端程序:

package com.net.demo03;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
    UDP-接收端
    不需要连接发送端(无连接)
 */
public class UdpServerDemo01 {
    public static void main(String[] args) throws Exception {
        //创建DatagramSocket对象
        DatagramSocket socket = new DatagramSocket(9090); //指定9090为接受端的端口
        //接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);

        socket.receive(packet); //阻塞式接收
      	//从接收到的包中,获取数据
        System.out.println(packet.getAddress().getHostAddress()); //获取IP地址 127.0.0.1
        System.out.println(new String(packet.getData(), 0, packet.getLength())); //输出接收的消息内容 你好,服务器!

        //关闭连接
        socket.close();

    }
}

循环发送消息

发送端程序:

package com.net.demo04;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

/*
    发送端程序
 */
public class UdpSenderDemo01 {
    public static void main(String[] args) throws  Exception {
        //创建Socket
        DatagramSocket socket = new DatagramSocket(8888); //指定8888为发送端的端口

        //准备数据:从控制台读取输入 System.in
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        while (true) {
            String data = reader.readLine();
            byte[] datas = data.getBytes();
            DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666)); //指定接收端的ip地址和端口
            //发送信息
            socket.send(packet);
            if (data.equals("bye")) {
                break;
            }
        }

      	//关闭资源
        reader.close();
        socket.close();
    }
}

接收端程序:

package com.net.demo04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
    接收端程序
 */
public class UdpReceiveDemo01 {
    public static void main(String[] args) throws Exception {
        //创建Socket
        DatagramSocket socket = new DatagramSocket(6666);

        while (true) {
            //接收数据包
            byte[] bytes = new byte[1024];
            DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
            socket.receive(packet); //阻塞式接收数据包

            //断开连接
            byte[] data = packet.getData();
            String receiveData = new String(data, 0, data.length);
            System.out.println(receiveData);
            if (receiveData.equals("bye")) {
                break;
            }
        }

      	//关闭资源
        socket.close();
    }
}

多线程发送信息

线程类要实现Runnable接口

开启发送端线程:

package com.net.demo04;

public class TalkStudent {
    public static void main(String[] args) {
        //开两个线程
        new Thread(new TalkSend(7777, "localhost", 9999)).start();
        new Thread(new TalkReceive(8888, "老师")).start();
    }
}

开启接收端线程:

package com.net.demo04;

public class TalkTeacher {
    public static void main(String[] args) {
        //开启两个线程
        new Thread(new TalkSend(5555, "localhost", 8888)).start();
        new Thread(new TalkReceive(9999, "学生")).start();
    }
}

发送端线程:

package com.net.demo04;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

/*
    发送端线程
 */
public class TalkSend implements Runnable {
    DatagramSocket socket = null;
    BufferedReader reader = null;

    private int formPort;
    private String toIP;
    private int toPort;

    public TalkSend(int formPort, String toIP, int toPort) {
        this.formPort = formPort;
        this.toIP = toIP;
        this.toPort = toPort;

        try {
            //创建Socket
            socket = new DatagramSocket(formPort);
            //准备数据:从控制台读取 System.in
            reader = new BufferedReader(new InputStreamReader(System.in));
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        while (true) {
            String data = null;
            try {
                data = reader.readLine();
                byte[] datas = data.getBytes();
                DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIP, this.toPort));
                //发送信息
                socket.send(packet);
                if (data.equals("bye")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        socket.close();
    }
}

接收端线程:

package com.net.demo04;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/*
    接收端线程
 */
public class TalkReceive implements Runnable {

    DatagramSocket socket = null;

    private int port;
    private String msgForm;

    public TalkReceive(int port, String msgForm) {
        this.port = port;
        this.msgForm = msgForm;

        //创建Socket
        try {
            socket = new DatagramSocket(port);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        while (true) {
            try {
                //接收数据包
                byte[] bytes = new byte[1024];
                DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
                socket.receive(packet); //阻塞式接收数据包

                //断开连接
                byte[] data = packet.getData();
                String receiveData = new String(data, 0, data.length);
                System.out.println(msgForm + ":" + receiveData);
                if (receiveData.equals("bye")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        socket.close();
    }
}

URL

URL就是统一资源定位符,用来定位资源位置,可以准确找到互联网上的一个资源。

DNS域名解析服务:把域名解析为IP地址。

​ 如:www.baidu.com -> 14.215.177.38

URL格式:

协议://ip地址:端口号/项目名/资源

从网站下载资源

通过URL在网站下载文件:

前提是需要获得资源的URL地址

  • 使用Chrome的检查功能获取URL
package com.net.demo05;

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

/*
    使用URL从网站下载资源
    http://localhost:8080/siming/ia_3800000574.jpg
 */
public class URLDownload {
    public static void main(String[] args) throws Exception {
        //1.创建URL对象,放入下载地址
//        URL url = new URL("http://localhost:8080/siming/ia_3800000574.jpg");
        URL url = new URL("https://dl.stream.qqmusic.qq.com/C400001zLvbN1NYMuv.m4a?guid=1841897314&vkey=76015862ECD29189DDD7E35E449CEF1EB146E49924A0BE0AF859111C561CC9FB934EDF63EB3B199EE5CB3286BC737536312A82CC59E65CDA&uin=1040330915&fromtag=66");

        //2.连接到这个资源,要使用HTTP协议
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        //3.下载
        InputStream is = urlConnection.getInputStream();

//        FileOutputStream fos = new FileOutputStream("ia_3800000574.jpg");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Siming\\Downloads\\Music\\桥边姑娘.m4a");

        //一边读,一边写
        byte[] bytes = new byte[1024];
        int readCount = 0;
        while ((readCount = is.read(bytes)) != -1) {
            fos.write(bytes, 0, readCount);
        }

        //刷新管道
        fos.flush();

        ////关闭资源
        fos.close();
        is.close();
        urlConnection.disconnect(); //断开URL连接
    }

}
posted @ 2021-08-13 23:06  siming笨笨  阅读(108)  评论(0)    收藏  举报