Mushroom Notes Mushroom Notes
🍄首页
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
    • 设计模式
  • JavaEE

    • Servlet
    • JDBC
    • 会话技术
    • 过滤器监听器
    • 三层架构
  • JDK

    • 总览
  • JVM

    • 总览
  • 常用mate
  • CSS
  • JavaScript
  • rds 数据库

    • MySQL
    • MySQL 进阶
    • MySQL 库表规范
  • nosql 数据库

    • Redis
    • Redis 进阶
    • Redis 底层
    • MongoDB
  • Spring生态

    • Spring
    • Spring MVC
    • Spring boot
    • Spring Validation
  • Spring Cloud生态

    • Spring Cloud
    • 服务治理
    • 远程调用
    • 网关路由
    • 服务保护
    • 分布式事务
    • 消息中间件
  • 数据库

    • Mybatis
    • Mybatis Plus
    • Elasticsearch
    • Redisson
  • 通信

    • Netty
📚技术
  • 方案专题
  • 算法专题
  • BUG专题
  • 安装专题
  • 网安专题
  • 面试专题
  • 常用网站
  • 后端常用
  • 前端常用
  • 分类
  • 标签
  • 归档

kinoko

一位兴趣使然的热心码农
🍄首页
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
    • 设计模式
  • JavaEE

    • Servlet
    • JDBC
    • 会话技术
    • 过滤器监听器
    • 三层架构
  • JDK

    • 总览
  • JVM

    • 总览
  • 常用mate
  • CSS
  • JavaScript
  • rds 数据库

    • MySQL
    • MySQL 进阶
    • MySQL 库表规范
  • nosql 数据库

    • Redis
    • Redis 进阶
    • Redis 底层
    • MongoDB
  • Spring生态

    • Spring
    • Spring MVC
    • Spring boot
    • Spring Validation
  • Spring Cloud生态

    • Spring Cloud
    • 服务治理
    • 远程调用
    • 网关路由
    • 服务保护
    • 分布式事务
    • 消息中间件
  • 数据库

    • Mybatis
    • Mybatis Plus
    • Elasticsearch
    • Redisson
  • 通信

    • Netty
📚技术
  • 方案专题
  • 算法专题
  • BUG专题
  • 安装专题
  • 网安专题
  • 面试专题
  • 常用网站
  • 后端常用
  • 前端常用
  • 分类
  • 标签
  • 归档
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
      • IP地址
      • 端口
      • 协议
      • UDP通信
      • TCP通信
      • NIO
    • 设计模式
  • JavaEE

  • JDK版本特性

  • JVM

  • Java
  • JavaSE
kinoko
2023-12-17
目录

网络编程


什么是网络编程?

编写在不同计算机上进行数据传输的程序

网络编程应用场景

网络应用程序、即时通信、网游对战、金融证券、国际贸易、邮件等。

常见软件架构:

  • **Client-Server(CS):**客户端-服务端模式
  • **Browser/Server(BS):**浏览器-服务器模式

**网络编程三要素:**IP地址、端口号、协议

# IP地址


概念:互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给网络中的计算机进行编号 **作用:**通过IP地址可以找到网络中的某台电脑;IP地址就相当于生活中的家庭住址

分类

IPv4:是一个32位的二进制数,通常被分为4个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。

IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。有资料显示,全球IPv4地址在2011年2月分配完毕。为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,分成8组以十六进制数显示,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。

查看IP地址相关指令

  • **查看本机IP地址:**在DOS命令行输入ipconfig
  • **检查网络是否连通:**在DOS命令行输入ping IP地址/域名

特殊IP地址127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用

Java操作IP地址的相关API(InetAddress类)

方法 说明
static InetAddress getLocalhost() 获取本机的IP地址对象
public static InetAddress getByName(String host) 设置主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
public String getHostName() 获取此IP地址的主机名
public String getHostAddress() 返回文本显示中的IP地址字符串

# 端口


概述

端口是应用程序在设备中唯一的标识。我们一台电脑上会安装很多的应用程序,内网通,微信,QQ。我们可以通过端口找到计算机中的某个程序。

**作用:**通过端口号可以找到电脑上的某个程序

端口号的取值范围

端口号一般是用两个字节表示的整数,它的取值范围是0~65535。其中0~1023之间的端口号用于一些知名的网络服务或者应用。我们自己使用1024以上的端口号就可以了。

# 协议


**定义:**计算机网络中,连接和通信的规则被称为网络通信协议。

网络通信协议的两套参考模型

  • OSI参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广。
  • TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。

image.png

# UDP通信


