【Java学习】网络编程全总结——TCP、Udp、多线程、IO流、Socket、简易在线咨询聊天室、Java爬虫

网络编程

1.1、概述

信件:
在这里插入图片描述
打电话:—连接—接了—通话 TCP

发短信:—发送了就完事了—接收 UDP

计算机网络

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

网络编程的目的:

传播交流信息,数据交换,通信。

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

1、如何准确地定位网络上的一台主机:(端口),定位到这个计算机的某个资源。

2、找到了这个主机,如何传输数据呢?

javaweb:网页编程 B/S

网络编程:TCP/IP C/S

1.2、网络通信的要素

如何实现网络上的通信?

通信双方地址:

(192.168.1.1):局域网。。

  • ip:
  • 端口号

规则:网络通信的协议:

TCP/IP参考模型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iJkr6Isx-1636029850721)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20211016163156342.png)]

HTTP:超文本传输协议

FTP:文件上传协议
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gXsad4XM-1636029850723)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20211016163205504.png)]
小结:

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

  • 如何准确的定位到网络上的一台或者多台主机
  • 找到主机之后如何进行通信

2、网络编程中的要素

  • IP和端口号 IP
  • 网络通信协议 UDP,TCP

3、万物皆对象

1.3、IP

ip地址:inetAddress

  • 唯一定位一台网络上计算机
  • 127.0.0.1:本机loaclhost
  • Ip地址的分类
    • Ipv4/Ipv6
      • IPv4 127.00.1,四个字节组成,0-255,42亿(4,228,250,625);30亿都在北美,亚洲四亿,2011年就用尽;
      • IPV6:128位,8个无符号整数!
    • 公网(互联网)-私网(局域网)
      • ABCD类地址
      • 192.168.xx.xx专门给组织内部使用的
  • 域名:记忆IP问题!
    • IP:
package com.mao.lesson01;

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

// 测试IP
public class TestInetAddress {
    
    
    public static void main(String[] args) throws UnknownHostException {
    
    

        // 查询本机地址
        // getByName(String host) 确定主机名称的IP地址。
        final InetAddress byName = InetAddress.getByName("127.0.0.1");
        System.out.println(byName);         //  /127.0.0.1

        final InetAddress localhost = InetAddress.getByName("localhost");
        System.out.println(localhost);      // localhost/127.0.0.1

        // getHostAddress()  返回文本显示中的IP地址字符串。
        final InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);      // DESKTOP-JDIH9D6/10.102.211.250

        System.out.println("========================");
        // 查询网站ip地址
        final InetAddress byName1 = InetAddress.getByName("www.huchao.vip");
        System.out.println(byName1);        // www.huchao.vip/47.93.20.204

        System.out.println("======================");
        // 常用方法
        //
        System.out.println(byName1.getCanonicalHostName());         // 规范的名字   获取此IP地址的完全限定域名。              47.93.20.204
        System.out.println(byName1.getHostAddress());               // ip    返回文本显示中的IP地址字符串。                   47.93.20.204
        System.out.println(byName1.getHostName());                  // 域名,或者自己电脑的名字   获取此IP地址的主机名。    www.huchao.vip

    }
}

1.4、端口(port)

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

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

  • 被规定0-65535

  • TCP,UDP:65536*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

       1、netstat -ano #查看所有的端口
       2、netstat -ano|findstr "9640" # 查看指定的窗口 其中|是指先干后面的再在前面中找
       3、tasklist|findstr "8696" #查看指定端口的进程
      
package com.mao.lesson01;

import java.net.InetSocketAddress;

public class TestInetSocketAddres {
    
    
    public static void main(String[] args) {
    
    
        final InetSocketAddress inetSocketAddress = new InetSocketAddress("47.93.20.204", 8080);
        System.out.println(inetSocketAddress);                                      // /47.93.20.204:8080
        final InetSocketAddress localhost = new InetSocketAddress("localhost", 8080);
        System.out.println(localhost);                                             // localhost/127.0.0.1:8080
        final InetSocketAddress inetSocketAddress1 = new InetSocketAddress("127.0.0.1", 8080);
        System.out.println(inetSocketAddress1);                                   // /127.0.0.1:8080

        System.out.println("===================");
        System.out.println(localhost.getAddress());         // localhost/127.0.0.1
        System.out.println(localhost.getHostName());        // localhost
        System.out.println(localhost.getPort());            // 8080

        System.out.println("==================");
        System.out.println(inetSocketAddress.getAddress());     //  /47.93.20.204
        System.out.println(inetSocketAddress.getHostName());    //  47.93.20.204
        System.out.println(inetSocketAddress.getPort());        //  8080
    }
}

