Java学习之初识网络编程

网络编程

1. 概述

1.1 计算机网络

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

1.2 网络编程

目的:

类似于之前的无限电台,可以进行信息的传播交流、数据交换、通信

想实现这个效果需要做什么?

1.如何精确的定位网络上的一台主机?
(外网即公网)IP地址:端口号,定位到计算机上的某个资源(应用,如进行qq通信)
192.168.16.124:端口
2.找到主机之后如何传输数据?
Javaweb开发:网页编程 B/S架构,通过浏览器去访问
网络编程:TCP/IP C/S架构,客户端/服务器

1.3 网络通信的要素

如何实现网络的通信?
通信双方的地址:

  • IP(公网)
  • 端口号
    规则:网络通信的协议
    在这里插入图片描述
    在这里插入图片描述

小结

  • 网络编程中有两个主要的问题
    如何准确定位到网络上的一台或者多台主机
    找到主机之后如何进行通讯
  • 网络编程中的要素
    IP 和端口号
    网络通讯协议 udp,tcp
  • 万物皆对象(Java)

1.4 IP

IP地址:InetAddress类

  • 唯一定位一台网络上的计算机
  • 127.0.0.1:本机localhost
  • IP地址的分类
    • IPV4/ IPV6协议
      IPv4: 127.0.0.1, 4个字节组成,0~255, 42亿~ ; 30亿在北美,亚洲4亿。2011年用尽
      IPv6: 128位。 8个无符号整数
      2001:3CA1:010F:001A:121B:0000:0000:0010 
      
    • 公网(互联网)- 私网(局域网)
      • 192.168.xx.xx 一般来说是局域网,给组织内部使用
      • ABCD类地址(以下地址为IPv4地址)
              最初设计互联网络时,为了便于寻址以及层次化构造网络,每个IP地址包括两个标识码(ID),即网络ID和主机ID。同一个物理网络上的所有主机都使用同一个网络ID,网络上的一个主机(包括网络上工作站,服务器和路由器等)有一个主机ID与其对应,即网络地址表示其属于互联网的哪一个网络,主机地址表示其属于该网络中的哪一台主机。二者是主从关系。IP地址根据网络ID的不同分为5种类型,A类地址、B类地址、C类地址、D类地址和E类地址用来表示不同规格的网络。
        网络号:用于识别主机所在的网络;
        主机号:用于识别该网络中的主机。
              IP地址分为五类,A类保留给政府机构,B类分配给大中型企业,C类分配给任何需要的人,D类用于组播,E类用于实验,各类可容纳的地址数目不同。全0和全1的都保留不用。
        在这里插入图片描述
        • A类IP地址
          A类IP地址 地址范围1.0.0.0-126.255.255.255(二进制表示为:00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110),最后一个是广播地址,私有号段:10.0.0.0-10.255.255.255,(默认子网掩码:255.0.0.0或 0xFF000000)。(所谓的私有地址就是在互联网上不使用,而被用在局域网络中的地址)
          最大网络数:2^{7}-2 126个
          最大主机数:2^{24}-2
          全0全1的地址不可分配,作为保留地址。 上面减2也是这个原因。
          第一个字节为网络号,后三个字节为主机号。该类IP地址的最前面为“0”,所以地址的网络号取值于1~126之间。一般用于大型网络。
        • B类IP地址
          B类IP地址地址范围128.0.0.1-191.254.255.255(二进制表示为:10000000 00000001 00000000 00000001 - 10111111 11111110 11111111 11111110),最后一个是广播地址,私有号段:172.16.0.0-172.31.255.255,(默认子网掩码:255.255.0.0或0xFFFF0000)。
          最大网络数:2^{14}-1
          最大主机数:2^{16}-2
          前两个字节为网络号,后两个字节为主机号。该类IP地址的最前面为“10”,所以地址的网络号取值于128~191之间。一般用于中等规模网络。
        • C类IP地址
          C类IP地址范围192.0.0.0-223.255.254.255(二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110),私有号段:192.168.0.0-192.168.255.255,(子网掩码:255.255.255.0或 0xFFFFFF00)。
          最大网络数:2^{21}-1
          最大主机数:2^{8}-2
          前三个字节为网络号,最后一个字节为主机号。该类IP地址的最前面为“110”,所以地址的网络号取值于192~223之间。一般用于小型网络。
        • D类IP地址
          D类IP地址范围224.0.0.1-239.255.255.254 。D类是多播地址。该类IP地址的最前面为“1110”,所以地址的网络号取值于224~239之间。一般用于多路广播用户 。
          多点广播地址用来一次寻址一组计算机,它标识共享同一协议的一组计算机。
        • E类IP地址
          E类IP地址范围240.0.0.0—255.255.255.254。E类是保留地址。该类IP地址的最前面为“1111”,所以地址的网络号取值于240~255之间。
        • 回送地址:127.0.0.1。 也是本机地址,等效于localhost或本机IP。 一般用于测试使用。例如:ping 127.0.0.1来测试本机TCP/IP是否正常。
        • 特殊的IP地址:
          • 1.每一个字节都为0的地址(“0.0.0.0”)对应于当前主机;
          • 2.IP地址中的每一个字节都为1的IP地址(“255.255.255.255”)是当前子网的广播地址;
          • 3.IP地址中凡是以“11110”开头的E类IP地址都保留用于将来和实验使用。
          • 4.IP地址中不能以十进制“127”作为开头,该类地址中数字127.0.0.1到127.255.255.255用于回路测试,
          • 5.网络ID的第一个8位组也不能全置为“0”,全“0”表示本地网络。
  • 域名:记忆IP问题
    -IP:www.vip.com
