Java SE:网络编程

网络编程意义

网络编程使得不同计算机之间能够进行通信和交互,从而实现了许多现代应用程序和服务的功能。

常见的网络编程架构

CS(客户端服务端模式):需要客户端开发

BS(浏览器服务端模式):不需要开发客户端

IP

设备的唯一标识符

端口号

应用程序在设备中的唯一标识符 ,每个端口号都是设备的出口或入口,所有设备在网络中都是通过端口进行传输接收数据

协议

UDP协议

面向无连接,速度快,有大小限制,数据不安全,数据易丢失

传输接收数据4步走

java 复制代码
 //创建主机对象
        //调用方法创建主机对象
        InetAddress address = InetAddress.getByName("小胖子");
        System.out.println(address);

        //获取主机名字
        String name = address.getHostName();
        System.out.println(name);

        //获取主机地址
        String hostAddress = address.getHostAddress();
        System.out.println(hostAddress);
java 复制代码
 public static void main(String[] args) throws IOException {
        //利用UDP端口发送数据

        /*
        * 1.创建发送数据的平台
        * 2.打包数据
        * 3,发送
        * 4,释放资源
        * */

        /*
        * 传输:
        * 若无指定参数则默认随机传递端口
        * 若指定参数则传递到指定端口位置
        * */
        //1. 创建发送数据的平台
        DatagramSocket socket = new DatagramSocket();

        //要传输的内容
        String str = "丢那醒啊";
        //转换为字节数据传递数据
        byte[] bytes = str.getBytes();
        //要发送到的目的地
        //创建主机对象,要发送的目的地·
        InetAddress address = InetAddress.getByName("小胖子");
        //创建端口
        int port = 10086;

        //2. 打包要发送的数据
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        //3. 发送内容
        socket.send(dp);

        //4. 释放资源
        socket.close();
    }
java 复制代码
public static void main(String[] args) throws IOException {
        /*
        * 接收数据:
        * 指定接收数据平台
        * 接收数据包
        * */

        //创建接收数据平台
        DatagramSocket socket = new DatagramSocket(10086);

        //1. 创建接收数据包对象
        //存放接收的字节数据进数组
        byte[] bytes = new byte[1024];

        //2. 创建接收数据包
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        //调用接收数据方法Receive,此时接收的数据存入数据包packet中
        socket.receive(packet);

        //3. 接收数据
        //从哪个设备中发出
        InetAddress address = packet.getAddress();
        //发出的数据内容以字节数组进行接收
        byte[] data = packet.getData();
        //从哪个设备的端口中发出
        int port = packet.getPort();

        String str = new String(data, 0, data.length);
        System.out.println("接收的数据内容为:" + str);
        System.out.println("发送数据的电脑端口为:" + port);

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

单播、组播、广播

单播:一台设备向另一台设备发送数据

组播:一台设备向另一组设备发送数据(如:机房授课,授课设备向一组被授课设备发送信息)(组播地址:224.0.0.0 ~ 239.255.255.255其中预留组播地址:224.0.0.0 ~ 224.0.0.255)

广播:一台设备向所有设备同时发送数据,所有设备都能接收到 (广播地址:255.255.255.255)

java 复制代码
public class MultSend {
    //组播发送数据,一台设备向一组设备同时发送数据,要保证组播地址完全一致

    public static void main(String[] args) throws IOException {
        ///1. 创建组播对象(要发送的平台)
        MulticastSocket ms = new MulticastSocket();

        //2. 打包数据包(要发送的数据内容)
        String str = "吃蒙你啊";
        byte[] bytes = str.getBytes();
        //接收的地址
        InetAddress address = InetAddress.getByName("224.0.0.1");
        //发送端口号
        int port = 10086;

        //打包成数据包
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, port);

        //3. 发送数据包
        ms.send(packet);

        //4. 释放资源
        ms.close();

    }
}


public class MulReceive2 {
    public static void main(String[] args) throws IOException {
        //接收组播发送的数据

        //1. 创建组播平台(组播的端口号必须保持一致)
        MulticastSocket ms = new MulticastSocket(10086);

        //2. 接收组播数据包
        //用多大空间接收
        byte[] bytes = new byte[1024];
        //将本机添加到组播中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);
        //接收包的端口号
        int port = 10086;

        //创建接收包对象
        DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, address, 10086);

        //3. 接收数据包
        ms.receive(packet);
        //解析数据
        InetAddress source = packet.getAddress();
        byte[] data = packet.getData();
        int length = packet.getLength();
        int packetPort = packet.getPort();

        String str = new String(data, 0, length);

        //4. 释放资源
        ms.close();