在这里插入图片描述

1.5、通信协议

协议:约定,就好比我们现在说的是普通话。

网络通信协议:速率,传输码率,代码结构,传输控制。。。。。。

问题:非常的复杂

大事化小:分层!

TCP/IP协议簇实际上是一层协议

重要:

  • TCP:用户传输协议
  • UDP:用户数据报协议

出名的协议:

  • TCP:
  • IP:网络互联协议

ps:应用层:QQ、微信;传输层:通信;网络层:分包;数据链路层:具体的传输:如光纤,海底电缆。。。。。。;

TCP UDP对比:

详解 TCP 连接的“ 三次握手 ”与“ 四次挥手 ”

https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc

TCP:打电话

  • 连接,稳定

  • 三次握手、四次挥手

  • 三次握手

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W0EvfJd0-1636029850728)(https://pics1.baidu.com/feed/d8f9d72a6059252d20d93b0a6645fb3e59b5b9d2.jpeg?token=c86d4509157378798ebbccbe843486d1&s=9746F8123F5754CA48D574DA0300D0B2)]

  • 三次握手通俗理解

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HTDeF5o1-1636029850730)(https://pics2.baidu.com/feed/838ba61ea8d3fd1f488fcc3a6790dd1a94ca5f0b.jpeg?token=b04e8b986fcc6303a2871b92b6e7e44a&s=0450E432491271CA18ED00CF0300E0B1)]

     三次握手
     最少需要三次,才能保证稳定连接!
     A;你愁啥?
     B:瞅你咋地?
     A:干一场!  
    

    四次挥手 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mw53wyba-1636029850731)(https://pics5.baidu.com/feed/48540923dd54564e5260495ce0006487d0584fb6.jpeg?token=c3a743af38e25ff66deb6a07891be58e&s=C584FC1A71CFF4EE1A75A45203007073)]

    四次挥手通俗理解
    在这里插入图片描述

     四次挥手
     A:我要走了
     B:你真的要走了吗?
     B:你真的真的要走了吗?
     A:我真的要走了!
    
  • 客户端、服务端

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

UDP:发短信

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

1.6、TCP

有关socket的知识分享:https://www.cnblogs.com/dolphinx/p/3460545.html

API

 public class Socket
  extends Object
  implements Closeable

该类实现客户端套接字(也称为“套接字”)。套接字是两台机器之间通讯的端点。

套接字的实际工作由SocketImpl类的实例执行。 应用程序通过更改创建套接字实现的套接字工厂,可以配置自己创建适合本地防火墙的套接字。

  • 从以下版本开始:

    JDK1.0
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1NN1WjP-1636029850733)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20211101210018259.png)]

SeverSocket:

API

public class ServerSocket
extends Object
implements Closeable

这个类实现了服务器套接字。 服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者。
服务器套接字的实际工作由SocketImpl类的实例执行。 应用程序可以更改创建套接字实现的套接字工厂,以配置自己创建适合本地防火墙的套接字。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DSy01trl-1636029850733)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20211101210045443.png)]

为了使客户能成功地连接到服务器,服务器必须建立一个ServerSocket 对象,该对象通过将客户端地套接字对象和服务器端地一个套接字对象连接起来,从而达到连接的目的。

客户端:(socket对象)socket(IP地址,端口)

服务器:(ServerSocket对象)serversocket(端口 )用ServerSocket.accept()来建立一个和客户端的Socket对象相连接的Socket对象。

服务器端的输出流/输入流的目的地和客户端的输入流/输出流的源刚好相同。
在这里插入图片描述
服务端代码:

package com.mao.lesson02;

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

