菜鸟之路Day21一一网络编程

简介: 《菜鸟之路 Day21:网络编程(一)》由 blue 于 2025 年 3 月 2 日发布。本文介绍了网络编程的基础知识,包括 CS/BS 架构的区别、网络编程三要素(IP、端口号、协议),并详细讲解了 InetAddress 类的使用。接着通过 UDP 和 TCP 协议演示了数据发送与接收的过程,涵盖单播、组播和广播通信方式。最后,通过多个综合练习(如多发多收、文件上传等)巩固所学内容,并引入多线程和线程池优化服务器性能。

菜鸟之路Day21一一网络编程

作者:blue

时间:2025.3.2

1.初识网络编程

①什么是网络编程:计算机与计算机之间通过网络进行数据传输

②常见软件架构有哪些:CS/BS

③通信的软件架构CS/BS各有什么区别和优缺点

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

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

​ CS:适合定制专业化的办公软件如:IDEA,网游

​ BS:适合移动互联网应用,可以在任何地方随时访问的系统

2.网络编程三要素

IP:设备在网络中的地址,是唯一的标识

端口号:应用程序在设备中唯一的标识

协议:数据在网络中传输的规则,常见的协议有UDP,TCP,http,https,ftp

3.InetAddress类的使用

InetAddress类表示互联网协议(IP)地址

public class myInetAddressDemo1 {
   
    public static void main(String[] args) throws UnknownHostException {
   
        /*
        * static InetAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
        * String getHostName() 获取此IP地址的主机名
        * String getHostAddress() 返回文本显示中IP地址字符串
        * */

        //获取InetAddress对象
        //IP的对象或者说是主机的对象
        InetAddress address = InetAddress.getByName("LAPTOP-CFHO6DM4");
        //InetAddress address = InetAddress.getByName("192.168.213.203");
        System.out.println(address);

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

        //获取对应的ip
        String ip = address.getHostAddress();
        System.out.println(ip);
    }
}

4.UDP通信程序

4.1发送数据

①创建发送端的DatagramSocket对象;②数据打包(DatagramPacket)

③发送数据;④释放资源

public class SendMessageDemo {
   
    public static void main(String[] args) throws IOException {
   
        //1.创建发送端的DatagramSocket对象
        //细节:
        //绑定端口,以后我们就是通过这个端口往外发送
        //空参:所有可用的都拗口中随机一个进行使用
        //有参:指定端口号进行绑定
        DatagramSocket ds = new DatagramSocket();

        //2.打包要发送的数据
        String str = "好好学习,天天向上";
        byte[] bytes = str.getBytes();//转为字节数组
        InetAddress ip = InetAddress.getByName("127.0.0.1");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);

        //3.发送数据
        ds.send(dp);

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

4.2接受数据

①创建接收端的DatagramSocket对象;②接收打包好的数据

③解析数据包;④释放资源

public class ReceiveMessageDemo {
   