        System.out.println("地址为" + source + "从端口" +  packetPort + "发送了:" + str);

    }
}

TCP协议

面向有连接即需要两端必须连接,否则无法传输数据 ,传输速度快,数据不易丢失
三次握手四次挥手协议

三次握手:客户端向服务端请求发送数据(第一次握手),服务端回应客户端表示收到请求(第二次握手),客户端确认将数据发送(第三次握手)

四次挥手:客户端向服务端请求停止发送数据(第一次挥手),服务端回应客户端表示收到该请求 (第二次挥手),但此时服务端可能还未完全接收完整数据,若此时立刻关闭传输通道则数据丢失,故要等待服务端发送确认停止传输数据(第三次挥手),客户端确认停止发送数据(第四次挥手)

java 复制代码
public class Cilen {
    public static void main(String[] args) throws IOException {
        /*
        * UDP协议只管发送数据,不管通道是否连接,故容易发生丢失数据的情况
        * TCP协议要保证两台设备直接已经构建通道,不然无法传输数据
        * */
        //获取本机对象IP地址
        InetAddress address = InetAddress.getByName("小胖子");
        String hostAddress = address.getHostAddress();
        System.out.println(hostAddress);

        //创建Socket对象
        //若获取不到服务端对象会报错
        Socket socket = new Socket(address, 10086);

        //写出数据
        OutputStream os = socket.getOutputStream();
        //创建字符输出流
        os.write("吃蒙你啊啊!666".getBytes());

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


public class Server{
    public static void main(String[] args) throws IOException {
        //创建服务器对象
        ServerSocket serverSocket = new ServerSocket(10086);

        //和客户端连接(获取到连接的客户端对象)
        Socket s = serverSocket.accept();

        /*InputStream is = s.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);*/

        //字符缓冲流读取数据
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

        int b;
        while ((b = br.read()) != -1) {
            System.out.print((char) b);
        }

        br.close();
        s.close();
        serverSocket.close();
    }
}

test:客户端和服务端互发消息

java 复制代码
public class client {
    //客户端发送一条数据,接收服务器的反馈并打印
    public static void main(String[] args) throws IOException {
        //1.创建客户端对象
        /*
         * 参数一:信息要发送的目的地,此时发送到本机
         * 参数二:发送端绑定的端口
         * */
        Socket socket = new Socket(InetAddress.getByName("小胖子"), 10086);

        //2.发送数据
        String str;
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);

        //键盘输入内容
        System.out.println("请输入要发送的内容:");
        str = sc.nextLine();

        //3.输出
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

        //细节:关闭通道,让程序继续往下读
        socket.shutdownOutput();

        //4.接收反馈消息
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

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



public class Server {
    public static void main(String[] args) throws IOException {
        //服务端,接收客户端的消息并打印,再给客户端反馈消息
        //1.创建客户端对象(绑定对应端口)
        ServerSocket ss = new ServerSocket(10086);

        //2.获取客户端对象(等待客户端连接,连接成功将返回连接对象)
        Socket socket = ss.accept();

        //3.获取字节输入流对象
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        //读入数据
        /*
         * 细节:
         * 从通道中读入数据时若没有读取到结束标记程序会卡在此处
         * 若要程序继续往下运行需要关闭通道
         * */
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

        //4. 反馈消息
        OutputStream os = socket.getOutputStream();
        String str = "接收成功";
        os.write(str.getBytes());

        //5.释放资源
        socket.close();
        ss.close();
    }
}

控制台版聊天室练习

客户端

java 复制代码
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws IOException {
        //客户端
        while (true) {
            //创建客户端对象,连接服务端
            Socket socket = new Socket(InetAddress.getByName("小胖子"), 10086);
            System.out.println("连接服务器");

            Scanner sc = new Scanner(System.in);

            //登录界面
            ChatJFrame();
            System.out.println("请输入你的选择:");
            switch (sc.nextLine()) {
                case "1" -> Login(socket);
                default -> System.out.println("输入错误,请重新输入");
            }
        }
    }

    //登录
    public static void Login(Socket socket) throws IOException {
        /*
        * 其他要求:

用户名和密码要求:

要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。

要求2:密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。
        * */

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        //拼接输入内容,并传递给服务端
        StringBuilder sb = new StringBuilder();
        String str = sb.append(name + "=").append(password).toString();
        sendContent(socket, str);

        //接收服务器反馈
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String outcome = br.readLine();
        //根据反馈做出不同的相应
        /*
        "用户名不符合要求"
        "密码不符合要求"
        "没有该用户信息"
        "登录成功"
        "用户名和密码不匹配"
        */
        if ("登录成功".equals(outcome)) {
            System.out.println("登录成功,开始聊天");
            startChat(socket);
        } else {
            System.out.println(outcome);
        }
    }

    private static void startChat(Socket socket) throws IOException {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入你要发送的内容:");
            String str = sc.nextLine();
            sendContent(socket, str);
        }
    }

    private static void sendContent(Socket socket, String str) throws IOException {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(str);
        bw.newLine();
        bw.flush();
    }

    //登录界面
    public static void ChatJFrame() {
        System.out.println("==============欢迎来到食懵尼啊聊天室================");
        System.out.println("1登录");
        System.out.println("2注册");
    }
}

服务端

java 复制代码
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Server {
    public static void main(String[] args) throws IOException {
        //创建服务端对象
        ServerSocket ss = new ServerSocket(10086);

        while (true) {
            //获取客户端对象(与谁连接)
            Socket socket = ss.accept();
            System.out.println("成功连接客户端");
            myRunnable myRunnable = new myRunnable(socket);
            //开启多条线程,同时连接多个客户端
            Thread thread = new Thread(myRunnable);
            thread.start();
        }
    }

    private static void login(Socket socket) throws IOException {
        //将客户端写出的数据读入
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String str = br.readLine();
        String[] split = str.split("=");
        String name = split[0];
        String password = split[1];

        //获取服务端输出对象,回写数据给客户端
        //判断用户登录的账户密码信息
        //1.判断用户名是否符合要求
        boolean flag = isName(name);
        if (!flag) {
            //回写数据
            System.out.println("用户名不符合要求");
            loginOutCome(socket, "用户名不符合要求");
            return;
        }

        //2.判断密码是否符合要求
        flag = isPassword(password);
        if (!flag) {
            System.out.println("密码不符合要求");
            loginOutCome(socket, "密码不符合要求");
            return;
        }

        //3.校验用户密码符合要求后遍历文件内的内容是否含有该用户信息
        //创建输入流对象(将文件内容导入程序)
        br = new BufferedReader(new InputStreamReader(new FileInputStream("Work/register")));
        //创建双列集合存储用户名和密码
        HashMap<String, String> hm = new HashMap<>();
        //将文件内容加载入程序
        while ((str = br.readLine()) != null) {
            hm.put(str.split("=")[0], str.split("=")[1]);
        }
        //判断是否含有该信息
        boolean b = hm.containsKey(name);
        if (!b) {
            System.out.println("没有该用户信息");
            loginOutCome(socket, "没有该用户信息");
            return;
        }
        String value = hm.get(name);
        if (value.equals(password)) {
            System.out.println("登录成功");
            loginOutCome(socket, "登录成功");
            while (true) {
                receiveContent(socket, name);
            }
        } else {
            System.out.println("用户名和密码不匹配");
            loginOutCome(socket, "用户名和密码不匹配");
        }

    }

    private static void receiveContent(Socket socket, String name) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String str = br.readLine();
        System.out.println(name + ":" + str);
        //转发消息给其他用户
    }

