网络编程入门笔记

网络编程入门笔记

狂神网络编程

基础的科普

概述

举例

打电话——TCP

发短信——UDP

网络编程的目的

传播交流信息、数据交换、通信

网络通信的要素

  • 通信双方的地址,包括IP地址、端口号port
  • 网络通信的协议

tcp/ip参考模型

OSI七层网络模型是理想化的参考模型,并没有实际应用,实际是TCP/IP参考模型。

tpc/ip参考模型.png

IP地址

IP地址:InetAddress

  • IP用于唯一定位一台网络上的计算机
  • 本机:127.0.0.1 或者 localhost
  • IP地址的分类
    • IPv4 : 32位,4个字节组成,每个字节范围为0——255的无符号整数,总量有42亿,即2的32次方。分布不均,30亿在北美,亚洲4亿,2011年就用尽了。
    • IPv6 :128位,8个无符号整数,每个整数16位。
      • 示例:2001:DB8:0:23:8:800:200C:417A
    • 公网(互联网)/私网IP(局域网)
      • ABCD类地址
      • 192.168.X.X 这类地址是专门给组织内部使用的,内网地址
  • 域名:用于记忆IP地址、集群时的多个IP地址与域名的映射

扩展

IPv6表示法

IPv6的地址长度为128位,是IPv4地址长度的4倍。于是IPv4点分十进制格式不再适用,采用十六进制表示。IPv6有3种表示方法。

一、冒分十六进制表示法
  格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16b,以十六进制表示,例如:
  ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
  这种表示法中,每个X的前导0是可以省略的,例如:
  2001:0DB8:0000:0023:0008:0800:200C:417A→ 2001:DB8:0:23:8:800:200C:417A

二、0位压缩表示法
  在某些情况下,一个IPv6地址中间可能包含很长的一段0,可以把连续的一段0压缩为“::”。但为保证地址解析的唯一性,地址中”::”只能出现一次,例如:
  FF01:0:0:0:0:0:0:1101 → FF01::1101
  0:0:0:0:0:0:0:1 → ::1
  0:0:0:0:0:0:0:0 → ::

三、内嵌IPv4地址表示法
  为了实现IPv4-IPv6互通,IPv4地址会嵌入IPv6地址中,此时地址常表示为:X:X:X:X:X:X:d.d.d.d,前96b采用冒分十六进制表示,而最后32b地址则使用IPv4的点分十进制表示,例如::192.168.0.1与::FFFF:192.168.0.1就是两个典型的例子,注意在前96b中,压缩0位的方法依旧适用

IP地址中A类、B类、C类地址的区别

IP地址表示方法不同:

一个A类IP地址是指, 在IP地址的四段号码中,第一段号码为网络号码,剩下的三段号码为本地计算机的号码。如果用二进制表示IP地址的话,A类IP地址就由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”。A类IP地址中网络的标识长度为8位,主机标识的长度为24位。

一个B类IP地址是指,在IP地址的四段号码中,前两段号码为网络号码。如果用二进制表示IP地址的话,B类IP地址就由2字节的网络地址和2字节主机地址组成,网络地址的最高位必须是“10”。B类IP地址中网络的标识长度为16位,主机标识的长度为16位。

一个C类IP地址是指,在IP地址的四段号码中,前三段号码为网络号码,剩下的一段号码为本地计算机的号码。如果用二进制表示IP地址的话,C类IP地址就由3字节的网络地址和1字节主机地址组成,网络地址的最高位必须是“110”。C类IP地址中网络的标识长度为24位,主机标识的长度为8位。

2、IP地址范围不同:

A类IP地址 地址范围从1.0.0.1到127.255.255.254 (二进制表示为:00000001 00000000 00000000 00000001 - 01111111 11111111 11111111 11111110)。最后一个是广播地址。

B类IP地址地址范围从128.0.0.1-191.255.255.254 (二进制表示为:10000000 00000000 00000000 00000001-10111111 11111111 11111111 11111110)。 最后一个是广播地址。

C类IP地址范围从192.0.0.1-223.255.255.254 (二进制表示为: 11000000 00000000 00000000 00000001 - 11011111 11111111 11111111 11111110)。最后一个是广播地址。

3、子网掩码不同:

A类IP地址的子网掩码为255.0.0.0

B类IP地址的子网掩码为255.255.0.0

C类IP地址的子网掩码为255.255.255.0

4、适用范围不同:

