第二十九讲——网络编程

第二十九讲——网络编程

1——什么是计算网络

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

  • 在地理不同的多台计算机,通过通信线路,实现资源共享和信息传递的计算机系统。

比较重点的协议

拨电话 链接 接通 通话 TCP/协议
发短信 发送 接收 UDP协议

网络编程的目的

  • 信息传递
  • 资源共享

网络编程条件

  • 如何定位到网络上的一台主机或者多台主机 ipv4在 2019 年11 月全球 43 亿地址分配完毕

    • ip(主机地址) 192.168.31.124
    • 端口(软件地址“客户端”),定位到这个计算机上的某个资源
  • 定位到主机,如何传输数据 :

假设端口是开放的没有防火墙,可以放一些病毒,整个网络就瘫痪了

Javaweb: 网页编程 B/S 面对浏览器

网络编程: TCP/IP C/S 面对客户端

2——网络编程的要素

graph LR; 1("如何实现网络通信")-->2("通信双方地址") 1-->3("网络通信协议")

通信双方地址;

  • ip

  • 端口

规则: 网络通信协议;

TCP/IP 四层概念模型

image

重点讲解; 传输层 TCP UDP

image


小结

网络编程中两个主要问题;

  • 如何准确的定位网络上的一台或者多台主机
    • ip 和 端口
    • 网络通信协议 UDP TCP
  • 定位到主机后如何通信

3——IP inetAddress

此类表示互联网协议 (IP) 地址。


ip地址:inetAddress

  • 唯一定位一台网络上的计算机
  • 本机 ip(唯一没有网也能ping 的ip地址) localhost 127.0.0.1

image

  • ip地址的分类

    • ipv4/ipv6

      • ipv4 ; 127.0.0.1 , 24位 4个字节组成。 0~255 , 共有43亿;
      • ipv6 ; 128位 。8个无符号整数 无穷尽
      // 例如 2001:abb1:aaaa:0105:0000:0000:1eee:1213
      
    • 公网(互联网 ipv4)—私网(局域网)

      • 192.168.xxx.xx,专门给组织内部使用的

Application

调用的是静态的方法所以不需要 new

package Inet;
import java.net.InetAddress;
import java.net.UnknownHostException;