public class InetAddressTest {
    public static void main(String[] args) {
        //InetAddress类无public构造器,不能直接调用构造器实例化对象
        /**
         * Constructor for the Socket.accept() method.
         * This creates an empty InetAddress, which is filled in by
         * the accept() method.  This InetAddress, however, is not
         * put in the address cache, since it is not created by name.
        InetAddress() {
            holder = new InetAddress.InetAddressHolder();
        }
         */
        try {
            //查询本机地址
            InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
            System.out.println(inetAddress1);
            InetAddress inetAddress3 = InetAddress.getByName("localhost");
            System.out.println(inetAddress3);
            InetAddress inetAddress4 = InetAddress.getLocalHost();
            System.out.println(inetAddress4);
            //获取网站IP地址
            InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
            System.out.println(inetAddress2);
            //常用方法
            System.out.println(inetAddress2.getAddress());//B@1b6d3586字节
            System.out.println(inetAddress2.getCanonicalHostName());//规范的名字
            System.out.println(inetAddress2.getHostAddress());//域名,或自己电脑的名字
            System.out.println(inetAddress2.getHostName());
            
        } catch (UnknownHostException e) {//未知的主机异常
            e.printStackTrace();
        }
    }
}

1.5 端口

端口表示计算机上的一个程序的进程(任务管理器–> PID);

  • 不同的进程有不同的端口号,用来区分软件;(端口号不可冲突)
  • 被规定范围0~65535
  • 又分为TCP端口和UDP端口(使用的协议),所以实际上65535*2;TCP:80,UDP:80不冲突,但是单一协议下端口号不可冲突;
  • 端口分类
    • 公有端口0~1023,这个范围内的端口尽量不要去占用,会被内置的一些进程使用,或者服务器使用;
      • HTTP:80
      • HTTPS:443
      • FTP:21
      • Telent:23
    • 程序注册端口:1024~49151,分配用户或者程序
      • Tomcat:8080
      • MySQL:3306
      • Oracle:1521
    • 动态端口/私有端口:不建议使用49152~65535
 netstat -ano #查看所有的端口
 netstat -ano|findstr “5900” #查看指定的端口,| 管道过滤
 tasklist|findstr “8696” #查看端口属于哪一个进程
//套接字
public class InetSocketAddressTest {
    //InetSockeInetAddress有构造器
    public static void main(String[] args) {
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",8080);
        System.out.println(inetSocketAddress);
        System.out.println(inetSocketAddress.getAddress());
        System.out.println(inetSocketAddress.getHostName());//地址,对应于windows中的hosts文件,C盘-->Windows-->System32-->drivers-->etc
        System.out.println(inetSocketAddress.getHostString());
        System.out.println(inetSocketAddress.getPort());//端口
    }
}
  • 端口映射(两台主机程序之间相互通信)