A类适用的类型为大型网络,A类网络地址数量较少,有126个网络,每个网络支持的最大主机数为256的3次方-2=16777214台;

B类适用的类型为中型网络,B类网络地址数量适中,有16384个网络,每个网络支持的最大主机数为256的2次方-2=65534台;

C类适用的类型为小型网络,C类网络地址数量较多,有209万余个网络,适用于小规模的局域网络,每个网络支持的最大主机数为256的1次方-2=254台。

ABCD类地址记忆

A:0-127 中间128位

B:128-191 中间64位

C:192-223 中间32位

D:224-239 中间16位

E:240-255 中间16位

记的话就从0开始一直加128,再加它的二分之一,一直这样下去,即可算出。

端口Port

我们如果把IP地址比喻成一栋楼,那端口Port就是每套房子的名字,我们的主机上的程序相当于一套房子,而我们的每个程序运行时都要分配一个端口号。

  • 不同的进程有不同的端口号,用来区分软件的。
  • 规定范围 0~65535
  • 不同的协议TCPUDP都有对应0~65535的端口号范围,因此总的可以使用的端口号有65535 * 2 个,不同协议的端口号可以相同,而相同协议下比如TCP只能有一个80端口被一个进程使用,不能有冲突。
  • 端口分类
    • 公有端口0~1023
      • HTTP: 80 (默认端口)
      • HTTPS: 443
      • FTP: 21
      • SSH: 22
      • Telent :23
    • 程序注册端口范围:1024~49151 ,分配给用户或者程序的端口
      • Tomcat:8080
      • MySQL: 3306
      • Oracle:1521
    • 私有、动态端口范围:49152~65535

简单命令

#查看所有端口列表
netstat -ano
#查看指定端口或字符串对应的端口列表。 |表示管道,过滤的意思,会先执行过滤再查找
netstat -ano|findstr "8080" 
#查看指定端口或者程序的进程
tasklist|findstr "chrome"
tasklist|findstr "1736"

代码示例

public class INetAddressTest {
    public static void main(String[] args) throws UnknownHostException {
        //查询本机地址的3种方式
        ///127.0.0.1
        System.out.println(InetAddress.getByName("127.0.0.1"));
        //DESKTOP-G590BHK/172.16.57.150 电脑计算机名加主机
        System.out.println(InetAddress.getLocalHost());

        InetAddress localhost = InetAddress.getByName("localhost");
//        System.out.println(localhost.getAddress());//返回2进制数组地址, byte[]
        //localhost/127.0.0.1
        System.out.println(localhost);
        //activate.navicat.com 返回典范,标准的主机名,因为在本机电脑hosts配置了 127.0.0.1       activate.navicat.com
//        System.out.println(localhost.getCanonicalHostName());
        //localhost
        System.out.println(localhost.getHostName());
        System.out.println(localhost.getHostAddress());
        System.out.println("-----------------------------------");
        InetAddress qq = InetAddress.getByName("www.qq.com");
        System.out.println(qq);
        System.out.println(qq.getCanonicalHostName());
        System.out.println(qq.getHostName());
        System.out.println(qq.getHostAddress());
        System.out.println("----------------port-------------------");

        //IP端口对象
        InetSocketAddress socketAddress = new InetSocketAddress("localhost", 8080);
        System.out.println(socketAddress.getAddress());
        System.out.println(socketAddress.getHostName());
        System.out.println(socketAddress.getPort());
    }
    /**
     * 这两个类作用不大,只是了解
     * /127.0.0.1
     * DESKTOP-G590BHK/172.16.57.150
     * localhost/127.0.0.1
     * localhost
     * 127.0.0.1
     * -----------------------------------
     * www.qq.com/121.14.77.201
     * www.qq.com
     * www.qq.com
     * 121.14.77.201
     * ----------------port-------------------
     * localhost/127.0.0.1
     * localhost
     * 8080
     */
}

通信协议

协议:就是约定,就像我们约定沟通用普通话。

因为通信协议的复杂,我们就大事化小,进行分层处理。

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

TCP:传输控制协议,Transmission Control Protocol

UDP:用户数据报协议,User Datagram Protocol

比较常用出名的便是TCP/IP协议。

tcp/ip.png

TCP与UDP的对比

TCP

  • 打电话
  • 建立连接,稳定
  • 三次握手、四次挥手
  • 有客户端、服务端的概念
  • 传输完成,释放连接,效率低

关于三次握手和四次挥手,面试官想听到怎样的回答

