Java 网络编程基础

网络通信三要素

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

基本的通信架构

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

image-20241005174021564

IP 地址

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

image-20241005174700193

image-20241005174812691

image-20241005175206940

image-20241005175323233

⚠️ 右上角框框为 运营商 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 地址)

image-20241005175829937

端口号

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

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

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

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

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

通信协议

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

image-20241005190510717

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

image-20241005190553340

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

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

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

image-20241005191313754

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

image-20241005191628265

四次挥手

image-20241005191731199

ContentsUDP通信-快速入门

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

image-20241005192053753

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 通信-多发多收

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 通信。

image-20241005200654684

image-20241005200740292

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 类来实现的。

image-20241005201852781

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 通信-多发多收

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(); // 释放连接资源
    }
}
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 通信-同时接收多个客户端

image-20241005205708921

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(); // 释放连接资源
    }
}

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();
        }
    }
}
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通信-综合案例

即时通信-群聊

image-20241005211636643

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());
                }
            }
        }
    }
}
public class ClentReaderThread extends Thread {
    private Socket socket;
    public ClentReaderThread(Socket socket) {
        this.socket = socket;
    }
}

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();
        }
    }
}
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”

image-20241005213841638

image-20241005213929070

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笔记
image-20241005214829542


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))
        }
    }
}
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>);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值