Java 网络编程基础

网络通信三要素

此笔记来之与黑马.B站的视频是真的高

基本的通信架构

  • 基本的通信架构有2种形式:CS架构(Client 客户端/ Server 服务端)、BS架构( Browser 浏览器/ Server 服务端)。

IP 地址

IP(InternetProtocol):全称 "互联网协议地址",是分配给上网设备的唯一标志。

IP 地址有两种形式:IPv4, IPv6

⚠️ 右上角框框为 运营商 id

公网 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地址:检查网络是否连通。
InnetAddress (IP 地址)

端口号

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

分类
  • 周知端口:0 ~ 1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21)

  • **注册端口:**1024 ~ 49151,分配给用户进程或某些应用程序。

  • 动态端口:49152 到 65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。

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

通信协议

网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。

开放式网络互联标准:OSI 网络参考模型
  • OSI 网络参考模型:全球网络互联标准
传输层的2个通信协议
  • UDP(User Datagram Protocol):用户数据报协议;TCP(Transmission Control Protocol):传输控制协议。
UDP协议

特点:无连接、不可靠通信。诵信效率高!语音诵话视频直播

  • 不事先建立连接,数据按照包发,一包数据包含:自己的 IP、程序端口,目的地 IP、程序端口和数据(限制在 64KB 内)等。
  • 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的。
TCP 协议
  • 特点:面向连接、可靠通信。
  • TCP 的最终目的:要保证在不可靠的信道上实现可靠的传输。
  • TCP 主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。
四次挥手

ContentsUDP通信-快速入门

Java提供了一个 java.net.DatagramSocket 类来实现 UDP 通信。

java 复制代码
public class Client {
    public static void main(String[] args) throws Exception {
        // 1. 创建客户对象(发数据出去的人)
        DatagramSocket socket = new DatagramSocket();
        // 2. 创建数据包对象封装要发出去的数据(创建一个数据包)
        /** public DatagramPacket(byte buf[], int length,
        	InetAddress address, int port)
        	参数一: 封装要发出去的数据
        	参数二:发送出去的数据大小(字节个数)
        	参数三:服务端的 IP 地址(找到服务端主机)
        	参数四:服务端程序的端口
        */
        
        byte[] byres = "我是快乐的客户端,我爱你 abc".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytets.length,
                                                  InetAddress.getLocalHost(), port: 6666);
        
        // 3. 正式发送这个数据包的数据出去了
        socket.send(packet);
        System.out.println("客户端数据发送完毕~~");
        socket.close(); // 释放数据!
    }
}

class Server {
    public static void main(Stirng[] args) throws Exception {
        System.out.println("-----服务器端启动");
        // 1. 创建一个服务端口(创建一个接数据包的人) 注册端口
        DatagramSocker socket = new DatagramSocket(port: 6666);
        
        // 2. 创建一个数据包对象,用于接收数据的(创建一个数据包)
        byte[] buffer = new byte[1024 * 64]; // 64 KB
        DatagramPacket packet = new DatagramPacket(buffer.length);
        
        // 3. 开始正式使用数据包来接收客户端发来的数据
        socket.receive(packet);
        
        // 4.从字节数组中,把接收到的数据直接打印出来
        // 接收多少就倒出多少
        int len = packet.getLength();
        
        String res = new String(buffer, 0, len);
        System.out.println(rs);
        
        System.out.println(packet.getAddress().getHostAddresss());
        System.out.prinlnt(packet.getPort());
        
        socket.close(); //释放资源
    }
}

UDP 通信-多发多收

java 复制代码
public class Client {
    public static void main(String[] args) throws Exception {
        // 1. 创建客户对象(发数据出去的人)
        DatagramSocket socket = new DatagramSocket();
        // 2. 创建数据包对象封装要发出去的数据(创建一个数据包)
        /** public DatagramPacket(byte buf[], int length,
        	InetAddress address, int port)
        	参数一: 封装要发出去的数据
        	参数二:发送出去的数据大小(字节个数)
        	参数三:服务端的 IP 地址(找到服务端主机)
        	参数四:服务端程序的端口
        */
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请说: ");
            String msg = scanner.nextLine();
            
            if ("exit".equals(msg)) {
                break;
            }
            
             byte[] byres = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytets.length,
                                                      InetAddress.getLocalHost(), port: 6666);

            // 3. 正式发送这个数据包的数据出去了
            socket.send(packet);
            System.out.println("客户端数据发送完毕~~");
        }
        
         socket.close(); // 释放数据!
    }
}