// 服务端
public class TestServerDemo01 {
    
    
    public static void main(String[] args) {
    
    
        ServerSocket serverSocket = null;
        Socket accept = null;
        InputStream inputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;

        try {
    
    
            // 1.我得有一个地址
            serverSocket= new ServerSocket(9999);

            while (true){
    
    
                //2. 等待客户端连接过来
                accept = serverSocket.accept();
                //3. 读取客户端的消息
                inputStream = accept.getInputStream();

                // 管道流
                byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] bytes = new byte[1024];
                int length;
                while ((length=inputStream.read(bytes))!=-1){
    
    
                    byteArrayOutputStream.write(bytes,0,length);
                }
                System.out.println(byteArrayOutputStream.toString());
            }

        } catch (IOException ioException) {
    
    
            ioException.printStackTrace();
        }finally {
    
    
            if (byteArrayOutputStream!=null){
    
    
                try {
    
    
                    byteArrayOutputStream.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (inputStream!=null){
    
    
                try {
    
    
                    inputStream.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();
                }
            }
        }
    }
}

客户端代码

package com.mao.lesson02;

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) {
    
    
        InetAddress byName =null;
        Socket socket=null;
        OutputStream outputStream=null;
        try {
    
    
            //1.要知道服务器的地址,端口号
            byName = InetAddress.getByName("127.0.0.01");
            int port =9999;
            //2.创建一个socket连接
            socket = new Socket(byName,port);
            //3.发送消息 io 流
            outputStream = socket.getOutputStream();
            outputStream.write("你好,我是胡超,欢迎和我一起学习Java网络编程".getBytes());
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            if (socket!=null){
    
    
                try {
    
    
                    socket.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (outputStream!=null){
    
    
                try {
    
    
                    outputStream.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }

    }
}

每执行一次客户端代码,都会在服务端显示一次
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EWRL3O5O-1636029850734)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20211101222320073.png)]
进程堵塞

提升作用域

文件上传

服务器端:

package com.mao.lesson02;

import com.sun.org.apache.bcel.internal.generic.NEW;

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

/**
 * @Author HuChao
 * @Description //TODO Hasee
 * @Date 17:28 2021/11/2
 * @Param
 * @return
 **/
public class TcpServerDemo02 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 1.创建服务
        final ServerSocket serverSocket = new ServerSocket(9000);
        // 2.监听客户端连接
        final Socket socket = serverSocket.accept();    //阻塞式监听,会一直等待客户端的到来

        // 3.获取输入流
        final InputStream inputStream = socket.getInputStream();
        // 4.文件输出
        final FileOutputStream fileOutputStream = new FileOutputStream(new File("recevie.png"));
        //创立缓冲区和写
        final byte[] bytes = new byte[1024];
        int length;
        while ((length=inputStream.read(bytes))!=-1){
    
    
            fileOutputStream.write(bytes,0,length);
        }
        // 通知客户端接受完毕
        final OutputStream outputStream = socket.getOutputStream();
        outputStream.write("客户端,服务都端接受完毕了,你可以断开了".getBytes());

        // 关闭资源
        outputStream.close();
        fileOutputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

 
 /*
 1、接收要read,所以要用inputStream
 2、要把文件写给大家看,所以要fileoutputstream
  */

客户端:

package com.mao.lesson02;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * @Author HuChao
 * @Description //TODO Huchao
 * @Date 17:28 2021/11/2
 * @Param
 * @return
 **/
public class TcpClientDemo02 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 1.创建一个socket连接
        final Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
        // 2.创建一个输出流
        final OutputStream outputStream = socket.getOutputStream();
        // 3.读取文件
        final FileInputStream fileInputStream = new FileInputStream(new File("双湖拍.png"));
        // 4.写出文件
        final byte[] bytes = new byte[1024];
        int length;
        while ((length=fileInputStream.read(bytes))!=-1){
    
    
            outputStream.write(bytes,0,length);
        }

        // 通知服务器,我已经传输完了
        //发送成功后关闭输出流
        //通知服务器我已经输出结束了
        socket.shutdownOutput();    // 我已经传输完了!

        // 确定服务端接受完毕,才能够断开连接
        final InputStream inputStream = socket.getInputStream();//接收来自服务端的消息,用数组流来输出
        /**
         *     public byte[] getBytes() {
         *         return StringCoding.encode(value, 0, value.length);
         *     }
         * */
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        final byte[] bytes1 = new byte[1024];
        int length2;
        while ((length2=inputStream.read(bytes1))!=-1){
    
    
            byteArrayOutputStream.write(bytes1,0,length2);
        }
        System.out.println(byteArrayOutputStream.toString());

        // 5.关闭资源
        byteArrayOutputStream.close();
        inputStream.close();
        fileInputStream.close();
        outputStream.close();
        socket.close();
    }
}

 /*
 1、抛出异常
 2、套接字的get输入输出流
 3、通知服务器,我已经输出结束了:socket.shutdownOutput();
 4、while((len=is.read(buff))!=-1){
     bos.write(buff,0,len);
 5、while和0-长度
 }
  */