1.6 通信协议

协议:约定,类似于用普通话进行交流;
网络通信协议:速率、传输码率、代码结构、传输控制…
问题:非常复杂
大事化小:分层!

1.61 TCP/IP协议簇:实际上是一组协议

其中比较重要的协议有:

  • TCP:用户传输协议(传输层)
  • UDP:用户数据报协议(传输层)
    出名的协议:
  • TCP:用户传输协议
  • IP:网络互连协议
1.62 TCP和UDP的对比
  • TCP:类似于打电话
    • 连接,稳定
    • 三次握手、四次挥手
最少需要三次,保证稳定连接
A:你瞅啥
B:瞅你咋地
A:干一场

A:我要走了
B:你真的要走了吗
B:你真的真的要走了吗
A:我真的要走了

在这里插入图片描述
在这里插入图片描述
- 客户端、服务端
- 传输完成,释放连接,效率低

  • UDP:类似于发短信
    • 不连接,不稳定
    • 客户端、服务端:没有明确的界限
    • 不管有没有准备好,都可以发给你
    • DDOS:洪水攻击(饱和攻击)

2. TCP

2.1 客户端

  • 1.连接服务器
  • 2.发送消息
//客户端
public class TCPClientDemo01 {
    public static void main(String[] args) {
        Socket socket = null;
        OutputStream os = null;
        try {
            //1.要知道服务器的地址
            InetAddress serverIP = InetAddress.getByName("127.0.0.1");
            //2.端口号
            int port = 9999;
            //3.创建一个socket连接
            socket = new Socket(serverIP, port);
            //4.发送消息IO流
            os = socket.getOutputStream();
            os.write("你好,欢迎学习!".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭连接以及IO流
            if (os != null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //谁先连接的谁先断开
            if (socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.2 服务器

  • 建立服务端口
  • 等待用户的连接accept
  • 接受用到的消息
//服务器
//服务器
public class TCPServerDemo01 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            //1.我得有一个地址,Java网络编程又称作套接字编程,socket有插座插槽的意思
            serverSocket = new ServerSocket(9999);
            while (true){
                //2.等待客户端连接,进行侦听
                socket = serverSocket.accept();
                //3.读取客户端的消息
                is = socket.getInputStream();
                //管道流
                baos = new ByteArrayOutputStream();
                //public int read(byte[] b) throws IOException
                // 从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。如果到了文件末尾,返回-1.
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer)) != -1){
                    baos.write(buffer,0,len);
                }
                System.out.println(baos.toString());
            }
            /*//2.等待客户端连接,进行侦听
            socket = serverSocket.accept();
            //3.读取客户端的消息
            is = socket.getInputStream();
            //管道流
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1){
                baos.write(buffer,0,len);
            }
            System.out.println(baos.toString());*/
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流,先关闭外层的处理流,再关闭内层流
            if (baos != null){
                try {
                    baos.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();
                }
            }
        }
    }
}


注意: 关闭连接和IO流

2.3 TCP文件上传

服务器端
//服务器
public class TCPServerDemo02 {
    public static void main(String[] args) throws IOException {
        //1.创建服务
        ServerSocket serverSocket = new ServerSocket(9000);
        //监听客户端的连接
        Socket socket = serverSocket.accept();//阻塞式监听,会一直等待客户端连接
        //获取输入流
        InputStream is = socket.getInputStream();
        //4.文件输出
        FileOutputStream fos = new FileOutputStream(new File("receive.jpeg"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer))!= -1){
            fos.write(buffer,0,len);
        }
        //通知客户端我已经接收完毕了
        OutputStream os = socket.getOutputStream();
        os.write("我接收完毕了,你可以断开了".getBytes());
        //关闭资源
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();

    }