    public static void main(String[] args) throws IOException {
   
        //1.创建DatagramSocket对象
        //注意:此时接收端所指定的端口号,必须与发送方的目的端口号相一致才能接收到数据
        DatagramSocket ds = new DatagramSocket(10086);

        //2.接收打包好的数据
        byte[] bytes = new byte[1024];//创建一个字节数组来存放接收到的数据
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

        ds.receive(dp);//接收数据,注意,这个方法是阻塞的,收不到数据程序就会停在这里

        //3.解析数据包
        byte[] data = dp.getData(); //获取接收到的字节数组
        int length = dp.getLength();//获取数据报文的长度
        int port = dp.getPort(); //获取发送方的端口号
        InetAddress ip = dp.getAddress();//获取发送方的ip
        System.out.println("收到数据:"+new String(data,0,length));
        System.out.println("数据长度:"+length);
        System.out.println("数据从发送方的"+port+"端口发出");
        System.out.println("发送方的IP地址为:"+ip);

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

4.3UDP通信程序练习(聊天室)

按照下面的要求实现程序

UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束

UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收

发送端

public class SendMessage {
   
    public static void main(String[] args) throws IOException {
   
        //发送端
        DatagramSocket ds = new DatagramSocket();

        Scanner sc = new Scanner(System.in);
        while (true) {
   
            System.out.println("请输入你想说的话");
            String str = sc.nextLine();
            InetAddress ip = InetAddress.getByName("127.0.0.1");
            int port = 10086;//目的端口和目的地址
            DatagramPacket dp = new DatagramPacket(str.getBytes(),str.getBytes().length,ip,port);
            ds.send(dp);//发送数据
            if("886".equals(str)) break;
        }

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

    }
}

接收端

public class ReceiveMessage {
   
    public static void main(String[] args) throws IOException {
   
        //接收端
        DatagramSocket ds = new DatagramSocket(10086);

        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        while (true) {
   
            ds.receive(dp);
            byte[] data = dp.getData();
            int length = dp.getLength();
            String ip = dp.getAddress().getHostAddress();
            String name = dp.getAddress().getHostName();
            System.out.println("收到来自ip为:"+ip+"主机名为:"+name+"所发送的信息:"+new String(data,0,length));
        }
    }
}

4.4单播,组播,广播

单播:上述代码都是单播

组播:组播地址:224.0.0.0 - 239.255.255.255,其中224.0.0.0 - 224.0.0.255为预留的组播地址

同一分组的ip都可以接收到信息

组播发送端

public class SendMessage {
   
    public static void main(String[] args) throws IOException {
   
        //组播发送端
        //创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket();

        //创建DatagramPacket对象
        String str = "你好,你好";
        byte[] bytes = str.getBytes();
        InetAddress ip = InetAddress.getByName("224.0.0.1");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);

        //发送数据
        ms.send(dp);

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

组播接收端

有多个组播接收端,只要接收端的ip为发送端目的的组播ip,就都能收到信息

public class ReceiveMessageOne {
   
    public static void main(String[] args) throws IOException {
   
        //组播接收端
        //创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket(10086);

        //将当前本机,添加到224.0.0.1这一组中
        InetAddress ip = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(ip);

        //创建DatagramPacket对象
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);

        //接收数据
        ms.receive(dp);

        //解析数据
        byte[] data = dp.getData();
        int length = dp.getLength();
        String HostAddress = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();
        System.out.println("收到来自ip为:"+HostAddress+"主机名为:"+name+"所发送的信息:"+new String(data,0,length));

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

广播:广播地址:255.255.255.255

局域网内任意ip都可以接收到信息

只需要将目的ip改为:255.255.255.255

public class SendMessageDemo {
   
    public static void main(String[] args) throws IOException {
   
        //1.创建发送端的DatagramSocket对象
        //细节:
        //绑定端口,以后我们就是通过这个端口往外发送
        //空参:所有可用的端口中随机一个进行使用
        //有参:指定端口号进行绑定
        DatagramSocket ds = new DatagramSocket();

        //2.打包要发送的数据
        String str = "好好学习,天天向上";
        byte[] bytes = str.getBytes();//转为字节数组
        InetAddress ip = InetAddress.getByName("255.255.255.255");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);

        //3.发送数据
        ds.send(dp);

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

5.TCP通信程序

TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象

通信之前要保证连接已经建立

通过Socket产生IO流来进行网络通信

客户端流程:

①创建客户端Socket对象(Socket)与指定服务端连接

Socket(String host,int port)

②获取输出流,写数据

OutputStream getOutputStream()

③释放资源

void close()
public class Client {
   
    public static void main(String[] args) throws IOException {
   
        //创建客户端Socket对象(Socket)与指定服务端连接
        Socket socket = new Socket("127.0.0.1",10000);

        //获取输出流,写数据
        OutputStream os = socket.getOutputStream();
        os.write("aaaa".getBytes());

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

服务端流程:

①创建服务器端的Socket对象(ServerSocket)

ServerSocket(int port)

②监听客户端连接,返回一个Socket对象

Socket accpet()

③获取输入流,读数据,并把数据显示在控制台

InputStream getInputStream()

④释放资源

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

        //监听客户端连接,返回一个Socket对象
        Socket accept = ss.accept();

        //获取输入流,读数据,并把数据显示在控制台
        InputStream is = accept.getInputStream();
        int b;
        while((b=is.read())!=-1){
   
            System.out.println((char)b);
        }

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

6.综合练习

6.1综合练习1(多发多收)

客户端:多次发送数据

服务器:接收多次接收数据,并打印

客户端

public class Client {
   
    public static void main(String[] args) throws IOException {
   
        Socket socket = new Socket("127.0.0.1",10000);
        Scanner sc = new Scanner(System.in);
        OutputStream os = socket.getOutputStream();

        while (true) {
   
            System.out.println("请输入你要发送的信息");
            String str = sc.nextLine();
            os.write(str.getBytes());
            if("886".equals(str)) break;
        }
        socket.close();
    }
}

服务端

public class Server {
   
    public static void main(String[] args) throws IOException {
   
        ServerSocket ss = new ServerSocket(10000);
        //监听
        Socket accept = ss.accept();

        InputStreamReader isr = new InputStreamReader(accept.getInputStream());
        int b;
        while((b= isr.read())!=-1){
   
            System.out.print((char)b);
        }

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

6.2综合练习2(接收并反馈)

客户端:发送一条数据,接收服务端反馈的消息并打印

服务器:接收数据并打印,再给客户端反馈消息

客户端

public class Client {
   
    public static void main(String[] args) throws IOException {
   
        //创建Socket对象
        Socket socket = new Socket("127.0.0.1",10000);

        //发送数据
        OutputStream os = socket.getOutputStream();
        String str="见到你很高兴!";
        os.write(str.getBytes());

        //写一个结束标志
        socket.shutdownOutput();

        //接受服务器端返回数据
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while((b= isr.read())!=-1){
   
            System.out.println((char)b);
        }

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

服务端

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

        //监听客户端
        Socket socket = ss.accept();

        //接收来自客户端的数据
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while((b= isr.read())!=-1){
   
            System.out.println((char)b);
        }

        //对客户端进行反馈
        OutputStream os = socket.getOutputStream();
        String str="有多开心呢?";
        os.write(str.getBytes());

        socket.close();
        ss.close();
    }
}

6.3综合练习3(上传文件)

客户端:将本地文件上传到服务器。接收服务器的反馈。

服务器:接收客户端上传的文件,上传完毕之后给出反馈。

客户端

public class Client {
   
    public static void main(String[] args) throws IOException {
   
        //创建Socket
        Socket socket = new Socket("127.0.0.1",10000);
        //读本地文件的数据,然后发出去
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\ClientDir\\girl1.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        int len;
        byte[] bytes = new byte[1024*5];
        while((len=bis.read(bytes))!=-1){
   
            bos.write(bytes,0,len);
        }
        bis.close();

        //结束标志,很关键
        socket.shutdownOutput();

        //接收服务端反馈
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int B;
        while((B=isr.read())!=-1){
   
            System.out.println((char)B);
        }

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

服务器

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

        //监听客户端
        Socket socket = ss.accept();

        //接收客户端发来的数据,写到本地
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\ServerDIr\\girl1.jpg"));
        int len;
        byte[] bytes = new byte[1024*5];
        while((len=bis.read(bytes))!=-1){
   
            bos.write(bytes,0,len);
        }
        bos.close();

        //向客户端反馈
        BufferedOutputStream Bos = new BufferedOutputStream(socket.getOutputStream());
        Bos.write("上传成功".getBytes());
        Bos.flush(); // 刷新输出流,确保数据发送到客户端

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

6.4综合练习4(解决上一题文件名重复的问题)

我们可以用UUID这个类,来形成随机的文件名称

public class UUIDdemo {
   
    public static void main(String[] args) {
   
        String str = UUID.randomUUID().toString().replace("-","");
        System.out.println(str);
    }
}

修改上题的服务端代码,将文件名替换为随机的UUID

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

        //监听客户端
        Socket socket = ss.accept();

        //接收客户端发来的数据,写到本地
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        String name = UUID.randomUUID().toString().replace("-","");//随机的文件名
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\ServerDIr\\"+name+".jpg"));
        int len;
        byte[] bytes = new byte[1024*5];
        while((len=bis.read(bytes))!=-1){
   
            bos.write(bytes,0,len);
        }
        bos.close();

        //向客户端反馈
        BufferedOutputStream Bos = new BufferedOutputStream(socket.getOutputStream());
        Bos.write("上传成功".getBytes());
        Bos.flush(); // 刷新输出流,确保数据发送到客户端

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

6.5综合练习5(上传文件一一多线程版)

Runnable类

public class MyRunnable implements Runnable{
   
    Socket socket;

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

    @Override
    public void run() {
   
        try {
   
            //接收客户端发来的数据,写到本地
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            String name = UUID.randomUUID().toString().replace("-","");//随机的文件名
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\ServerDIr\\"+name+".jpg"));
            int len;
            byte[] bytes = new byte[1024*5];
            while((len=bis.read(bytes))!=-1){
   
                bos.write(bytes,0,len);
            }
            bos.close();

            //向客户端反馈
            BufferedOutputStream Bos = new BufferedOutputStream(socket.getOutputStream());
            Bos.write("上传成功".getBytes());
            Bos.flush(); // 刷新输出流,确保数据发送到客户端
        } catch (IOException e) {
   
            throw new RuntimeException(e);
        } finally {
   
            if (socket!=null) {
    //做一个非空判断
                try {
   
                    socket.close();
                } catch (IOException e) {
   
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

服务端

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

        while (true) {
   
            //监听客户端
            Socket socket = ss.accept();

            //开启一条线程,一个用户对应服务端一条线程
            new Thread(new MyRunnable(socket)).start();
        }
    }
}

6.6综合练习6(线程池优化)

频繁创建线程并销毁非常浪费系统资源,所以要用线程池优化,需要更改的只有服务端的代码

服务端

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

        //线程池
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                3,//核心线程数量,不能小于0
                16,//最大线程数,不能小于0,最大数量 >= 核心线程数量
                60,//空闲线程最大存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(3),//任务队列
                Executors.defaultThreadFactory(),//创建线程工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
        );

        while (true) {
   
            //监听客户端
            Socket socket = ss.accept();

            //开启一条线程,一个用户对应服务端一条线程
            poolExecutor.submit(new MyRunnable(socket));
        }
    }
}
目录
相关文章
|
JavaScript 前端开发
kettle从sftp下载多个文件并进行转换后输出
kettle从sftp下载多个文件并进行转换后输出
|
JavaScript 小程序
微信小程序 wxml 中使用 js函数
微信小程序 wxml 中使用 js函数
850 0
|
存储 SQL 消息中间件
ClickHouse(12)ClickHouse合并树MergeTree家族表引擎之AggregatingMergeTree详细解析
AggregatingMergeTree是ClickHouse的一种表引擎,它优化了MergeTree的合并逻辑,通过将相同主键(排序键)的行聚合为一行并存储聚合函数状态来减少行数。适用于增量数据聚合和物化视图。建表语法中涉及AggregateFunction和SimpleAggregateFunction类型。插入数据需使用带-State-的聚合函数,查询时使用GROUP BY和-Merge-。处理逻辑包括按排序键聚合、在合并分区时计算、以分区为单位聚合等。常用于物化视图配合普通MergeTree使用。查阅更多资料可访问相关链接。
630 4
|
Android开发 iOS开发
Flutter中获取监听屏幕方向、锁定屏幕方向
Flutter中获取监听屏幕方向、锁定屏幕方向
447 2
|
12月前
|
JavaScript
vue 中 axios 的安装及使用
本文介绍了在Vue项目中安装和使用axios的方法。首先通过命令`npm install axios --save-dev`安装axios,然后在组件的`created`生命周期钩子中使用`axios.get`异步获取数据,并将获取的数据更新到组件的`data`中。文中提供了完整的示例代码,包括安装命令、验证安装成功的步骤、Vue组件的模板、脚本和样式。
vue 中 axios 的安装及使用
|
人工智能 安全 API
本地部署马斯克开源Grok-1大模型,贝锐花生壳3步实现远程访问
在人工智能领域,除了在线工具如ChatGPT和Midjourney,本地部署的AI工具如Stable Diffusion同样重要,尤其在满足定制需求和确保数据安全方面。以马斯克的xAI开源项目Grok-1为例,这款拥有314B参数的大模型需要高性能硬件支持。借助贝锐花生壳的内网穿透技术,用户可通过简单的三步操作实现对这类AI工具的远程访问:安装并登录花生壳客户端、设置内网穿透映射、生成并使用远程访问地址。花生壳提供的HTTPS映射确保了访问的安全性,使远程使用AI工具变得更加便捷和安全。
278 1
|
缓存 JSON JavaScript
深入理解RESTful API设计原则与最佳实践
- REST是一种基于HTTP的Web服务设计风格,强调资源、统一接口和无状态性。 - 设计原则:统一接口(资源标识、操作、自描述消息、无状态),资源中心,标准方法,分层系统和缓存。 - 最佳实践:版本控制、JSON格式、有意义的状态码、HATEOAS和安全性(HTTPS,认证,授权)。 - 示例:使用Node.js和Express实现用户管理API,包括GET、POST、PUT和DELETE操作,展示资源操作的基本实现。 代码示例展示了如何创建、读取、更新和删除用户资源,以及处理HTTP状态码和错误情况。实际应用时,需进一步完善安全和性能优化。
1885 0
|
存储 Kubernetes 容器
K8S中使用nfs作为存储卷
K8S中使用nfs作为存储卷
140 0
阿里云代金券在哪领取?领券中心入口
阿里云优惠券在哪领取?在活动中心的领券中心即可领取当前最新可用的代金券:
434 0