class Server {
    public static void main(Stirng[] args) throws Exception {
        System.out.println("-----服务器端启动");
        // 1. 创建一个服务端口(创建一个接数据包的人) 注册端口
        DatagramSocker socket = new DatagramSocket(port: 6666);
        
        // 2. 创建一个数据包对象,用于接收数据的(创建一个数据包)
        byte[] buffer = new byte[1024 * 64]; // 64 KB
        DatagramPacket packet = new DatagramPacket(buffer.length);
        
        while (true) {
            System.out.println("服务端启动");
            // 3. 开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);

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

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

            System.out.println(packet.getAddress().getHostAddresss());
            System.out.printtln(packet.getPort());   
            System.out.println("--------------分割线---------------");
        }
        socket.close(); //释放资源
    }
}

TCP 通信-快速入门

Java 提供了一个 java.net.Socket 类来实现 TCP 通信。

java 复制代码
public class Client {
	public static void main(String[] args) throws Exception {
        // 1. 创建 Socket 对象,并同时请求与服务端程序的连接
		Socket socket = new Socket("127.0.0.1", 8888);
        
        // 2.从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();
        
        // 3.把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);
        
        // 4.开始写数据出去了
        dos.writeUTF("在一起, 好吗?");
        dos.close();
        
        socket.close(); // 释放连接资源
    }
}

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

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

java 复制代码
public class Server {
    public static void (String[] args) throws Exception {
        // 1. 创建 ServerSocket的对象, 同时为服务器注册端口
        ServerSocket serverSocket = new ServerSocket(port: 8888);
        
        // 2.使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();
        
        // 3. 从socket通信管道中得到一个字节输入流
        InputStream is = socket.getInputStream();
        
        // 5.使用数据输入流读取客户端发送过来的消息
        String rs = dis.readUTF();
        System.out.println(rs);
        
        System.out.println(socket.getRemoteSocketAddress())
            
        dis.close();
        socket.close();
    }
}

TCP 通信-多发多收

java 复制代码
public class Client {
	public static void main(String[] args) throws Exception {
        // 1. 创建 Socket 对象,并同时请求与服务端程序的连接
		Socket socket = new Socket("127.0.0.1", 8888);
        
        // 2.从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();
        
        // 3.把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);
        
        Scanner sc = new Scanner(System.in);
        while (true) {
            
            System.out.println("请说: ");
            String msg = sc.nextLine();
            
            // 一旦用户输入了 exit, 就退出了客户端程序
            if ("exit".equals(msg)) {
                System.out.println("欢迎您下次光临!! 退出成功!");
                break;
            }
            
            // 4.开始写数据输出了
            dos.writeUTF(msg);
            dos.flush();
        }
		
        dos.close();
        socket.close(); // 释放连接资源
    }
}
java 复制代码
public class Server {
    public static void (String[] args) throws Exception {
        // 1. 创建 ServerSocket的对象, 同时为服务器注册端口
        ServerSocket serverSocket = new ServerSocket(port: 8888);
        
        // 2.使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();
        
        // 3. 从socket通信管道中得到一个字节输入流
        InputStream is = socket.getInputStream();
        
        // 4.把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);
        
        while (true) {
            // 5.使用数据输入流读取客户端发送过来的消息
            try {
                String rs = dis.readUTF();
            	System.out.println(rs);
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println(socket.getRemoteSocketAddress() + "离线了");
                break;
            }
        	
        }
            
        dis.close();
        socket.close();
    }
}

TCP 通信-同时接收多个客户端