客户端
//客户端
public class TCPClientDemo02 {
    public static void main(String[] args) throws IOException {
        //1.创建一个Socket连接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
        //创建一个输出流
        OutputStream os = socket.getOutputStream();
        //3.文件流
        FileInputStream fis = new FileInputStream("2.jpeg");
        //4.写出文件
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer))!= -1){
            os.write(buffer, 0,len);
        }

        //通知服务器我已经结束了
        socket.shutdownOutput();//我已经传输完了

        //确定服务器接收完毕,才能断开连接
        InputStream is = socket.getInputStream();
        //String byte[]
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer2 = new byte[1024];
        int len2;
        while ((len2 = is.read(buffer2))!= -1){
            baos.write(buffer2,0,len2);
        }
        System.out.println(baos.toString());
        //关闭资源
        fis.close();
        os.close();
        socket.close();
    }
}

3. Tomcat

服务端

  • 自定义 S
  • Tomcat服务器 S:Java后台服务器

客户端

  • 自定义 C
  • 浏览器 B

4. UDP

类似于发短信:不需要连接,需要对方的地址!
DatagramPacket 类:该类表示数据报包,用于实现无连接分组传送服务。 仅基于该数据包中包含的信息,每个消息从一台机器路由到另一台机器。 从一台机器发送到另一台机器的多个分组可能会有不同的路由,并且可能以任何顺序到达,不能保证包的传送是否到达。

4.1 聊天实现

4.11 发送消息
//UDP不需要连接服务器
public class UDPClientDemo01 {
    public static void main(String[] args) throws Exception {
        //1.建立一个socket,用于发送数据报
        DatagramSocket socket = new DatagramSocket();
        //2.建一个包
        String msg = "你好啊,服务器!";
        //发送给谁
        InetAddress localhost = InetAddress.getByName("localhost");
        int port = 9090;
        //数据,数据的长度起始,要发送给谁
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        // 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号
        DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
        //3。发送包
        socket.send(datagramPacket);
        //4.关闭流
        socket.close();
    }
}
4.12 接收消息
//UDP没有明确的客户端和服务器之分
    //服务器更像是接收快递,客户端相当于发送快递,如果服务器没有开启,就永远接收不到客户端发来的消息;
    //所以还是要等待客户端的连接!处于随时侦听的状态
public class UDPServerDemo01 {
    public static void main(String[] args) throws Exception {
        //1.开放端口
        DatagramSocket socket = new DatagramSocket(9090);
        //2.接收数据包,更像是接收快递,客户端相当于发送快递,如果服务器没有开启,就永远接收不到客户端发来的消息;
        byte[] buffer = new byte[1024];
        //DatagramPacket(byte[] buf, int offset, int length)
        //构造一个 DatagramPacket用于接收长度的分组 length ,指定在缓冲器中的偏移量。
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        //receive(DatagramPacket p) 从此套接字接收数据报包。
        socket.receive(packet);//阻塞接收

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(new String(packet.getData(), 0, packet.getLength()));
        //3.关闭连接
        socket.close();
    }
}

注意: 我在UDP通信过程中遇到了传输中文乱码以及异常截断等问题,原因在于在将字符串放入packet的时候,给的是字符串的长度,用字节流传输应该是给字节数组大小。

4.2 UDP聊天实现(UDP在线咨询)

(多线程,交互聊天)
在线咨询:两个人都可以是发送方,也可以是接收方!

4.21 发送信息
public class TalkSend implements Runnable {
    //一开始就建立连接
    DatagramSocket socket = null;
    BufferedReader reader = null;
    //自己的IP
    private int fromPort;
    //对方的地址和端口号
    private String toIP;
    private int toPort;

    //构造器,构造器需要初始化IP和端口号
    public TalkSend(int fromPort, String toIP, int toPort) {
        this.fromPort = fromPort;
        this.toIP = toIP;
        this.toPort = toPort;
        try {
            socket = new DatagramSocket(fromPort);
            //准备数据:控制台输入System.in
            reader = new BufferedReader(new InputStreamReader(System.in));
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while (true){
            try {
                String data = reader.readLine();
                byte[] datas = data.getBytes();
                DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(toIP , toPort));
                socket.send(packet);
                if (data.equals("bye")){
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        socket.close();
    }
}
4.22 接收信息
public class TalkReceive implements Runnable {
    DatagramSocket socket = null;
    private int port;
    private String msgFrom;

    public TalkReceive(int port, String msgFrom) {
        this.port = port;
        this.msgFrom = msgFrom;
        try {
            socket = new DatagramSocket(port);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true){
            try {
                //准备接收包裹
                byte[] container = new byte[1024];
                DatagramPacket packet = new DatagramPacket(container, 0, container.length);
                socket.receive(packet);//阻塞式接收包裹,接收完,读取数据
                //断开连接
                //获取报文字符,判断是否是bye,如果是,则断开连接
                byte[] data = packet.getData();
                String receiveData = new String(data, 0, data.length);
                System.out.println(msgFrom + ":" + receiveData);
                if (receiveData.equals("bye")){
                    break;
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        socket.close();
    }
}

4.23 教师端
public class TalkTeacher {
    //老师给学生发消息
    public static void main(String[] args) {
        //老师发送消息的端口是5555,向学生8888端口发送消息
        new Thread(new TalkSend(5555, "localhost", 8888)).start();
        new Thread(new TalkReceive(9999, "学生")).start();
    }
}

4.24 学生端
public class TalkStudent {
    //学生给老师发消息
    public static void main(String[] args) {
        //学生7777端口向老师9999端口发送消息
        //学生接收消息的端口是8888
        new Thread(new TalkSend(7777, "localhost", 9999)).start();
        new Thread(new TalkReceive(8888, "老师")).start();
    }
}

我们也可以用cmd窗口执行class文件,进入package目录,执行**.class**文件,注意,java编译指令的class文件名需包含包名的完整名称,如

java com.kuang.net.chat.TalkStudent

注意:端口问题
在这里插入图片描述

5. URL-统一资源定位符

URL:统一资源定位符,用来定位互联网上面的某一个资源。例如https://www.baidu.com/
DNS域名解析:www.baidu.com xxx.xxx.xxx.xxx

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

5.1 URL类及常用方法

类:URL,官方文档介绍:
Class URL表示统一资源定位符,指向万维网上的“资源”的指针。 资源可以像文件或目录一样简单,或者可以是对更复杂的对象的引用,例如对数据库或搜索引擎的查询。 有关URL类型及其格式的更多信息,请访问: Types of URL
一般来说,URL可以分为几个部分。 请考虑以下示例:

 http://www.example.com/docs/resource1.html

上面的URL表示要使用的协议是http (超文本传输协议),并且信息驻留在名为www.example.com的主机上。 该主机上的信息名为/docs/resource1.html 。 主机上此名称的确切含义取决于协议和主机。 信息通常驻留在一个文件中,但它可以在飞行中生成。 该URL的这个组件称为路径组件。

public class URLDemo01 {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=kuangshen&password=123");
        System.out.println(url.getProtocol()); //协议
        System.out.println(url.getHost()); // 主机IP
        System.out.println(url.getPort()); // 端口
        System.out.println(url.getPath()); // 文件
        System.out.println(url.getFile()); // 文件全路径
        System.out.println(url.getQuery()); // 参数
    }
}

5.2 下载资源

public class URLdown {
    public static void main(String[] args) throws Exception {
        //下载地址
        URL url = new URL("https://m10.music.126.net/20200702124433/8acb31bf2f4df3a49b507051ec98b096/yyaac/obj/wonDkMOGw6XDiTHCmMOi/3049281714/8858/e09f/2eee/e171322b80b816df16c4fc5bcc6f05cd.m4a");
        //连接到这个资源,用HTTP连接
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        //通过连接获得输入流
        InputStream inputStream = urlConnection.getInputStream();
        FileOutputStream fos = new FileOutputStream("e171322b80b816df16c4fc5bcc6f05cd.m4a");

        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer))!= -1){
            fos.write(buffer, 0, len);//写出这个数据
        }
        fos.close();
        inputStream.close();
        urlConnection.disconnect();//断开连接
    }
}

以上为狂神视频笔记加少许自己的理解和整理,其中需要拓展的知识会另外分享,我们一起加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值