// 测试 ip
public class TestInetAddress {
    public static void main(String[] args) {
        try {
            // 查询本机地址
            InetAddress inetAddress01 = InetAddress.getByName("127.0.0.1");
            // 根据原始 IP 地址创建的 InetAddress 对象。
            // 这里是用名字去返回ip地址, 前面说了 ip 和域名是互通的,所以这里可以写 ip
            System.out.println(inetAddress01);

            InetAddress inetAddress02 = InetAddress.getByName("localhost"); // 本机地址
            System.out.println(inetAddress02);

            InetAddress inetAddress03 = InetAddress.getLocalHost();
            System.out.println(inetAddress03);


            // 查询网站地址
            InetAddress inetAddress00 = InetAddress.getByName("www.baidu.com");
            // 可以直接用 ip 去搜索 因为 ip 不好记,才有的域名  www.baidu.com/119.63.197.151
            System.out.println("\n"+inetAddress00);


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

Print

D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\Application\out\production\Application Inet.TestInetAddress
/127.0.0.1
localhost/127.0.0.1
DESKTOP-SVLJGP3/192.168.31.235

www.baidu.com/119.63.197.139

Process finished with exit code 0

遗留问题 1. 为什么能用百度 ip直接访问网站,而哔哩哔哩却不能? www.bilibili.com/118.193.16.42

参考链接;为啥用ip不可以访问知乎,而百度却可以? - 帅地 - 博客园 (cnblogs.com)


4——IP 地址 + 端口号 InetSocketAddress

此类实现 IP 套接字地址(IP 地址 + 端口号)。

image


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

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

  • 被规定

  • 端口号不能冲突; TCP/UDP 端口 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      #DOS 命令 查看所有的端口状态 
      netstat -ano|findstr "13460"   # 可以查看指定的端口 QQ的端口号
      tasklist # 任务列表
      tasklist|findstr "13460"  # 查看指定端口的进程
      
      

image


Application

package Inet;

import java.net.InetSocketAddress;

public class TestInetSocketAddress {
    // 套接字Socket来定位和连接某台服务器提供的某个服务,套接字实际上可以看作一个组合(IP地址+端口port)
    public static void main(String[] args) {

        InetSocketAddress inetSocketAddress01 = new InetSocketAddress("127.0.0.1",13460);
        InetSocketAddress inetSocketAddress02 = new InetSocketAddress("localhost",13460);

        System.out.println(inetSocketAddress01);
        System.out.println(inetSocketAddress02);

        System.out.println("\n"+inetSocketAddress01.getAddress());
        System.out.println(inetSocketAddress01.getHostName()); // 主机名字 可以在 host 文件中设置
        System.out.println(inetSocketAddress01.getPort()); // 端口
    }
}

Print

D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=57765:D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\Application\out\production\Application Inet.TestInetSocketAddress
/127.0.0.1:13460
localhost/127.0.0.1:13460

/127.0.0.1
127.0.0.1
13460

Process finished with exit code 0


电脑之间的传输;

image


5——通信协议

协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。它的三要素是:语法、语义、时序。
​ 为了使数据在网络上从源到达目的,网络通信的参与方必须遵循相同的规则,这套规则称为协议(protocol),它最终体现为在网络上传输的数据包的格式。

​ 协议往往分成几个层次进行定义,分层定义是为了使某一层协议的改变不影响其他层次的协议。


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

重要;

  • TCP : 数据控制协议
  • UDP : 用户数据报协议

出名的协议 ;

  • TCP : 数据控制协议
  • IP : 网络互连协议

image

TCP UDP 对比

TCP(打电话) UDP(发短信)
连接,稳定 不连接,不稳定
客户端,服务端 客户端,服务端 :没有明确的界限
传输完成,释放连接,效率低 不管有没有准备好 , 都可以发给你 ....(能不能收到要看很多的因素)
三次握手 四次挥手 DDOS :洪水工具(发送大量数据包到电脑上虽然本身不带病毒但是可以导致堵塞网络) ! (饱和攻击)
最少需要三次,保证稳定 连接!
    A : Hello!
    B : Hi~
    A : I Love You!
        
四次挥手断开连接。
    A : 不聊了,我妈叫我回去吃饭了
    B : 你真的要去吃饭了吗
    A : 我真的要走了
    B : 我知道了

6——TCP连接

TCP 单方实现聊天

试了一天没有成功原来是因为在最后 finally 的时候没有抛出异常导致的——读入写出

客户端

  1. 链接服务器 Socket
  2. 发送消息

服务端

  1. 建立服务的端口 ServerSocket
  2. 等待用户的链接 accept (accept会返回一个socket 理解为和客户端连接的一个对象能互相传递消息)
  3. 接收用的消息(在返回的scoket上有 get方法来返回流(输入流或输出流))

TcpServer

package TCPLesson01;

import java.io.ByteArrayInputStream;
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 accept = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;

        try {
            // 1. 我得有一个自己的 ip地址(默认地址) 和 端口号
            /**  服务端套接字 :  ServerSocket
             *
             * 现在服务端 LocalHost 是 IP:127.0.0.1 Port:9999
             * */
             serverSocket = new ServerSocket(9999);
            //创建特定端口的服务器套接字

            while(true){ // 加上while 可以实现循环监听不停的监听
                // 2. 等待客户端连接过来  accept  侦听并接受到此套接字的连接。
                accept = serverSocket.accept();
                // 当连接上时他们(服务端的accept 和客户端的socket)是同一个对象

                // 3. 读取客户端的消息
                is = accept.getInputStream();

                /** 这是学 IO 时候用来读取数据的方法  (在这里不可取)
                 *             byte[] buffer = new byte[1024];
                 *             int len;
                 *             while((len=is.read(buffer))!=-1){
                 *                 String s = new String(buffer,0,len);
                 *                 System.out.println(s);
                 *             }
                 *  缺点是有中文可能会有乱码,因为中文一字两字节,标点是一字节当字节数,不能被1024整除时就会有乱码
                 */

                // 管道流

                baos = new ByteArrayOutputStream();
                // 此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。所以避免了乱码
                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 (accept!=null){
                try {
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

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

            }


        }


    }
}

TcpClient

package TCPLesson01;

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;

        try {
            // 1. 链接 Server 要知道服务器的地址 端口号
            InetAddress serverIp = InetAddress.getByName("127.0.0.1");
            int port = 9999;

            //  创建一个 socket 连接
            /*** 客户端套接字 : Socket
             *
             * 客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
             */
            socket = new Socket(serverIp,port);
            // 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
            // 2. 发送消息 IO流
            os = socket.getOutputStream();// # 我初步理解为,创建一个客户端流通道
            os.write(("Hello,JAVA~".getBytes() ));


        } catch (Exception e) {
            // 这些对象都有对应的异常
            e.printStackTrace();
        } finally{
            // 关闭资源 遵循先开后关  关闭需要捕获异常
            // 反正 客户端是一定要关闭流的,要不然会报错 java.net.SocketException: Connection reset
            // 视频中说如果为null 就没有必要关闭了 这个不李姐
            if (os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

  1. 报警么看看 ip 是不是写错 127 172 (细心一点)

image


TCP 实现文件上传

Server

package TCPLesson01;

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

public class TCPServerDemo02 {
    public static void main(String[] args)  throws Exception{
          // 一般情况下不建议这样偷懒抛出异常 要像上一篇一样抛出
        // 1. 创建端口
        ServerSocket serverSocket = new ServerSocket(9000);

        // 2. 监听客户端连接,是阻塞式的; 客户端没有连接过来是不会停止程序的,就像 Scanner类 等待键盘输入的阻塞一样
        Socket socket = serverSocket.accept();//阻塞式侦听,会一直等待客户端连接,连接后自动取消阻塞"关闭"

        // 3. 获取输入流
        InputStream is = socket.getInputStream();

        // 4. 文件输出
        FileOutputStream fos = new  FileOutputStream(new File("receive.jpg"));
        // 图片是相对路径是Application下的,不知道为什么要这样放,其他路径都不行
        byte[] buffer = new byte[1024];
        int length;
        while((length=is.read(buffer))!=-1){
            fos.write(buffer,0,length);
        }

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


        // 5. 关闭资源
        os.close();
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();

    }
}
/**
 * 如果其他电脑没有开防火墙,能ping到地址的话,就能从他的电脑下载文件,完成入侵
 * */

Client

package TCPLesson01;

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

public class TCPClientDemo02 {
    public static void main(String[] args) throws Exception{
        // 一般情况下不建议这样偷懒抛出异常 要像上一篇一样抛出


        // 1. 创建一个Socket连接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9000);
        /** 可以设置 socket 的端口号
            InetAddress localInetAddress01 = InetAddress.getByName("localhost");
            int localPort = 8080;
            socket = new Socket(inetAddress,port,inetAddress01,9090);
        */ 
        // 2. 创建一个输出流
        OutputStream os = socket.getOutputStream();

        // 3. 读取文件
        FileInputStream fis =  new FileInputStream(new File("1.jpg"));
        // 图片是相对路径是Application下的,不知道为什么要这样放,其他路径都不行

        // 4. 写出文件
        byte[] buffer = new byte[1024];
        int length;
        while((length=fis.read(buffer))!=-1){
            os.write(buffer,0,length);
        }

        // 通知服务器, 我已经发送完毕  如果没有这个那么两边都在等待
        socket.shutdownOutput();
        //禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列

        // 确定服务器接收完毕才能断开连接 这个非常简单 他就是接收来自服务端的一句话
        InputStream inputStream = socket.getInputStream();
        // String byte[]
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 接过来的东西 我们不认识 要通过管道流才能认识
        byte[] buffer01 = new byte[2014];
        int length01;
        while((length01 = inputStream.read(buffer01))!=-1){
            baos.write(buffer01,0,length01);
        }

        System.out.println(baos.toString());
        // 5. 关闭资源
        baos.close();
        inputStream.close();
        fis.close();
        os.close();
        socket.close();

    }
}
  1. 妈耶 接收消息乱码因为打错字了 buffer01 打成 buffer

print

D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\Application\out\production\Application TCPLesson01.TCPClientDemo02
我接收完毕了,你可以断开了

Process finished with exit code 0

// 在关于流的内容中,要注意及时的关闭流,及关闭的顺序
// 初步理解在关于 TCP 传输文件时需要配合 shutdownOutput 使用

image


7——初始Tomcat

​ Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。

​ 对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应 HTML 下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与 Apache 独立的进程单独运行的

  • 服务端
    • 自定义 S
    • Tomcat 服务器 ( 人家写好的,用 JAVA 写的 ) S : JAVA
  • 客户端
    • 自定义 C
    • 浏览器 B

对这一小节没有任何要求,知道有 Tomcat 用于中小型web开发的 就可以了


8——UDP 连接

发包与接包

不需要连接就像发快递和收快递

UDP 没有服务端和客户端,因为没有连接和被连接的概念

只有发送方和接收方的,且同时可以是发送方也可以是接收方

PS:   以下代码使用服务端和客户端名称,是为方便理解

  • DatagramPacket 此类表示数据报包。
  • DatagramSocket 此类表示用来发送和接收数据报包的套接字。

PID:9090---发送--->PID:8080

UDPClient

package UDP;

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

// 不需要连接服务器
public class UDP9090 {
    public static void main(String[] args) {
        DatagramSocket users01 = null;
        try {
            // 1. 建立一个 本地端口
             users01 = new DatagramSocket(9090);
             // 不设置 port 的话会自己绑定一个空闲 UID

            // 2. 建立一个包
            /**
             * 一个完整的包;
             * 五大参数
             * 发送内容, 起止位置(起,止),发送地址(localhost,port)
             * */

            String msg = "在吗";
            // 发给谁
            InetAddress inetAddress = InetAddress.getByName("localHost");
            int port = 8080;
            // 5大参数:  发送内容, 起止位置(起,止),发送地址(localhost,port)
            DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,inetAddress,port);

            // 3. 发送一个包
            users01.send(packet);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            users01.close();
            // UDP 发送消息是不会报错的, 因为不需要建立连接而TCP 需要建立连接才能发送所以会报错
        }


    }
}

UDPServer

package UDP;

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

public class UDP8080 {

    public static void main(String[] args) {
        DatagramSocket users02 = null;
        try {

            // 1. 开放端口
             users02 =new DatagramSocket(8080);
            // 2. 接收数据包
            byte[] buffer = new byte[1024];
            DatagramPacket  packet = new DatagramPacket(buffer,0,buffer.length);

            users02.receive(packet); // 阻塞接收

            System.out.println("发送人ip ;" +packet.getAddress().toString());
            System.out.println("发送人PID ;"+packet.getPort());
            System.out.println("接收内容; "+new String(buffer,0,buffer.length));
            System.out.println("接收内容; "+new String(packet.getData(),0,packet.getData().length));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            users02.close();
        }
    }
}

print

D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\Application\out\production\Application UDP.UDPServer
发送人ip ;/127.0.0.1
发送人UID ;9090
接收内容; 在吗                                                                       
接收内容; 在吗
Process finished with exit code 0

可以互相发送、接收,有时间再搞了


image


# 讲到这里 有两种方式处理接收到的 InputStream 的数据
   # 利用管道流(ByteArrayOutputStream)
   # 输入到 缓冲流 再 new String()
# 因为接收到的数据格式是 byte[] ,都可以用这两种方式,无疑下一种方式简单粗暴,在小数据下,建立一定 大小的 byte[] 是能接收的 (超额就读取出错)
#但是如果涉及到大量的数据,无疑 管道流的对数据非常友好的 它的特性就是随着输入的数据不断的增长内存 从而能接收大量数据

UDP-单方聊天实现

UDPSender

package UDP.Chat;

import Snake01.Data;

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

public class UDPSender {
    public static void main(String[] args) {

        DatagramSocket socket = null;
        try {
            // 1. 创建数据报套接字
             socket = new DatagramSocket(8080);

            // 准备数据; 控制台(idea的控制台)读取 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("127.0.0.1",9090));
                socket.send(packet);

                if (data.equals("bye")){
                    break;
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            socket.close();
        }
    }
}

UDPReceive

package UDP.Chat;

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

public class UDPReceive {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {

            // 1. 创建数据报套接字
             socket = new DatagramSocket(9090);

            // 2. 接收包
            while(true){

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

                socket.receive(packet); // 阻塞式接收

                byte[] data = packet.getData();
                String receiveData = new String(data,0,data.length);// 这里的length 是1024 所以会产生大量的空字符 
                System.out.println(receiveData.trim());
                // 如果是 "bye" 就关闭
                if (receiveData.trim().equals("bye")){
                    break;// 一定要 去除 字符串的空字符才能 判定为 true
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            socket.close();
        }

    }

}

print

F:\Application\out\production\Application UDP.Chat.UDPSender
你知道吗,我最喜欢的,是看见你的笑      
如果时间,能够停止,我愿守护着你,直到永远                                                              
永远是多远,很久是多久,我没那么贪,我只想你的以后,和我的以后,是一个以后                                               
你有错,遇到了你,我变得小气,从来没有想过退让 
bye
Process finished with exit code 0


get 新技能,获取控制台的字符串

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
//  BufferedReader     缓冲的阅读器 
//  InputStreamReader  输入流阅读器

初步理解多线程就是,边听还能边读,众所周知,程序是按照线走下来的,多线就是提供多个线并运行解决 "方案"


UDP -多线程——实现对话

能说能收,实现对话

​ 这里发现一个点; 在同一个发送人,或接收人,他们用的接收或发送端口并不是同一个。也就是说需要用两个端口一个负责接收,一个负责发送。 这个我猜想是,就像人一样,嘴巴只能说不能听,耳朵只能听不能说,当他们组合的时候才是一个完整的接收系统。

TalkStudent

package Work.Talk;

public class Student {
    public static void main(String[] args) {


            // new 线程 (new 可运行的 (本地主机PID , 接收方IP ,接收方PID))
            TalkSender talkSender = new TalkSender(9999,"localHost",7777);
            // 两个参数; 用什么PID 收, 收的是谁的数据
            TalkReceive talkReceive = new TalkReceive(8888,"老师");

            // 把 Runnable 装入线程
        /***
         * 静态代理模式
         * Thread 是一个代理 代理的是 Runnable 接口 这里不做深究 后面学
         */
            Thread sender  = new Thread(talkSender);
            Thread receive = new Thread(talkReceive);

            /***
             *  两个线程启动
             *  Thread 线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
             *  start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
             */
        // 两个线程启动
            sender.start();
            receive.start();

        // 如果 数据报套接字 是关闭的状态就关闭线程
        // 目前没找到合理的方式退出 虚拟机 只能用 System.exit(); 暴力退出
        // 等学到多线程 再回来补上方法

        while(true){
            // 关闭线程
            // 如果 数据报套接字接受或者发送,任意一个是关闭的状态, 就关闭线程
            if (talkSender.sender.isClosed() | talkReceive.receive.isClosed()){
                try {
                    sender.interrupt();
                    receive.interrupt();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }

        }

        System.out.println("线程是否关闭; "+receive.isInterrupted());

        System.out.println("结束程序");

        System.exit(88);


    }
}

TalkTeacher

package Work.Talk;

public class Teacher {
    public static void main(String[] args) {

        // new 线程 (new 可运行的 (本地主机PID , 接收方IP ,接收方PID))
        TalkSender talkSender = new TalkSender(6666,"localHost",8888);
        // 两个参数; 用什么PID 收, 收的是谁的数据
        TalkReceive talkReceive = new TalkReceive(7777,"学生");

        // 把 Runnable 装入线程
        /***
         * 静态代理模式
         * Thread 是一个代理 代理的是 Runnable 接口 这里不做深究 后面学
         */
        Thread sender  = new  Thread(talkSender);
        Thread receive = new Thread(talkReceive);


        /***
         *  两个线程启动
         *    Thread 线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
         *    start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
         */

        // 两个线程启动
        sender.start();
        receive.start();

        // 如果 数据报套接字 是关闭的状态就关闭线程
        // 目前没找到合理的方式退出 虚拟机 只能用 System.exit(); 暴力退出
        // 等学到多线程 再回来补上方法

        while(true){
            // 关闭线程
            // 如果 数据报套接字接受或者发送,任意一个是关闭的状态, 就关闭线程
            if (talkSender.sender.isClosed() | talkReceive.receive.isClosed()){
                try {
                    sender.interrupt();
                    receive.interrupt();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }

        }

        System.out.println("线程是否关闭; "+receive.isInterrupted());

        System.out.println("结束程序");

        System.exit(88);

    }
}

TalkSender

package Work.Talk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;

/**
 * 作为线程要 实现 Runnable
 * Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现.
 */

// 发送方
public class TalkSender implements Runnable {

    // 5. 提高属性 作用域
    BufferedReader  reader = null;
    DatagramSocket sender = null;
    DatagramPacket packet = null;

    // 1. 建立发送参数
     // 本机PID
     private int  localHostPort;
     // 接收方IP
     private String toIp;
     // 接收方PID
     private int toPort;

     // 2. 构造器初始化内容
    public TalkSender(int localHostPort, String toIp, int toPort) {

        this.localHostPort = localHostPort;
        this.toIp = toIp;
        this.toPort = toPort;

        // 2.1 读取 Control 内容
        reader = new BufferedReader(new InputStreamReader(System.in));
        // 新建缓冲的阅读器,需要给它一个阅读器(给了一个输入流的阅读器 InputStreamReader 需要给一个输入流 inputStream ,然后给他一个“标准”输入流。 )
        // 缓冲的阅读器 配合 new InputStreamReader(System.in) 从控制台读取字符

        try {
            // 2.2 建立数据报套接字
             sender = new DatagramSocket(localHostPort);
        } catch (SocketException e) {
            e.printStackTrace();
        }

    }

    // 说白了就是作为发送方要抽取一个发送消息的方法独立出来 ,然后在使用的时候调用 run 方法 使其并运行
    @Override
    public void run() {

        try {

            while(true){
                // 3. 建立包
                String content = reader.readLine();
                byte[] contentData = content.getBytes();
                 packet = new DatagramPacket(contentData,0,contentData.length,new InetSocketAddress(toIp,toPort));
                // 4. 发送包
                sender.send(packet);

                if (content.contains("bye")){
                    break;
                    // 能检查到包含,这里只会退出 while 不会退出程序
                }

            }
            // 6. 关闭
            sender.close();
            reader.close();

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

        /**
         * void run()
         * 使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
         * 方法 run 的常规协定是,它可能执行任何所需的动作。
         */

    }

}

我居然在 send类 while 用这个语句来判定是否退出 哈哈哈 然后就发现,聊天的时候发送两句被吃了一句,粗心了就是被这个 reader.readLine() 吃了一行 浪费了我好久时间!


               if (reader.readLine().contains("bye")){
                    System.out.println("拜拜~");
                    break;
                }

TalkReceive

package Work.Talk;

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


// 接收方
public class TalkReceive implements Runnable{

    // 5. 提高 属性 作用域
    DatagramSocket receive = null;
    DatagramPacket packet = null;

    // 1. 建立接收数据
      // 本地PID
    private int localHostPort;
      // 发送方是
    private String sender;

    // 2. 构造器初始化内容
    public TalkReceive(int localHostPort, String sender) {

        this.localHostPort = localHostPort;
        this.sender = sender;
        try {
            // 2.1 建立数据报套接字
             receive = new DatagramSocket(localHostPort);

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

    }

    @Override
    public void run() {
        try {
            while(true){

                // 3. 建立包
                byte[] content = new byte[1024];
                packet = new DatagramPacket(content,0,content.length);
                // 4. 接收包
                receive.receive(packet);
                String castString = new String(packet.getData(),0,packet.getData().length).trim();
                System.out.println(sender+": "+castString);

                if (castString.contains("bye")){
                    break;
                }

            }

            // 6. 关闭
            receive.close();
            
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

print

image

用 CMD 就可以开两个窗口 是GBK Encodings IDEA是UTF-8 语言不通所以乱码

image


9——URL

http://www.baidu.com/

统一资源定位符: 定位资源的,定位互联网上的某一个资源

DNS 域名解析 http://www.baidu.com/ xxxx..x..x..x

原本是一个 ip 通过 DNS 域名解析后 才有的网站名字


URL 的组成协议:// ip 地址 :端口/项目名/资源  

URL——基本内容

Applciation

package URL;

import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo01 {
    public static void main(String[] args) {

        try {
            URL url = new URL("http://localHost:8080/helloworld/index.jsp?username=XXZ&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()); // 查找参数

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

    }
}

print

"D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\jbr\bin\java.exe" "-javaagent:D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=58626:D:\java\IDEA—2019\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\Application\out\production\Application URL.URLDemo01
http
localHost
8080
/helloworld/index.jsp
/helloworld/index.jsp?username=XXZ&password=123
username=XXZ&password=123

Process finished with exit code 0

这里不要深究做什么东西,主要是做到 URL 的基本功能


URL——下载网上资源

Application

package URL;

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

public class URLDown {
    public static void main(String[] args) {
        try {
            // 1. 下载地址
            URL url =  new URL("https://m801.music.126.net/20211029220343/30b65f6e8feeeb1d6f3586bf2415300b/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/11377961129/3b6e/308e/d5ec/7f700c7d5bf17b2131dd07181eb3c054.m4a");
            // 这是 网易云里的 Lemon 我很喜欢的一首

            // 2. 连接到这个资源 HTTP
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();// 初步理解 强转

            InputStream inputStream = urlConnection.getInputStream();
            // 输出到这个 文件夹  F:\Application  URL是包 不算是文件夹 猜想默认是自己的文件夹不是idea 生成的文件夹
            FileOutputStream fos = new FileOutputStream("lemon.m4a");
            // "E:\\File/新建文件夹/lemon.png"
            byte[] buffer = new byte[1024];
            int len;
            while((len = inputStream.read(buffer))!=-1){
                fos.write(buffer,0,len); // 写出这个数据
            }

            fos.close();
            inputStream.close();
            urlConnection.disconnect(); // 断开连接
        } catch (Exception e) {
            e.printStackTrace();

        }
    }
}

可以下载网络上的资源,技术ok ,啥都能下



爱了 :)

image

小声嘀咕; 我现在还不想去百度


新增单词

0 UDP 用户数据报协议
1 TCP 传输控制协议
2 inet 网络
3 address 地址 额架死
4 localhost 本机 漏扣后斯特 local 本地的 host 主机
5 port id 端口id port:端口 PID:端口号
6 netstat -ano 查看所有端口状态 诺特丝它特 net ; 理解为网络 stat ; 状态 ano 理解为所有 start :开始 star : 明星
7 findstr 查看指定 字符串 fai因的str
8 tasklist 任务列表 它丝可list task ;任务
9 Socket 套接字 扫肯特
10 client 客户端 可莱恩特
11 server 服务端 射沃 service 服务
12 accept 接受 啊可萨噗特 server.accept ( ) ;
13 receive 接收 ( 签收快递 ) 瑞祀无
14 shutdown 关闭 (关机) 宵特down close; 关闭 合上
15 Tomcat web服务器
16 DatagramPacket 数据报包 个卵M怕KT
17 DatagramSocket 数据报套接字 此类表示用来发送和接收数据报包的套接字。
18 send 发送 森德
19 Chat 聊天 恰特
20 sender 发送人
21 Buffered 缓冲的
22 Reader 阅读器
23 Talk 交谈 Talk show 脱口秀
24 Runnable 可运行的
25 Thread 线程 夫read new Thread(new TalkSend(2222,"localHost",6666)).start()
26 security 机密文件 塞q睿丁
27 Protocol 协议 proT扣
28 openConnection 打开网络连接 肯耐克特 urlConnection.getInputStream();
29 disconnect 断开网络连接 迪丝肯耐克特 urlConnection.disconnect();
30 interrupt 中断(线程) in特rua噗特
posted @ 2021-11-01 20:51  项晓忠  阅读(65)  评论(0编辑  收藏  举报