UDP协议特点: 用户数据报协议(User Datagram Protocol)

  • 不需要连接
  • 速度快
  • 有大小限制一次最多发送64K
  • 易丢失数据

UDP协议通信场景(速度要求高,数据完整性要求不高)

  • 直播
  • 语音通话
  • 视频会话

UDP通信流程 image.png DatagramPacket常用方法

方法 说明
int getLength ( ) 返回要发送的数据的长度或接收到的数据的长度。
InetAddress getAddress ( ) 返回该数据报发送或接收数据报的计算机的InetAddress对象。
int getPort ( ) 返回发送数据报的远程主机上的端口号,或从中接收数据报的端口号。

**注意:**发送端无需声明IP端口信息,只有接收端需要,就像发快递一样

示例

public class UDPSender {

    public static void main(String[] args) throws IOException {
        System.out.println("发送端开启");
        // 创建发送端
        DatagramSocket socket = new DatagramSocket();
        // 发送的数据
        byte[] massage = "约吗?".getBytes();
        // 创建数据包
        DatagramPacket data = new DatagramPacket(
            massage, 0, massage.length, InetAddress.getByName("192.168.73.75"), 6666);
        // 发送数据
        socket.send(data);
        socket.close();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UDPReceiver {

    public static void main(String[] args) throws IOException {
        System.out.println("接收端开启");
        // 创建接收端
        DatagramSocket socket = new DatagramSocket(6666);
        // 保存接收的数据
        byte[] massage = new byte[1024 * 8];
        // 创建数据包接收数据,将接收的数据放入数组中
        DatagramPacket data = new DatagramPacket(massage, 0, massage.length);
        // 接收数据
        socket.receive(data);
        // 获取数据包的IP地址
        String ip = data.getAddress().getHostAddress();
        // 获取数据包的端口号
        int port = data.getPort();
        // 接收了多少打印多少
        System.out.println(ip + ":" + port + '\t' + 
                           new String(massage, 0, data.getLength()));
        socket.close();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# TCP通信


TCP协议特点: 传输控制协议 (Transmission Control Protocol)

  • 需要连接
  • 速度慢
  • 没有大小限制
  • 不易丢失数据

TCP协议通信场景(速度要求不高,数据完整性要求高)

  • 下载
  • 扫码支付
  • 金融等数据通信。

TCP通信流程 image.png Socket常用构造

方法 说明
Socket (String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号。
Socket (InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。

Socket常用方法

方法 说明
OutputStream getOutputStream( ) 返回此套接字的输出流。
InputStream getInputStream( ) 返回此套接字的输入流。
InetAddress getInetAddress( ) 返回套接字所连接的InetAddress对象。
SocketAddress getRemoteSocketAddress( ) 返回此套接字连接到的端点的地址,如果未连接,则 null
void shutdownInput( ) 将此套接字的输入流放置在“流的末尾”。
void shutdownOutput( ) 断开此套接字的输出流。
void close( ) 关闭此套接字

**注意:**Socket的InputStream的read()方法,如果read读不到数据,不会返回-1,而是一直等待,直到客户端调用shutdownOutput()断开输出流连接,相当于传输了一个-1

ServerSocket常用构造

方法 说明
ServerSocket(int port) 创建绑定到指定端口的服务器套接字。

ServerSocket常用方法

方法 说明
Socket accept( ) 侦听要连接到此套接字并接受它。
void close( ) 关闭此套接字

示例

public class TCPreceiver {

    public static void main(String[] args) throws IOException {
        // 创建服务端
        ServerSocket serverSocket = new ServerSocket(10086);
        System.out.println("服务端开启");
        // 与客户端建立连接,获取客户端
        Socket socket = serverSocket.accept();
        // 获取客户端输入流
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024 * 8];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        // 获取客户端输出流
        OutputStream os = socket.getOutputStream();
        os.write("服务端收到".getBytes());
        // 关闭流
        os.close();
        is.close();
        socket.close();
        serverSocket.close();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TCPsender {

    public static void main(String[] args) throws IOException {
        // 建立客户端
        Socket socket = new Socket("127.0.0.1", 10086);
        System.out.println("客户端开启");
        // 获取输出流
        OutputStream os = socket.getOutputStream();
        os.write("呼叫服务端".getBytes());
        // 获取输入流
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024 * 8];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        is.close();
        os.close();
        socket.close();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

多人同时文件上传案例

客户端

public class TCPsender {

    public static void main(String[] args) throws IOException {
        System.out.println("客户端开启");
        // 建立客户端,指定连接目的地
        Socket socket = new Socket("127.0.0.1", 10086);
        // 获取输出流
        OutputStream os = socket.getOutputStream();
        // 建立文件字节输入流读取文件
        FileInputStream fis = new FileInputStream("D:\\IDEAproject\\MyFileTest\\img.jpg");
        byte[] bytes = new byte[1024 * 8];
        int len;
        while ((len = fis.read(bytes)) != -1) {
            os.write(bytes, 0, len);
        }
        // 断开输出流与服务端的连接
        socket.shutdownOutput();
        // 获取输入流
        InputStream is = socket.getInputStream();
        len = is.read(bytes);
        System.out.println(new String(bytes, 0, len));
        is.close();
        os.close();
        socket.close();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

服务端

public class TCPreceiver {

    public static void main(String[] args) throws IOException {
        // 创建服务端
        ServerSocket serverSocket = new ServerSocket(10086);
        System.out.println("服务端开启");

        while (true) {
            // 与客户端建立连接,获取客户端
            Socket socket = serverSocket.accept();
            // 创建连接池
            ThreadPoolExecutor pool = new ThreadPoolExecutor(
                    5,
                    10,
                    100,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(10),
                    Executors.defaultThreadFactory()
            );
            // 获取连接的客户端信息
            System.out.println(socket.getRemoteSocketAddress()+"已连接...");
            // 服务端连接到客户端后就将业务与客户端提交到线程池
            UpLoadRunnable ulr = new UpLoadRunnable(socket);
            pool.submit(ulr);
        }

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

多线程实现

public class UpLoadRunnable implements Runnable{

    Socket socket = new Socket();

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

    @Override
    public void run() {
        try {
            // 获取客户端输入流
            InputStream is = socket.getInputStream();
            // 文件重名措施
            String uuid = UUID.randomUUID().toString();
            // 创建文件字节输出流
            FileOutputStream fos = 
                new FileOutputStream("Ex_Test\\TestFile\\upload\\" + uuid + ".jpg");
            byte[] bytes = new byte[1024 * 8];
            int len;
            while ((len = is.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
            System.out.println(uuid + "文件上传成功");
            // 获取客户端输出流
            OutputStream os = socket.getOutputStream();
            os.write("文件上传完成".getBytes());
            // 关闭流
            os.close();
            fos.close();
            is.close();
            socket.close();
        } catch (IOException e) {
            System.out.println("文件上传失败: "+e.toString());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

使用TCP模拟B/S服务器

public class WebServer {
    
    public static void main(String[] args) throws IOException {
        // 创建服务端
        ServerSocket serverSocket = new ServerSocket(9799);
        while (true) {
            // 服务端同意请求
            Socket socket = serverSocket.accept();
            // 获取输出流
            OutputStream out = socket.getOutputStream();
            // http协议固定需要回复浏览器的东西
            out.write("HTTP/1.1 200 OK\r\n".getBytes());
            out.write("Content-Type:text/html;charset=utf-8\r\n".getBytes());
            out.write("\r\n".getBytes());
            // 返回数据到浏览器
            out.write("<h1 align='center'>欢迎使用服务器<h1>".getBytes());
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# NIO


概述

JDK1.4以前: InputStream/OutputStream称为BIO(Blocking IO) 阻塞式IO JDK1.4推出了一套新的IO体系称为NIO (New IO/ Not Blocking IO) 非阻塞式IO

阻塞、非阻塞概念

阻塞和非阻塞都是处理IO数据的方式

  • 阻塞:如果没有数据就一直等待
  • 非阻塞:如果没有数据,不会一直等待,可以做其他事情

非阻塞的好处:不需要一直等待,当有数据来才需要处理,没有数据可以做其他操作

NIO中的三个角色

  • Channel(通道):可以双向传输数据

  • ByteBuffer: 相当于之前BIO的byte[],可以保存要发送和接收的数据,由于byte[]是基于java的创建,内存空间是在java的堆内存中,而ByteBuffer是基于操作系统的,所以效率比byte[]要高,并且ByteBuffer已经被封装成了java中的一个类,新增了很多方法,功能更强大

  • Selector 选择器(相等于门卫大爷):可以管理多个连接

NIO使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势

优点: 可以提高IO效率

缺点: 代码较为繁琐

#JavaWeb
上次更新: 2023/12/29 11:32:56
反射
设计模式

← 反射 设计模式→

最近更新
01
JVM 底层
09-13
02
JVM 理论
09-13
03
JVM 应用
09-13
更多文章>
Theme by Vdoing | Copyright © 2022-2024 kinoko | MIT License | 粤ICP备2024165634号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式