java 复制代码
public class Client {
	public static void main(String[] args) throws Exception {
        // 1. 创建 Socket 对象,并同时请求与服务端程序的连接
		Socket socket = new Socket("127.0.0.1", 8888);
        
        // 2.从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();
        
        // 3.把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);
        
        Scanner sc = new Scanner(System.in);
        while (true) {
            
            System.out.println("请说: ");
            String msg = sc.nextLine();
            
            // 一旦用户输入了 exit, 就退出了客户端程序
            if ("exit".equals(msg)) {
                System.out.println("欢迎您下次光临!! 退出成功!");
                break;
            }
            
            // 4.开始写数据输出了
            dos.writeUTF(msg);
            dos.flush();
        }
		
        dos.close();
        socket.close(); // 释放连接资源
    }
}
java 复制代码
public class Server {
    public static void main(String[] args) {
        System.out.println("--------服务器启动成功--------------");
        // 1. 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8888);
        
        while (true) {
            
            // 2. 使用serverSocket对象,调用一个 accept 方法,等待客户端连接请求
            Socket socket = serverSocket.accept();
            
            // 3. 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            new ServerReaderThread(socket).start();
        }
    }
}
java 复制代码
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) {
            	String msg = dis.readUTF();
                System.out.println(msg);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP通信-综合案例

即时通信-群聊

java 复制代码
public class Client {
	public static void main(String[] args) throws Exception {
        // 1. 创建 Socket 对象,并同时请求与服务端程序的连接
		Socket socket = new Socket("127.0.0.1", 8888);
        
        // 创建一个独立的线程 负责随机从socket中接收服务端发送过来的消息
        new ClentReaderThread(socket).start();
        // 2.从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();
        
        // 3.把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);
        
        Scanner sc = new Scanner(System.in);
        while (true) {
            
            System.out.println("请说: ");
            String msg = sc.nextLine();
            
            // 一旦用户输入了 exit, 就退出了客户端程序
            if ("exit".equals(msg)) {
                System.out.println("欢迎您下次光临!! 退出成功!");
                break;
            }
            
            // 4.开始写数据输出了
            dos.writeUTF(msg);
            dos.flush();
        }
		
        dos.close();
        socket.close(); // 释放连接资源
    }
    
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true) {
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    System.out.println("自己下线了: " + socket.getRemoteSock());
                }
            }
        }
    }
}
java 复制代码
public class ClentReaderThread extends Thread {
    private Socket socket;
    public ClentReaderThread(Socket socket) {
        this.socket = socket;
    }
}
java 复制代码
public class Server {
    public static List<Socket> onLineSockets = new ArrayList<>();
    public static void main(String[] args) {
        
        System.out.println("--------服务器启动成功--------------");
        // 1. 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8888);
        
        while (true) {
            
            // 2. 使用serverSocket对象,调用一个 accept 方法,等待客户端连接请求
            Socket socket = serverSocket.accept();
            onLineSockets.add(socket); 
            System.out.println("有人上线了: " + socket.getRemoteSocketAddress());
            // 3. 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            new ServerReaderThread(socket).start();
        }
    }
}
java 复制代码
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) {
            	String msg = dis.readUTF();
                System.out.println(msg);
                
                sendMsgToAll(msg); // 发送给所有的客户端
            }
            
        } catch (IOException e) {
            System.out.println("有人下线了: " + socket.getRemoteSocketAddress());
            Server.onLineSockets.remove(socket);
            e.printStackTrace();
        }
    }
    
    private void sendMsgToAll(String msg) throws IOExecption {
		// 发送给所有在线的 socket 管道接收
        for (Socket onLineSocket : Server.onLineSockets) {
            OUtputStream os = onLineSocket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.write(msg);
            dos.flush();
        }
    }
}

实现一个 BS 架构(浏览器+程序)

要求从浏览器中访问服务器, 并立即让服务器响应一个很简单的网页给浏览器展示, 网页内容就是"黑马程序员666"

java 复制代码
public class ServerReaderThread extends Thread {
    private Socket socket;
    public SeverReaderThread(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" 黑马程序员磊哥</div>);
        } cathch (Exception e) {
            e.printStackTrace();
        }
    }
}

线程池优化 BS 架构

可以参考博主这篇JUC笔记

java 复制代码
public class Server {
    public static List<Socket> onLineSockets = new ArrayList<>();
    public static void main(String[] args) {
        
        System.out.println("--------服务器启动成功--------------");
        // 1. 创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8888);
        
        // 创建出一个线程池,负责处理通信管道的任务
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16*2, 16*2, new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        
        while (true) {
            
            // 2. 使用serverSocket对象,调用一个 accept 方法,等待客户端连接请求
            Socket socket = serverSocket.accept();
            pool.execute(new ServerReaderRunnable(socket))
        }
    }
}
java 复制代码
public class ServerReaderThread extends Runnable {
    private Socket socket;
    public SeverReaderThread(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" 黑马程序员磊哥</div>);
        }
    }
}
相关推荐
WaaTong2 分钟前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式
城南vision3 分钟前
计算机网络——TCP篇
网络·tcp/ip·计算机网络
面试鸭4 分钟前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展
Ciderw31 分钟前
块存储、文件存储和对象存储详细介绍
网络·数据库·nvme·对象存储·存储·块存储·文件存储
lihuhelihu40 分钟前
第3章 CentOS系统管理
linux·运维·服务器·计算机网络·ubuntu·centos·云计算
沈询-阿里1 小时前
java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl
java·开发语言
幸运超级加倍~1 小时前
软件设计师-上午题-15 计算机网络(5分)
笔记·计算机网络
AaVictory.1 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
LuckyLay1 小时前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring