Bootstrap

网络编程笔记

概述

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备, 通过通信线路连接起来, 

在网络操作系统, 网络管理软件及网络通信协议的管理和协调下, 实现资源共享和信息传递的计算机系统.

网络编程的目的:

传播交流信息, 进行数据交换, 通信

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

  1. 如何准确的定位网络上的一台主机? 通过ip: 端口 , 定位到计算机上的某个资源
  2. 找到这个主机后. 如何传输数据呢?

javaWeb: 网页编程 B/S
网络编程: TCP/IP C/S

网络通信的要素

image.png

IP

InetAddress类

java网络的类。通过该类通过得到ip地址和主机名一些信息


public class TestInetAddress {
    public static void main(String[] args) {
        //InetAddress类的基本使用
        try {
            //查询给定主机名称的主机的 IP 地址
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            System.out.println(inetAddress);    // /127.0.0.1
            InetAddress inetAddress1 = InetAddress.getByName("localhost");
            System.out.println(inetAddress1);   // localhost/127.0.0.1
            //检索主机的名称,并返回本地主机的地址
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println(localHost);     // DESKTOP-VFNTELP/192.168.3.53

            //查询百度网站ip地址
            InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
            System.out.println(inetAddress2);  //www.baidu.com/180.101.50.188

            //InetAddress类常用方法
            //获取规范主机名 180.101.50.188
            System.out.println(inetAddress2.getCanonicalHostName());
            //获取IP地址 180.101.50.188
            System.out.println(inetAddress2.getHostAddress());
            //获取域名/主机名  www.baidu.com
            System.out.println(inetAddress2.getHostName());
            //获取IP地址的字节数组  [B@6d311334
            System.out.println(inetAddress2.getAddress());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

端口

**定义: **端口表示计算机上一个程序的进程 , 不同的进程有不同的端口号! 用来区分软件

端口被规定 0 - 65535
端口被分为TCP , UDP : 每个都有65535 ->单个协议下, 端口号不要冲突

**端口分类: **

公有端口: 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 "端口" #查看指定端口
tasklist|findstr "端口"  #查看指定端口的进程

InetSocketAddress类

封装了ip,端口号, 解析主机名

public class TestInetSocketAddress {
    public static void main(String[] args) {
        //InetSocketAddress类的基本使用
        InetSocketAddress socketAdress1 = new InetSocketAddress("127.0.0.1", 8080);
        InetSocketAddress socketAdress = new InetSocketAddress("localhost", 8080);
        System.out.println(socketAdress); //localhost/127.0.0.1:8080
        System.out.println(socketAdress1); // /127.0.0.1:8080

        //常用方法
        System.out.println(socketAdress.getAddress()); //获取 InetAddress -> localhost/127.0.0.1
        System.out.println(socketAdress.getHostName()); //获取主机名 -> localhost
        System.out.println(socketAdress.getPort()); //获取端口号  -> 8080
        System.out.println(socketAdress.getHostString()); //获取主机名的字符串表示
    }
}

通信协议

**定义: **
就是一个约定, 比如我们中国是说普通话

网络通信协议:
速率, 传输码率, 代码结构, 传输控制
重要协议:
TCP: 用户传输协议
UDP: 用户数据报协议
出名的协议:
TCP/IP协议簇 (是一组协议)

image.png

TCP UDP对比

TCP:

如: 打电话 (需要连接确认后,才可发送)
优点: 连接稳定
缺点: 传输完成, 释放连接, 效率低
客户端, 服务端

UDP:

如: 发送短信 (不需要连接确认就可发送给指定方)
缺点: 不连接, 不稳定
客户端,服务端没有明确的界限 , 像导弹一样, 不管对面有没有准备好, 都可以发送(轰炸对方)

三次握手, 四次挥手

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

四次挥手 (模拟分手场景)
A:我要走了!					 ->发送连接断开信号
B:你真的要走了吗?     ->确认是否断开
B:你真的真的要走了吗? ->再次确认是否断开
A:我真的要走了!       -> 连接断开

TCP(Socket通信)

InetAddress通过该类通过得到ip地址和主机名一些信息
public static InetAddress getByName(String host)根据主机获得对应的InetAddress对象,参数host可以是IP地址或域名
public static InetAddress getLocalHost()获取本机对应的InetAddress对象
public String getHostName()获得该InetAddress对象的主机名称
ServerSocket使用ServerSocket类以获取一个端口,并且侦听客户端请求
public ServerSocket(int port)创建绑定到特定端口的服务器套接字
public int getLocalPort()返回此套接字在其上侦听的端口
public Socket accept()侦听并接受到此套接字的连接
SocketSocket类代表客户端和服务器都用来互相沟通的套接字
public Socket(InetAddress host, int port) throws IOException创建一个流套接字并将其连接到指定IP地址的指定端口号
public InetAddress getInetAddress()返回套接字连接的地址。
public int getPort()返回此套接字连接到的远程端口。
public int getLocalPort()返回此套接字绑定到的本地端口。
public InputStream getInputStream() throws IOException返回此套接字的输入流。
public OutputStream getOutputStream() throws IOException返回此套接字的输出流。
public void close() throws IOException关闭此套接字。

通信

客户端连接服务端后发送资源后关闭流
public class TCPClientDemo1 {
    //客户端发送资源
    public static void main(String[] args) {
        Socket socket = null;
        OutputStream clientMessage = null;
        try {
            //1. 知道服务器的IP地址和端口号
            InetAddress serverIp = InetAddress.getByName("127.0.0.1");
            int port = 9999;
            //2.创建Socket对象,指定服务器的IP地址和端口号
            socket = new Socket(serverIp, port);
            //3.发送数据
            clientMessage = socket.getOutputStream();
            clientMessage.write("这里是服务端, 你接收到了吗?".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //4.关闭连接
            if (clientMessage != null) {
                try {
                    clientMessage.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
服务端接收信息
public class TCPServerDemo1 {
    //服务端
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket clientAccept = null;
        InputStream inputStream = null;
        ByteArrayOutputStream readClientMes = null;
        try {
            //1. 创建ServerSocket对象,监听端口
            serverSocket = new ServerSocket(9999);
            //2. 接收客户端的连接请求
            clientAccept = serverSocket.accept();
            //3. 读取客户端发送的消息
            inputStream = clientAccept.getInputStream();
            //4.用管道符处理接收客户端发送消息
            readClientMes = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(buffer)) != -1) {
                readClientMes.write(buffer, 0, len);
            }
            System.out.println("客户端发送的消息为:" + readClientMes.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5. 关闭资源
            if (serverSocket != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (clientAccept != null) {
                try {
                    clientAccept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (readClientMes != null) {
                try {
                    readClientMes.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

TCP实现文件上传 (使用throw接收异常)

客户端

(发送文件后, 通知服务器, 并确定服务器接收完毕后, 断开连接)

public class TcpclientDemo2 {
    //TCP 创建发送文件(客户端)
    public static void main(String[] args) throws Exception{
        //1.创建socket, 进行连接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
        //2.创建输出流
        OutputStream os = socket.getOutputStream();
        //3.读取文件
        FileInputStream fileInputStream = new FileInputStream(new File("D:\\WorkSpace\\swagger\\赶海攻略.docx"));
        //4.写出文件
        byte[] bytes = new byte[1024];
        int len = 0;
        while((len = fileInputStream.read(bytes)) != -1){
            os.write(bytes, 0, len);
        }

        //通知服务器, 我这边发送完毕了
        socket.shutdownOutput();
        //确定服务器接收完毕后, 断开连接
        InputStream inputStream = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bytes1 = new byte[1024];
        int len1 = 0;
        while((len1 = inputStream.read(bytes1)) != -1){
            baos.write(bytes1, 0, len1);
        }
        System.out.println(baos);

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

服务端

** (接收客户端发送文件后, 告知客户端可以断开连接了)**

public class TcpServerDemo2 {
    //Tcp实现文件上传(服务段)
    public static void main(String[] args) throws Exception {
            //1.创建ServerSocket对象,监听端口
            ServerSocket serverSocket = new ServerSocket(9000);
            //2.创建Socket对象,等待客户端连接
            Socket socket = serverSocket.accept();
            //3.通过输入流接收客户端上传的文件
            InputStream inputStream = socket.getInputStream();
            //4.通过管道流和输出流将文件进行写入
            FileOutputStream fileOutputStream = new FileOutputStream("D:\\WorkSpace\\swagger\\攻略.docx");
            byte[] buffer = new byte[1024];
            int len = 0;
            while((len = inputStream.read(buffer))!= -1){
                fileOutputStream.write(buffer, 0 , len);
            }
            //通知客户端我接收完毕了
            socket.getOutputStream().write("服务端接收完毕, 客户端可以断开了".getBytes());

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

UDP通信

UDP发送消息, 需要对方地址,不需要对方连接响应即可发送
(如发送快递, 告诉你发了一个快递即可, 如果对面的人不存在, 那快递也可以发送过去,不能接收) ,
tcp是需要找服务器发送消息, 需要连接服务器,给出地址不然报错, 如你存在, 有快递信息可以直接收到快递 ,如果不存在, 发送出去也没人接收 )

DatagramSocket 类

  • DatagramSocket 类用于表示发送和接收数据报包的套接字。数据报包套接字是包投递服务的发送或接收点。每个在数据报包套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
    | 构造方法 | |
    | — | — |
    | DatagramSocket() | 绑定到本地地址和一个随机的端口号 |
    | DatagramSocket(int port) | 绑定本地地址和一个特定的端口号 |
    | DatagramSocket(int port, InetAddress iad) | 绑定到特定的端口号及指定地址 |
    | DatagramSocket(SocketAddress sad) | 绑定指定地址和随机端口号 |
基本方法
close()关闭套接字
recevie(DatagramPacket dp)接受数据报
send(DatagramPacket dp)发送数据报

DatagramPacket 类

  • java.net 包中的 DatagramPacket 类用来表示数据报包,数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
    | 接受类型 | |
    | — | — |
    | DatagramPacket(byte[] buf, int offset, int length) | 构造一个 DatagramPacket用于接收长度的分组 length ,指定偏移到缓冲器中 |
发送类型
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)构造用于发送长度的分组数据报包 length具有偏移offset 指定主机上到指定的端口号
DatagramPacket(byte[] buf, int length, SocketAddress address)将length长的buf数据发送到指定的套接字地址处

UDP消息传递

客户端不连接服务器, 直接发送消息
public class UdpClientDemo1 {
    //不需要连接服务器
    public static void main(String[] args) throws Exception {
        //1.建立一个socket
        DatagramSocket socket = new DatagramSocket();
        //2.建个包
        String msg = "你好, 服务器";
        //3. 发送给谁
        InetAddress localhost = InetAddress.getByName("localhost");
        int port = 8888;
        //4.添加(数据, 数据的长度起始,要发送给谁)
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.length(), localhost, port);
        //4.发送包
        socket.send(packet);
        //5.关闭socket
        socket.close();
    }
}

服务端还需需要等待客户端连接, 接收到服务端消息
public class UdpServerDemo01 {
    public static void main(String[] args) throws Exception {
    //等待客户端连接
        //开放端口
        DatagramSocket socket = new DatagramSocket(8888);
        //接收数据包
        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        //阻塞接收消息
        socket.receive(packet);
        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(new String(packet.getData(), 0 , packet.getLength()));
        //关闭连接
        socket.close();
    }
}

UDP聊天实现

通过键盘录入System.in实现聊天互动
发送端:

public class UdpServer {
    public static void main(String[] args) throws Exception {
    //等待客户端连接
        //开放端口
        DatagramSocket socket = new DatagramSocket(8888);
        //准备数据, 控制台读取system.in
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        //循环发送.直到客户端发送bye后退出
        while (true){
            String data = reader.readLine();
            //接收数据包
            byte[] datas = data.getBytes();
            DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666));
            //发送消息
            socket.send(packet);
            if (data.equals("bye")){
                break;
            }
        }
       //关闭连接
        socket.close();
    }
}

接收端: (循环接收)
while死循环不停止的接收数据, 知道服务端发送bye后, 结束通信终止循环

public class UdpReceiveDemo01 {
    public static void main(String[] args) throws Exception {
        //开放端口
        DatagramSocket socket = new DatagramSocket(6666);
        //循环发送消息, 直到bye
        while (true) {
            byte[] bytes = new byte[1024];
            DatagramPacket packet = new DatagramPacket(bytes,0, bytes.length);
            socket.receive(packet); //阻塞接收,避免信息量过大
            //断开连接
            byte[] data = packet.getData();
            String receiveData = new String(data, 0, data.length);
            System.out.println(packet);

            if (receiveData.equals("bye")){
                break;
            }
        }
        socket.close();
    }
}

UDP通过多线程,实现两人都可以是发送和接收方

发送线程:

//发送消息端(Teacher端)
public class chatSendUdpThread implements Runnable {
    DatagramSocket socket ;
    BufferedReader reader ;
    DatagramPacket packet ;
    int port;
    int toPort;

    public chatSendUdpThread(int port , int toPort){
        this.port = port;
        this.toPort = toPort;
    }
    @Override
    public void run() {
        try {
            //绑定本机地址和随机端口号
            socket = new DatagramSocket();
            //获取输入内容
            reader = new BufferedReader(new InputStreamReader(System.in));
            //读取数据
            while(true) {
                String data = reader.readLine();
                byte[] datas = data.getBytes();
                //接收数据
                packet = new DatagramPacket(datas, 0, datas.length, InetAddress.getByName("127.0.0.1"), toPort);
                //发送数据
                socket.send(packet);
                //遇到bye停止
                if ("bye".equals(data)) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭端口
            if (reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null){
                socket.close();
            }
        }
    }
}

接收线程

//接收端(Student端)
public class chatReceiveUdpThread implements Runnable {
    DatagramSocket socket ;
    DatagramPacket packet ;
    int port;

    public chatReceiveUdpThread(int port) {
        this.port = port;
    }

    @Override
    public void run() {
        try {
        //开放端口
        socket = new DatagramSocket();
        //接收数据
        while (true) {
            byte[] buffer = new byte[1024];
             packet = new DatagramPacket(buffer, 0, buffer.length);
            //阻塞接收
            socket.receive(packet);
            //读取数据
            byte[] data = packet.getData();
            String str = new String(data, 0, data.length);
            System.out.println("port:" + port + "消息:" + str);
            if (str.equals("bye")) {
                break;
            }
        }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
            if (socket!= null) {
                socket.close();
            }
        }
    }
}

开启两个线程(知道对方接收的端口, 发送的端口无所谓)
教师端:

public class TeacherSend {
    public static void main(String[] args) {
        new Thread(new chatReceiveUdpThread(8888)).start();
        new Thread(new chatSendUdpThread(8888, 9999)).start();
    }
}

学生端:

public class StudentSend {
    public static void main(String[] args) {
        new Thread(new chatReceiveUdpThread(8888)).start();
        new Thread(new chatSendUdpThread(9999,8888)).start();
    }
}

URL

统一资源定位符: 定位资源的, 定位互联网上的某一个资源
DNS域名解析 如将www.baidu.com 解析为ip地址

协议: //ip地址 : 端口号 /项目名/资源
**url由最少五部分组成(只能多,不能少) **

参数作用

url.getprotocol()协议
url.getHost()主机ip
url.getPort()端口
url.getFile()全路径
url.getQuery()参数

爬取资源

public class UrlDown {
    public static void main(String[] args) throws Exception {
        //下载地址
        URL url = new URL("https://i0.hdslb.com/bfs/banner/8bb2303e3b3338d4a056e99581f811f268d57444.png");
        //连接到这个资源  HTTP
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        InputStream inputStream = urlConnection.getInputStream();
        FileOutputStream fos = new FileOutputStream("1.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();//断开连接
    }
}
;