Java高级技术——网络编程

什么是网络编程?

  • 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。

Java提供了哪些网络编程的解决方案

  • java.net.*包下提供了网络编程的解决方案!

基本的通信架构

  • 基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 )、BS架构(Browser浏览器/Server服务端)。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
无论是CS架构,还是BS架构的软件都必须依赖网络编程!

1. 网络通信三要素

1. IP地址

网络通信的关键三要素

在这里插入图片描述

在这里插入图片描述

IP地址

  • IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志。
  • IP地址有两种形式:IPv4、IPv6

IPv4

在这里插入图片描述

IPv6

  • IPv6:共128位,号称可以为地球每一粒沙子编号。
  • IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。
    在这里插入图片描述

IP域名

在这里插入图片描述

公网IP, 内网IP

  • 公网IP:是可以连接互联网的IP地址;内网IP:也叫局域网IP,只能组织机构内部使用。

  • 192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用。

特殊IP地址:

  • 127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机。

IP常用命令:

  • ipconfig:查看本机IP地址。
  • ping IP地址:检查网络是否连通。

InetAddress

  • 代表IP地址

InetAddress的常用方法如下

在这里插入图片描述

InetAddress ip = InetAddress.getLocalHost();
System.out.println(ip); // 获取本机IP,会以一个inetAddress的对象返回
System.out.println(ip.getHostName()); // 获取该ip地址对象对应的主机名
System.out.println(ip.getHostAddress()); // 获取该ip地址对象中的ip地址信息

InetAddress ip1 = InetAddress.getByName("www.baidu.com"); // 根据ip地址或者域名,返回一个inetAdress对象
System.out.println(ip1);
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());

System.out.println(ip1.isReachable(10000)); // 在指定毫秒内,判断主机与该ip对应的主机是否能连通

2. 端口号

啥是端口?

在这里插入图片描述

端口

  • 标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 0~65535。

分类

  • 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
  • 注册端口:1024~49151,分配给用户进程或某些应用程序。
  • 动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。

注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

3. 协议

通信协议

  • 网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
    在这里插入图片描述

开放式网络互联标准:OSI网络参考模型

  • OSI网络参考模型:全球网络互联标准。
  • TCP/IP网络模型:事实上的国际标准。
    在这里插入图片描述

传输层的2个通信协议

  • UDP(User Datagram Protocol):用户数据报协议;
  • TCP(Transmission Control Protocol) :传输控制协议。

UDP协议

通信效率高! 语音通话 视频直播

  • 特点:无连接、不可靠通信。
  • 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
  • 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 。

TCP协议

通信效率相对不高! 网页 文件下载 支付

  • 特点:面向连接、可靠通信。
  • TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
  • TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。

TCP协议:三次握手建立可靠连接

  • 可靠连接:确定通信双方,收发消息都是正常无问题的!(全双工)
    在这里插入图片描述

为啥要三次握手建立连接

  • 传输数据会进行确认,以保证数据传输的可靠性

TCP协议:四次握手断开连接

  • 目的:确保双方数据的收发都已经完成!
    在这里插入图片描述

2. UDP通信-快速入门

UDP通信

  • 特点:无连接、不可靠通信。
  • 不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。
  • Java提供了一个java.net.DatagramSocket类来实现UDP通信。
    在这里插入图片描述

DatagramSocket: 用于创建客户端、服务端

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

DatagramPacket:创建数据包

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

使用UDP通信实现:发送消息、接收消息

客户端实现步骤

  • ①创建DatagramSocket对象(客户端对象) ——扔韭菜的人
  • ②创建DatagramPacket对象封装需要发送的数据(数据包对象) ——韭菜盘子
  • ③使用DatagramSocket对象的send方法,传入DatagramPacket对象 ——开始抛出韭菜
  • ④释放资源
/**
 * 目标:完成UDP通信快速入门:实现1发1收。
 */
public class Client {
    public static void main(String[] args) throws Exception {

        // 1、创建客户端对象(发韭菜出去的人)
		//        public DatagramSocket(int port)	创建服务端的Socket对象, 并指定端口号
        DatagramSocket socket = new DatagramSocket(6667);

        // 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)
		//        public DatagramPacket(byte buf[], 参数一:封装要发出去的数据。
		//        int length,                       参数二:发送出去的数据大小(字节个数)
		//        InetAddress address,              参数三:服务端的IP地址(找到服务端主机)
		//        int port)                         参数四:服务端程序的端口。
        byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 6666);

        // 3、开始正式发送这个数据包的数据出去了
        socket.send(packet);

        System.out.println("客户端数据发送完毕~~~");
        socket.close();
    }
}

服务端实现步骤

  • ①创建DatagramSocket对象并指定端口(服务端对象) ——接韭菜的人
  • ②创建DatagramPacket对象接收数据(数据包对象) ——韭菜盘子
  • ③使用DatagramSocket对象的receive方法,传入DatagramPacket对象 ——开始接收韭菜
  • ④释放资源
/**
 * 目标:完成UDP通信快速入门-服务端开发
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建DatagramSocket对象并指定端口(服务端对象)
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建DatagramPacket对象接收数据(数据包对象)
        // public DatagramPacket(byte[] buf, int length)	创建用来接收数据的数据包
        byte[] bytes = new byte[64 * 1024]; // 64KB
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        // 3、使用DatagramSocket对象的receive方法,传入DatagramPacket对象
        socket.receive(packet);

        // 4、从字节数组中,把接收到的数据直接打印出来
        // 接收多少就倒出多少
        // 获取本次数据包接收了多少数据。
        int len = packet.getLength();

        String rs = new String(bytes, 0, len);
        System.out.println(rs);

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(packet.getPort());


        socket.close(); // 释放资源

    }
}

程序执行步骤

  • ①启动服务端程序,等待客户端启动
  • ②启动客户端程序,观察服务端控制台

3. UDP通信-多发多收

客户端可以反复发送数据

客户端实现步骤

  • ①创建DatagramSocket对象(发送端对象) —— 扔韭菜的人
  • ②使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
  • ③如果用户输入的不是exit, 把数据封装成DatagramPacket ——韭菜盘子
  • ④使用DatagramSocket对象的send方法将数据包对象进行发送 ——开始抛出韭菜
  • ⑤释放资源
/**
 * 目标:完成UDP通信快速入门:实现客户端反复的发。
 */
public class Client {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(6667);

        Scanner in = new Scanner(System.in);
        while (true) {
            String line = in.nextLine();
            if(Objects.equals("exit", line)){
                break;
            }

            byte[] bytes = line.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes
                    , bytes.length
                    , InetAddress.getLocalHost()
                    , 6666);

            socket.send(packet);
        }

        socket.close();
    }
}

接收端可以反复接收数据

接收端实现步骤

  • ①创建DatagramSocket对象并指定端口(接收端对象) ——接韭菜的人
  • ②创建DatagramPacket对象接收数据(数据包对象) ——韭菜盘子
  • ③使用DatagramSocket对象的receive方法传入DatagramPacket对象
  • ④使用while死循环不断的进行第3步
/**
 * 目标:完成UDP通信快速入门-服务端反复的收
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        DatagramSocket socket = new DatagramSocket(6666);

        byte[] bytes = new byte[64 * 1024];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        while (true) {
            socket.receive(packet);

            int length = packet.getLength();
            String rs = new String(bytes, 0, length);
            System.out.println("----------------------------");
            System.out.println(rs);

            System.out.println(packet.getAddress().getLocalHost());
            System.out.println(packet.getPort());
            System.out.println("----------------------------");
        }

//        socket.close();
    }
}

程序执行步骤

  • ①启动服务端程序,等待客户端启动
  • ②启动客户端程序,观察服务端控制台

4. TCP通信-快速入门

TCP通信

  • 特点:面向连接、可靠通信。
  • 通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。
  • Java提供了一个java.net.Socket类来实现TCP通信。

在这里插入图片描述

TCP通信之-客户端开发

  • 客户端程序就是通过java.net包下的Socket类来实现的。

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

客户端发送消息

客户端实现步骤

  1. 创建客户端的Socket对象,请求与服务端的连接。
  2. 使用socket对象调用getOutputStream()方法得到字节输出流。
  3. 使用字节输出流完成数据的发送。
  4. 释放资源:关闭socket管道。
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现1发1收。
 */
public class Client {
    public static void main(String[] args) throws Exception {

        // 1、创建Socket对象,并同时请求与服务端程序的连接。
		// public Socket(String host , int port) 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 6666);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();

        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        // 4、开始写数据出去了
        dos.writeUTF("在一起,好吗?");
        dos.close();

        socket.close();
    }
}

TCP通信-服务端程序的开发

  • 服务端是通过java.net包下的ServerSocket类来实现的。

ServerSocket

在这里插入图片描述

在这里插入图片描述

服务端实现接收消息

服务端实现步骤

  1. 创建ServerSocket对象,注册服务端端口。
  2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
  3. 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
  4. 释放资源:关闭socket管道
/**
 *  目标:完成TCP通信快速入门-服务端开发:实现1发1收。
 */
public class Server {
    public static void main(String[] args) throws Exception {

        System.out.println("------服务端启动成功-------");

        // 1、创建ServerSocket的对象,同时为服务端注册端口。
		// public ServerSocket(int port)	为服务端程序注册端口
        ServerSocket socket = new ServerSocket(6666);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
		// public Socket accept()	阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。
        Socket accept = socket.accept();

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = accept.getInputStream();

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        // 5、使用数据输入流读取客户端发送过来的消息
        String rs = dis.readUTF();
        System.out.println(rs);
        // 其实我们也可以获取客户端的IP地址
        System.out.println("客户端ip地址:" + accept.getRemoteSocketAddress());

        socket.close();
    }
}

程序执行步骤

  • ①启动服务端程序,等待客户端启动
  • ②启动客户端程序,观察服务端控制台

5. TCP通信-多发多收