在这里插入图片描述
在这里插入图片描述

Tomcat

调试:consolehandler:GBK

服务端

  • 自定义(S)
  • Tomcat服务器(S):Java后台开发用别人的服务器!

客户端

  • 自定义(C)
  • 浏览器(B)

1.7、UDP

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

发送消息

发送端
API

public final class DatagramPacket
extends Object
该类表示数据报包。 
    
数据报包用于实现无连接分组传送服务。 仅基于该数据包中包含的信息,每个消息从一台机器路由到另一台机器。 从一台机器发送到另一台机器的多个分组可能会有不同的路由,并且可能以任何顺序到达。 包传送不能保证。 
    • 构造方法摘要

      Constructor and Description
      DatagramPacket(byte[] buf, int length) 构造一个 DatagramPacket用于接收长度的数据包 length
      DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
      DatagramPacket(byte[] buf, int offset, int length) 构造一个 DatagramPacket用于接收长度的分组 length ,指定偏移到缓冲器中。
      DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造用于发送长度的分组数据报包 length具有偏移 ioffset指定主机上到指定的端口号。
      DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 构造用于发送长度的分组数据报包 length具有偏移 ioffset指定主机上到指定的端口号。
      DatagramPacket(byte[] buf, int length, SocketAddress address) 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
package com.mao.lesson03;

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

/**
 * @ClassName UdpClientDemo01
 * @Description TODO
 * @Author HUCHAO
 * @Date 2021/11/2 18:08
 * @Version 1.0
 **/
// 不需要连接服务器
public class UdpClientDemo01 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 1. 建立一个Socket
        final DatagramSocket datagramSocket = new DatagramSocket();
        // 2. 建个包
        String msg ="你好啊,服务器!";
        final InetAddress localhost = InetAddress.getByName("localhost");
        int port=9090;

        // 数据,数据的长度起始,要发给谁
        final DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,localhost,port);

        // 3.发送包
        datagramSocket.send(datagramPacket);
        // 4.关闭流
        datagramSocket.close();
    }
}

接收端:
API

public class DatagramSocket
extends Object
implements Closeable
此类表示用于发送和接收数据报数据包的套接字。 
    
数据报套接字是分组传送服务的发送或接收点。 在数据报套接字上发送或接收的每个数据包都被单独寻址和路由。 从一个机器发送到另一个机器的多个分组可以不同地路由,并且可以以任何顺序到达。 

在可能的情况下,新构建的DatagramSocket启用了SO_BROADCAST套接字选项,以允许广播数据报的传输。 为了接收广播数据包,DatagramSocket应该绑定到通配符地址。 在一些实现中,当DatagramSocket绑定到更具体的地址时,也可以接收广播分组。 

示例: DatagramSocket s = new DatagramSocket(null); s.bind(new InetSocketAddress(8888));其中相当于: DatagramSocket s = new DatagramSocket(8888);这两种情况都将创建一个DatagramSocket,可以在UDP端口8888上接收广播。 
    • 构造方法摘要

      Modifier Constructor and Description
      DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。
      protected DatagramSocket(DatagramSocketImpl impl) 使用指定的DatagramSocketImpl创建一个未绑定的数据报套接字。
      DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。
      DatagramSocket(int port, InetAddress laddr) 创建一个数据报套接字,绑定到指定的本地地址。
      DatagramSocket(SocketAddress bindaddr) 创建一个数据报套接字,绑定到指定的本地套接字地址。
package com.mao.lesson03;

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

/**
 * @ClassName UdpServerDemo01
 * @Description TODO
 * @Author HUCHAO
 * @Date 2021/11/2 18:08
 * @Version 1.0
 **/