UDP

  • 发短信
  • 不建立连接,不稳定
  • 没有客户端、服务端的明确界限
  • 不管有没有准备好都可以发送信息
  • DDOS饱和暴力攻击就是UDP协议形式

TCP通信步骤

客户端

  • 连接服务器Socket
  • 发送消息

服务端

  • 建立服务端口ServerSocket
  • 等待用户连接 serverSocket.accept()
  • 接受用户消息

代码示例

TCP协议下的通讯
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1、客户端知道服务器的地址和端口号,建立连接。Socket是客户端套接字
        try (Socket socket = new Socket("localhost", 9999)) {
            try (OutputStream outputStream = socket.getOutputStream()) {
                /* Scanner scanner = new Scanner(System.in);
                System.out.println("!");
                //服务端无法读取到所以数据,只能读取最后一部分,只要scanner不断开,也就是客户端不结束,服务端的读取流就一直阻塞着
               while (scanner.hasNext()) {
                    if ("exit".equalsIgnoreCase(scanner.next())) {
                        scanner.close();
                        System.out.println("客户端退出聊天室!");
                        break;
                    }
                    //发送消息IO流
                    outputStream.write(scanner.next().getBytes());*/
                outputStream.write("客户端可以发送信息给智能小白了".getBytes());
//                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //服务端通过ip地址和端口号创建一个服务端套接字 ServerSocket
        try (ServerSocket serverSocket = new ServerSocket(9999)) {
            //等待客户端连接
            try (Socket socket = serverSocket.accept()) {
                //获取客户端的读字节流,读取客户端的消息
                try (InputStream is = socket.getInputStream()) {
                    //管道流
                    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                        int len = 0;
                        byte[] buffer = new byte[1024];
                        while ((len = is.read(buffer)) > 0) {
                            outputStream.write(buffer, 0, len);
                        }
                        System.out.println(outputStream.toString());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 客户端可以发送信息给智能小白了
     */
}
TCP协议下上传文件
public class TCPUploadClient {
    public static void main(String[] args) throws Exception {
        //1创建Socket连接
        Socket socket = new Socket("127.0.0.1", 8090);
        //2、创建输出流
        OutputStream os = socket.getOutputStream();

        //没有写file的路径名时默认是相对路径,即项目根目录下的位置,springboot-demo下
        //读取文件
        FileInputStream is = new FileInputStream(new File("asfasf.jpg"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) > 0) {
            os.write(buffer, 0, len);
        }

        //通知服务器我结束发送消息了,结束输出流,这样服务器那边的就不会阻塞等待了
        socket.shutdownOutput();

        InputStream inputStream = socket.getInputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, len);
        }
        System.out.println(outputStream.toString());

        //先开后关
        inputStream.close();
        outputStream.close();
        is.close();
        os.close();
        socket.close();
    }
    //文件传输完成了!再见!
}
public class TCPUploadServer {
    public static void main(String[] args) throws Exception {
        //创建服务ServerSocket
        ServerSocket serverSocket = new ServerSocket(8090);
        //监听客户端
        Socket socket = serverSocket.accept();
        //获取输入流
        InputStream is = socket.getInputStream();
        //创建输出文件名及输出流
        FileOutputStream os = new FileOutputStream(new File("hello.jpg"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) > 0) {
            os.write(buffer, 0, len);
        }

        //通知客户端文件传输完成,即向客户端写消息
        //先拿到输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("文件传输完成了!再见!".getBytes());

        //关闭资源
        outputStream.close();
        os.close();
        is.close();
        socket.close();
        serverSocket.close();
    }
}

UDP

UDP的客户端Client可以直接发生消息给服务端或者其他人,不需要建立连接,因此就不需要要求服务端或其他人是在线的,也能发生成功;而TCP发生消息需要先建立连接,这时候如果连接的服务器不可用(比如没有启动起来),TCP的客户端启动时就会报错。如下:

Exception in thread "main" java.net.ConnectException: Connection refused: connect

但是,如果UDP接收方,比如服务器不可用的话,是接收不到数据的,所以理论上虽然不会报错,但是UDP服务器也是要先启动起来等待接受数据的。

代码示例

UDP协议下通讯聊天——发包进行聊天

首选创建发送消息和接受消息两个UDP工具类,然后创建两个用户对象,分别创建两条线程监听执行。

接受API类

public class UDPChatReceiver implements Runnable {

    private DatagramSocket datagramSocket;
    private int fromPort;
    private String userName;

    public UDPChatReceiver(int fromPort, String userName) {
        this.fromPort = fromPort;
        this.userName = userName;
        try {
            //1创建一个数据包套接字DatagramSocket,开放接收端口
            datagramSocket = new DatagramSocket(fromPort);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            while (true) {
                //准备容器接收数据
                byte[] buffer = new byte[1024];
                //2创建接收数据包,传入缓存容器
                DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
                //3阻塞接收数据
                datagramSocket.receive(packet);
                System.out.println(packet.getAddress());
                //4读取数据后将字节流转字符串即可
                String data = new String(packet.getData());
                System.out.println(userName + ":" + data);
                if ("exit".equalsIgnoreCase(data)) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5关闭流
            datagramSocket.close();
        }
    }
}

发送API类

public class UDPChatSender implements Runnable {

    //目标方的IP
    private String toIP;
    //目标方的端口
    private int toPort;
    //自己的端口
    private int fromPort;

    private DatagramSocket datagramSocket;

    private BufferedReader reader;

    public UDPChatSender(String toIP, int toPort, int fromPort) {
        this.toIP = toIP;
        this.toPort = toPort;
        this.fromPort = fromPort;
        try {
            //1创建一个数据包套接字DatagramSocket
            datagramSocket = new DatagramSocket(fromPort);
            reader = new BufferedReader(new InputStreamReader(System.in));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            while (true) {
                //读取控制台数据
                String str = reader.readLine();
                //2创建一个数据包用于发送 ,包含数据及发送的目标IP和端口
                DatagramPacket packet = new DatagramPacket(str.getBytes(), 0, str.getBytes().length, InetAddress.getByName(toIP), toPort);
                //3发送数据包
                datagramSocket.send(packet);
                if ("exit".equalsIgnoreCase(str)) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4关闭流
            datagramSocket.close();
        }
    }
}

用户对象


public class Customer {
    public static void main(String[] args) {
        new Thread(new UDPChatReceiver(9800,"销售")).start();
        new Thread(new UDPChatSender("localhost",9500,9601)).start();
    }
}

public class Sale {
    public static void main(String[] args) {
        //接收线程
        new Thread(new UDPChatReceiver(9500,"客户")).start();
        //发送线程
        new Thread(new UDPChatSender("localhost",9800,9600)).start();
    }
}

java执行class要注意带包名时要退回到包名根路径下再执行java 包名.文件名 才能执行文件

java执行class.png

Tomcat

执行文件.png

Tomcat日志乱码是因为Windows默认编码是GBK,更改conf目录下的logging.properties文件即可。

tomcat.png

URL

https://www.kugou.com/song/8wj752.html?share=1#hash=A46E805441A932ED0F5EF78DD229F688&album_id=1598950

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

DNS域名解析指把域名解析成IP地址,如 www.kugou.com ——》 117.27.241.66

URL格式

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

代码示例——url资源下载

网络爬虫的本质就是爬取各种资源文件的URL地址,然后创建连接,打开下载流下载到地址来,或者是爬取到数据来进行分析。

public class URLTest {
    public static void main(String[] args) throws Exception {
        URL url1 = new URL("https://www.bilibili.com/video/BV1LJ411z7vY?p=12");
        //需要注意的是host如果是域名则获取的主机还是域名,没有端口的话默认是-1
        System.out.println(url1.getProtocol());//协议
        System.out.println(url1.getHost());//主机
        System.out.println(url1.getPort());//端口
        System.out.println(url1.getPath());//文件
        System.out.println(url1.getFile());//全路径
        System.out.println(url1.getQuery());//参数
        /**
         * https
         * www.bilibili.com
         * -1
         * /video/BV1LJ411z7vY
         * /video/BV1LJ411z7vY?p=12
         * p=12
         * null
         */

        //创建一个url对象,通过这个url对象可以进行访问、资源下载、爬虫抓取数据等操作
        URL url = new URL("https://webfs.yun.kugou.com/202102241417/c9463283bf6cecf8eead45be1614a275/part/0/960125/KGTX/CLTX001/a46e805441a932ed0f5ef78dd229f688.mp3");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        InputStream is = connection.getInputStream();
        FileOutputStream os = new FileOutputStream(new File("hope.mp3"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) > 0) {
            os.write(buffer);
        }
        os.close();
        is.close();
        connection.disconnect();
    }
}
原文地址:https://www.cnblogs.com/castamere/p/14441426.html