    private static void loginOutCome(Socket socket, String outcome) throws IOException {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(outcome);
        bw.newLine();
        bw.flush();
    }

    private static boolean isPassword(String password) {
        //要求2:密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。
        Pattern p = Pattern.compile("[0-9]{3,8}");
        Matcher m = p.matcher(password);
        return m.matches();
    }

    private static boolean isName(String name) {
        // 要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。
        Pattern p = Pattern.compile("[a-zA-z]{2,18}");
        Matcher m = p.matcher(name);
        return m.matches();
    }


    static class myRunnable implements Runnable {

        Socket socket;

        myRunnable(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                login(this.socket);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
相关推荐
吃肉不能购10 分钟前
Label-studio-ml-backend 和YOLOV8 YOLO11自动化标注,目标检测,实例分割,图像分类,关键点估计,视频跟踪
运维·yolo·自动化
学Linux的语莫14 分钟前
Ansible使用简介和基础使用
linux·运维·服务器·nginx·云计算·ansible
qq_3129201123 分钟前
docker 部署 kvm 图形化管理工具 WebVirtMgr
运维·docker·容器
Onlooker12924 分钟前
云服务器部署WebSocket项目
服务器
草莓base30 分钟前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
学Linux的语莫37 分钟前
搭建服务器VPN,Linux客户端连接WireGuard,Windows客户端连接WireGuard
linux·运维·服务器
legend_jz42 分钟前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
黑牛先生44 分钟前
【Linux】进程-PCB
linux·运维·服务器
Karoku0661 小时前
【企业级分布式系统】ELK优化
运维·服务器·数据库·elk·elasticsearch
drebander1 小时前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list