// 还是要等待客户端的链接!
public class UdpServerDemo01 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 开放端口
        final DatagramSocket datagramSocket = new DatagramSocket(9090);
        // 接受数据包
        final byte[] bytes = new byte[1024];
        final DatagramPacket datagramPacket = new DatagramPacket(bytes,0,bytes.length);

        datagramSocket.receive(datagramPacket); // 阻塞接收
        System.out.println(datagramPacket.getAddress().getHostAddress());
        System.out.println(new String(datagramPacket.getData(),0,datagramPacket.getLength()));

        // 关闭连接
        datagramSocket.close();
    }
}

咨询

循环发送消息

发送方:

package com.mao.chat;

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

/**
 * @ClassName UdpSenderDemo01
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/3 20:25
 * @Version 1.0
 **/
public class UdpSenderDemo01 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        final DatagramSocket datagramSocket = new DatagramSocket(8888);

        // 准备数据,控制台读取System.in
        final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        while (true){
    
    

            final String s = bufferedReader.readLine();
            final byte[] bytes = s.getBytes();
            final DatagramPacket datagramPacket = new DatagramPacket(bytes,0,bytes.length,new InetSocketAddress("localhost",6677));
            datagramSocket.send(datagramPacket);

            if (s.equals("bye")){
    
    
                break;
            }
        }
        datagramSocket.close();
    }
}

/*
1、BuffReader读取控制台字符时用readLine来读取一行
 */

接收方:(错误接收)

package com.mao.chat;

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

/**
 * @ClassName UdpReceiveDemo01
 * @Description TODO
 * @Author Hasee
 * @Date 2021/11/3 20:26
 * @Version 1.0
 **/
public class UdpReceiveDemo01 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        final DatagramSocket datagramSocket = new DatagramSocket(6677);

        while (true){
    
    
            // 准备接收包裹
            final byte[] bytes = new byte[1024];
            final DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
            datagramSocket.receive(datagramPacket); // 阻塞式接收包裹

            // 断开连接
            final byte[] data = datagramPacket.getData();
            final String s = new String(data, 0, data.length);
            System.out.println(s);
            if (s.startsWith("bye")){
    
    
                break;
            }
        }
        datagramSocket.close();
    }
}

/*
1、创建对象 while之外
2、接收包裹.getData
 */

注意!!! 大坑出现 !!!! 一定要看!!!!!!
在这里插入图片描述
详见下面这篇博客

DatagramPacket.getData()与DatagramPacket.getLength()的误区

DatagramPacket.getData()与DatagramPacket.getLength()的误区

正确修改

接收方:(正确接收:方法一)

// 断开连接
            final byte[] data = datagramPacket.getData();
            final int length = datagramPacket.getLength();
            final String s = new String(data, 0, length);
            System.out.println(s);
            if (s.startsWith("bye")){
    
    
                break;
            }

接收方:(正确接收:方法二)

// 断开连接
            final byte[] data = datagramPacket.getData();
            final String s = new String(data, 0, data.length);
            System.out.println(s.trim());	// 不加trim会出现白框
            if (s.startsWith("bye")){
    
    		// 使用equals会导致 接收端即使收到 bye 也无法退出
                break;
            }

在这里插入图片描述

  • 相关API

    • trim

      public String trim()
      

      返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。

      如果此String对象表示一个空字符序列,或由该代表字符序列的第一个和最后一个字符String对象都具有代码大于'\u0020' (空格字符),则此参考String被返回对象。

      否则,如果字符串中没有字符大于'\u0020'的字符,则返回一个表示空字符串的String对象。

      否则,令k为代码大于’\u0020’的字符串中第一个字符的'\u0020' ,并且m为代码大于'\u0020'的字符串中最后一个字符的'\u0020' 。 将返回一个String对象,表示该字符串的子字符串,以索引k处的字符开头,以索引m为止的字符结束,结果为this.substring(k, m + 1)

      此方法可用于从字符串的开始和结尾修剪空格(如上定义)。

      • 结果

        一个字符串,其值是此字符串,除去任何前导和尾随空格,或者如果该字符串没有前导或尾随的空格,则为该字符串。

  • startsWith

    public boolean startsWith(String prefix)
    

    测试此字符串是否以指定的前缀开头。

    • 参数

      prefix - 前缀。

    • 结果

      true如果由参数表示的字符序列是由该字符串表示的字符序列的前缀; false否则。 还需要注意的是true如果参数为空字符串或等于该将被返回String如由所确定的对象equals(Object)方法。

    • 从以下版本开始:

      1. 0