使用TCP通信实现:多发多收消息

  1. 客户端使用死循环,让用户不断输入消息。
  2. 服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息。
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 6666); // 服务端ip、端口

        OutputStream os = socket.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);

        Scanner in = new Scanner(System.in);
        while (true) {
            String line = in.nextLine();

            if(Objects.equals("exit", line)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            dos.writeUTF(line);
            dos.flush();
        }

    }
}
/**
 *  目标:完成TCP通信快速入门-服务端开发:实现服务端反复接受消息
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("---服务端启动了----");
        ServerSocket serverSocket = new ServerSocket(6666);

        Socket socket = serverSocket.accept();
        InputStream is = socket.getInputStream();
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            try {
                String line = dis.readUTF();
                System.out.println(line);
            } catch (IOException e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了!");
                dis.close();
                socket.close();
                break;
            }
        }


    }
}

程序执行步骤

  • ①启动服务端程序,等待客户端启动
  • ②启动客户端程序,观察服务端控制台

6. TCP通信-同时接受多个客户端

目前我们开发的服务端程序,是否可以支持与多个客户端同时通信 ?

  • 不可以的。
  • 因为服务端现在只有一个主线程,只能处理一个客户端的消息。

TCP通信-支持与多个客户端同时通信

在这里插入图片描述

本次是如何实现服务端同时接收多个客户端的消息的?

  • 主线程定义了循环负责接收客户端Socket管道连接
  • 每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("---服务端启动了----");
        ServerSocket serverSocket = new ServerSocket(6666);


        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            new ServerReaderThread(socket).start();
        }
    }
}
public class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);

            while (true) {
                try {
                    String line = dis.readUTF();
                    System.out.println(line);
                } catch (IOException e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 6666); // 服务端ip、端口

        OutputStream os = socket.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);

        Scanner in = new Scanner(System.in);
        while (true) {
            String line = in.nextLine();

            if(Objects.equals("exit", line)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            dos.writeUTF(line);
            dos.flush();
        }
    }
}

程序执行步骤

  • ①启动服务端程序,等待客户端启动
  • ②打开idea的Run/Debug Configurations界面,选中客户端,勾选配置项“Allow parallel run”
  • ③启动多个客户端,每个客户端控制台输入内容,观察服务端控制台

7. TCP通信-综合案例

即时通信-群聊

TCP通信-端口转发

在这里插入图片描述

群聊是什么含义? 怎么实现?

  • 是指一个客户端把消息发出去,其他在线的全部客户端都可以收到消息。
  • 需要用到端口转发的设计思想。
  • 服务端需要把在线的Socket管道存储起来,一旦收到一个消息要推送给其他管道
public class Server {
    public static List<Socket> onlineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(6666);

        while(true){
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();
            onlineSockets.add(socket);
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReadThread(socket).start();

        }

    }
}
 public class ServerReadThread extends Thread{
    private Socket socket;

    public ServerReadThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is =socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while(true){
                try {
                    String line = dis.readUTF();
                    System.out.println(line);
                    // 把这个消息分发给全部客户端进行接收。
                    sendMsgToAll(line);
                } catch (IOException e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    Server.onlineSockets.remove(socket);
                    dis.close();
                    socket.close();
                    break;
                }

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

    private void sendMsgToAll(String msg) throws IOException {
        // 发送给全部在线的socket管道接收。
        for(Socket socket : Server.onlineSockets){
            OutputStream os = socket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 6666);

        // 创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息。
        new ClientReadThread(socket).start();

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();
        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner in = new Scanner(System.in);
        while (true) {
            String line = in.nextLine();
            if(Objects.equals("exit", line)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            dos.writeUTF(line);
            dos.flush();
        }
    }
}
public class ClientReadThread extends Thread{
    private Socket socket;

    public ClientReadThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);

            while(true){
                try {
                    String rs = dis.readUTF();
                    System.out.println(rs);
                } catch (IOException e) {
                    System.out.println("自己下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

程序执行步骤

  • ①启动服务端程序,等待客户端启动
  • ②打开idea的Run/Debug Configurations界面,选中客户端,勾选配置项“Allow parallel run”
  • ③启动多个客户端,每个客户端控制台输入内容,观察服务端控制台、观察每个客户端控制台

实现一个简易版的BS框架

需求

  • 要求从浏览器中访问服务器,并立即让服务器响应一个很简单的网页给浏览器展示,网页内容就是“就是牛666”

BS架构的基本原理

在这里插入图片描述

注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据。

  • HTTP协议规定:响应给浏览器的数据格式必须满足如下格式
    在这里插入图片描述
/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws IOException {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8080);

        while(true){
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }

    }
}
public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //  立即响应一个网页内容:“黑马程序员”给浏览器展示。
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须换行
            ps.println("<div style ='color:red;font-size=120px;text-align:center'>就是牛666<div>");
            ps.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

程序执行步骤

  • ①启动服务端程序,等待浏览器访问
  • ②浏览器访问地址“http://127.0.0.1:8080/”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值