在线咨询:两个人都可以发送

发送端线程:

package com.mao.chat;

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;

/**
 * @ClassName Talk
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/3 21:03
 * @Version 1.0
 **/
public class TalkSend implements Runnable{
    
    
    DatagramSocket datagramSocket = null;
    DatagramPacket datagramPacket = null;
    BufferedReader bufferedReader = null;
    String s;
    private int fromPort;
    private String toIP;
    private int toport;
    
    // 建立连接
    public TalkSend(int fromPort, String toIP, int toport) {
    
    
        this.fromPort = fromPort;
        this.toIP = toIP;
        this.toport = toport;

        try {
    
    
            datagramSocket = new DatagramSocket(this.fromPort);
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        } catch (SocketException e) {
    
    
            e.printStackTrace();
        }
    }

    // 发送消息
    @Override
    public void run() {
    
    
        while (true){
    
    
            try {
    
    
                s = bufferedReader.readLine();      // 读取消息
                final byte[] bytes = s.getBytes();  // 发送内容
                datagramPacket = new DatagramPacket(bytes,0,bytes.length,new InetSocketAddress(this.toIP,this.toport)); // 发送包
                datagramSocket.send(datagramPacket);

                if (s.equals("bye")){
    
    
                    break;
                }
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        datagramSocket.close();
    }
}

/*今日忘记
1、IP:String
2、DatagramSocket(端口)
2、BuffReader(输入流);
3、发送要把String变成数组
 */

接收端线程:

package com.mao.chat;

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

/**
 * @ClassName TalkReceive
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/3 21:21
 * @Version 1.0
 **/
public class TalkReceive implements Runnable {
    
    
    DatagramSocket datagramSocket =null;
    private int port;
    private String msgfrom;

    public TalkReceive(int port,String msgfrom) {
    
    
        this.port = port;
        this.msgfrom=msgfrom;
        try {
    
    
            datagramSocket = new DatagramSocket(port);
        } catch (SocketException e) {
    
    
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            try {
    
    
                // 准备接收包裹
                final byte[] bytes = new byte[1024];
                final DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
                datagramSocket.receive(datagramPacket); // 阻塞式接收包裹

                // 断开连接
                final byte[] data = datagramPacket.getData();
                final String s = new String(data, 0, data.length);
                System.out.println(msgfrom+": "+s.trim());
                if (s.startsWith("bye")){
    
    
                    break;
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        datagramSocket.close();
    }
}

//重新定义数组

学生端:

package com.mao.chat;

/**
 * @ClassName Talk
 * @Description TODO
 * @Author HuChao
 * @Date 2021/11/3 21:30
 * @Version 1.0
 **/
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.mao.chat;

/**
 * @ClassName TalkTeacher
 * @Description TODO
 * @Author HuChao
 * @Date 2021/11/3 21:33
 * @Version 1.0
 **/
public class TalkTeacher {
    
    
    public static void main(String[] args) {
    
    
        // 静态代理
        new Thread(new TalkSend(5555,"localhost",8888)).start();
        new Thread(new TalkReceive(9999,"学生")).start();
    }

}

聊天效果
在这里插入图片描述在这里插入图片描述

1.8、URL

https://www.baidu.com/

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

DNS域名解析 WWW.baidu

package com.mao.lesson04;

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

/**
 * @ClassName URLDemo014
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/3 22:19
 * @Version 1.0
 **/
public class URLDemo01 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        URL url=new URL("https://localhost:8080/helloworld/index.jsp?username=huchao&password=123");
        System.out.println(url.getProtocol());  // 协议       https
        System.out.println(url.getHost());      // 主机ip     localhost
        System.out.println(url.getPort());      // 端口       8080
        System.out.println(url.getPath());      // 文件       /helloworld/index.jsp
        System.out.println(url.getFile());      // 全路径      /helloworld/index.jsp?username=huchao&password=123
        System.out.println(url.getQuery());     // 参数       username=huchao&password=123
    }
}

Java爬虫----爬取网易云音乐

API

  public final class URL
  extends Object
  implements Serializable

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

一般来说,URL可以分为几个部分。 请考虑以下示例:

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

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

URL可以选择指定一个“端口”,它是在远程主机上进行TCP连接的端口号。 如果未指定端口,则使用协议的默认端口。 例如, http的默认端口是80 。 另一个端口可以指定为:

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

的语法URL由下式定义RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax ,通过修正RFC 2732: Format for Literal IPv6 Addresses in URLs 。 文字IPv6地址格式也支持scope_ids。 描述了scope_ids的语法和用法here

URL可能附加了一个“片段”,也称为“ref”或“reference”。 片段由尖锐的符号字符“#”表示,后跟更多的字符。 例如,

     http://java.sun.com/index.html#chapter1
 

该片段在技术上不是URL的一部分。 相反,它表示在检索到指定的资源之后,应用程序对附有标签chapter1的文档的该部分chapter1 。 标签的含义是资源特定的。

一个应用程序还可以指定一个“相对URL”,它只包含相对于另一个URL访问资源的足够的信息。 HTML页面中经常使用相对URL。 例如,如果URL的内容:

     http://java.sun.com/index.html
 

在其中包含相对URL:

     FAQ.html
 

这将是一个速记:

     http://java.sun.com/FAQ.html
 

相对URL不需要指定URL的所有组件。 如果协议,主机名或端口号丢失,该值将从完全指定的URL继承。 必须指定文件组件。 可选片段不被继承。

URL类本身不会根据RFC2396中定义的转义机制对任何URL组件进行编码或解码。 来电者有责任编码任何需要在调用URL之前进行转义的字段,并对从URL返回的任何转义字段进行解码。 此外,由于URL不具有URL转义的知识,因此不能识别同一URL的编码或解码形式之间的等同性。 例如,两个URL:

  http://foo.com/hello world/ and http://foo.com/hello%20world 

将被视为不相等。

注意,在某些情况下, URI类确实执行其组件字段的转义。 管理URL编码和解码的推荐方法是使用URI ,并使用toURI()URI.toURL()在这两个类之间进行转换。

也可以使用URLEncoderURLDecoder类,但仅适用于与RFC2396中定义的编码方案不同的HTML表单编码。

  • 从以下版本开始:

    JDK1.0
    在这里插入图片描述

package com.mao.lesson04;

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

/**
 * @ClassName UrlDown
 * @Description TODO
 * @Author HuChao
 * @Date 2021/11/3 22:31
 * @Version 1.0
 **/
public class UrlDown {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 1.下载地址
        final URL url = new URL("https://m10.music.126.net/20211103230417/8ff1962e83198f8e9dcdd3f4684d8bea/yyaac/obj/wonDkMOGw6XDiTHCmMOi/4064595123/29fd/0611/0ab0/18713e49d7771e90ca4301ac93a0fa96.m4a");
        // 2.连接到这个资源 HTTP
        final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        final InputStream inputStream = urlConnection.getInputStream();

        final FileOutputStream fileOutputStream = new FileOutputStream("黄昏.m4a");

        final byte[] bytes = new byte[1024];
        int length;
        while ((length=inputStream.read(bytes))!=-1){
    
    
            fileOutputStream.write(bytes,0,length); // 写入这个数据
        }
        fileOutputStream.close();
        inputStream.close();
        urlConnection.connect();

    }
}

成功爬取音乐数据
在这里插入图片描述

总结:

  • 本文以Java网络编程为主线进行学习
  • 结合 计算机网络、Tcp、Udp、IO、多线程由浅入深,进行综合性案例实现
  • 另附 踩大坑笔记,及详细解决办法
  • 另有 在线咨询聊天室,和简易Java爬虫实现过程
  • 总体较为基础,但非常实用,建议收藏
  • 创作不易,希望支持

更多相关文章点这里

【Java全栈】Java全栈学习路线及项目全资料总结【JavaSE+Web基础+大前端进阶+SSM+微服务+Linux+JavaEE】
https://blog.csdn.net/qq_45696377/article/details/110575362

猜你喜欢

转载自blog.csdn.net/qq_45696377/article/details/121151072