11---网络基础 + IP地址类InetAddress + UDP传输数据 + TCP传输数据

本文介绍了网络编程的基础知识,包括两种模式、网络编程三要素和网络通信协议。详细讲解了UDP和TCP协议,以及IP地址类InetAddress的使用。还通过实例演示了UDP和TCP数据的传输过程,包括DatagramSocket、DatagramPacket、Socket和ServerSocket的使用,并提供了文件上传的案例练习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、网络入门

1、两种模式
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构
2、网络编程三要素
1.网络协议
2.IP地址
3.端口号
3、网络通信协议

​ 不同计算机传输数据的规则

(1)UDP协议,属于传输层协议
UDP: 相当于寄快递,传输数据不安全
    1.不需要连接
    2.数据可能会丢失
    3.速度快
    4.每个包最大64kb
应用场景:
                1.NBA直播
                2.局域网的游戏
(2)TCP协议,属于应用层协议
TCP: 打电话, 传输数据安全
    1.需要连接
    2.数据不会丢失
    3.速度慢
    4.建立连接后是以流的形式传输,没有大小限制

应用场景:
        1.支付宝扫码付钱
        2.聊天程序.
        3.下载
4、IP地址

(1)概念:

​ Internet Protocal Address: 互联网协议地址,每台联网的计算机都会分配一个IP地址(相当于家庭住址)。

(2)作用:

​ 通过IP地址就可以找到某台电脑

(3)分类:

IPV4:点分十进制,例如192.168.84.100  范围:每段的范围0~255
IPV6:冒分十六进制 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789

(4)特殊的ip地址

​ 127.0.0.1: 找本机(本地回环地址)

(5)关于IP的DOS命令行命令

​ a、查看本机ip地址: ipconfig

​ b、测试网络是否通畅:ping IP地址/域名/网址

5、端口

(1)概念:

​ 每个联网的程序都会使用一个端口号,通过端口号可以找到电脑上的某个程序(房间号)

(2)端口号范围:

​ 一台电脑端口号的范围:0~65535, 0~1024被操作系统或知名软件使用了.我们使用1024以后的。只有程序运行的起来的时候才会使用端口号

2、IP地址类:InetAddress

(1)概念

​ 代表一个IP地址对象

(2)构造方法(都是静态方法
static InetAddress getLocalHost() 获取【本机】的IP地址
static InetAddress getByName(String host) 通过【IP地址/域名字符串】得到InetAddress对象
(2)成员方法
String getHostAddress() 返回IP地址字符串
String getHostName() 返回主机名
public static void main(String[] args) throws UnknownHostException {
    //构造方法1
    // static InetAddress getLocalHost() 获取本机的IP地址
    InetAddress localHost = InetAddress.getLocalHost();
    System.out.println("localHost = " + localHost); // "DESKTOP-6MABD4P/192.168.84.99"

    //普通方法
    System.out.println("getHostAddress:" + localHost.getHostAddress()); // 192.168.84.99
    System.out.println("getHostName: " + localHost.getHostName()); // DESKTOP-6MABD4P

    //构造方法2
    // static InetAddress getByName(String host) 通过IP地址/域名字符串得到InetAddress对象
    InetAddress byName = InetAddress.getByName("192.168.84.99");
    System.out.println("byName = " + byName);

    InetAddress bxg = InetAddress.getByName("ntlias3.boxuegu.com");
    System.out.println("bxg = " + bxg);
}

3、UDP传输数据

1、UDP传输数据,需要:

​ 发送端,接收端以及他们的数据包

2、发送端 + 接收端—DatagramSocket类

DatagramSocket类的API

//构造方法
DatagramSocket() //创建一个UDP【发送端】,随机绑定一个端口  (UDP发送端)
DatagramSocket(int port) //创建一个UDP【接收端】,绑定要指定的端口 (UDP接收端)
成员方法
void send(DatagramPacket p) //发送数据的
void receive(DatagramPacket p) //接收数据
3、数据包—DatagramPacket类

DatagramPacket类的API

//构造方法
DatagramPacket(byte[] buf, int length) //创建空的数据包,用来接收数据的(接收数据的包)
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) //创建一个有数据的数据包,用来发送的
    
//成员方法
int getLength()//得到包已接收到的数据长度
String getAddress();//得到发送端的IP
String getPort();//得到发送端的端口号
4、UDP数据传输的实现步骤及其代码
(1)建立发送端步骤:
1.创建发送端
2.创建一个【有数据的】数据包(DatagramPacket)(创建一封信)
3.调用发送端的【发送方法】
4.关闭资源
(2)建立接收端步骤:
1.创建一个接收端
2.创建一个【空的】数据包
3.调用接收端的【接收方法】
4.操作数据
5.关闭资源

注意

接收端一定要有固定的端口让发送端能够找到
先运行接收端,后运行发送端
(3)发送端代码实现
public static void main(String[] args) throws IOException {
    System.out.println("发送端启动啦!");
    // UDP发送端(DatagramSocket)
    // 1.创建发送端
    // DatagramSocket() 创建一个UDP发送端,随机绑定一个端口  (UDP发送端)
    DatagramSocket socket = new DatagramSocket();

    // 2.创建一个数据包(DatagramPacket)(创建一封信)
    String str = "你好,约吗?";
    byte[] bytes = str.getBytes();
    DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getLocalHost(), 6666);

    // 3.调用发送端的发送方法
    // void send(DatagramPacket p) 发送数据的
    socket.send(packet);

    // 4.关闭资源
    socket.close();
}
(4)接收端代码实现
public static void main(String[] args) throws IOException {
    System.out.println("接收端启动啦!");
    // 1.创建一个接收端
    // DatagramSocket(int port) 创建一个UDP发送端,绑定要指定的端口 (UDP接收端)
    DatagramSocket socket = new DatagramSocket(6666);

    // 2.创建一个空的数据包
    byte[] buf = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buf, buf.length);

    // 3.调用接收端的接收方法
    socket.receive(packet);

    // 4.操作数据
    // 得到接收的数据的数量
    int length = packet.getLength();
    System.out.println("接收到的数据: " + new String(buf, 0, length));
    // 得到发送者的IP和端口
    System.out.println("发送者IP:" + packet.getAddress());
    System.out.println("发送者端口: " + packet.getPort());

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

4、TCP传输数据

1、TCP传输数据,需要:

​ 客户端和服务端

2、客户端—Socket类
(1)构造方法
Socket(String host, int port) 创建客户端并连接指定主机的指定端口
(2)普通方法(这里的普通方法虽然属于Socket类,但是服务端同样可以使用)
InputStream getInputStream() 得到输入流
OutputStream getOutputStream() 得到输出流
3、服务端—ServerSocket类
(1)构造方法:
ServerSocket(int port) 创建一个服务端,绑定要指定的端口
(2)普通方法
Socket accept() 同意客户端的请求
4、TCP数据传输的实现步骤及其代码
(1)建立客户端步骤
1.连接服务端
2.得到输出流
3.写数据
4.得到输入流
5.读取数据
6.关闭资源
(2)建立服务端步骤
1.创建服务端
2.等待客户端的连接,并同意
3.得到输入流读取数据
4.得到输出流写数据
5.关闭资源

注意

客户端和服务端指定的端口必须一致
先运行服务端,后运行客户端

在这里插入图片描述

(3)客户端代码实现
public static void main(String[] args) throws IOException {
    System.out.println("客户端启动啦!");
    // 1.连接服务端
    // Socket(String host, int port) 创建客户端并连接指定主机的指定端口
    Socket socket = new Socket("127.0.0.1", 10086);
    // 2.得到输出流
    OutputStream out = socket.getOutputStream();

    // 3.写数据
    out.write("你好,约吗???".getBytes());

    // 4.得到输入流
    InputStream in = socket.getInputStream();

    // 5.读取数据
    byte[] buf = new byte[1024];
    int len = in.read(buf);
    System.out.println("客户端收到数据: " + new String(buf, 0, len));

    // 6.关闭资源
    in.close();
    out.close();
    socket.close();
}
(4)服务端代码实现
public static void main(String[] args) throws IOException {
    System.out.println("服务端启动啦!");
    // 1.创建服务端
    // ServerSocket(int port) 创建一个服务端,绑定要指定的端口
    ServerSocket ss = new ServerSocket(10086);
    // 2.等待客户端的连接,并同意
    Socket socket = ss.accept();

    // 3.得到输入流读取数据
    InputStream in = socket.getInputStream();
    byte[] buf = new byte[1024];
    int len = in.read(buf);
    System.out.println("服务端收到: " + new String(buf, 0, len));

    // 4.得到输出流写数据
    OutputStream out = socket.getOutputStream();
    out.write("老地方见".getBytes());

    // 5.关闭资源
    out.close();
    in.close();
    socket.close();
    ss.close();
}
5、案例练习:文件上传

​ 由于是文件上传,必须准确接受数据,故使用TCP传输数据

在这里插入图片描述

注意

客户端代码中解决了文件上传不会停止的问题:socket.shutdownOutput();语句

f服务端代码中解决了上传文件名冲突的问题:让文件名和时间的毫秒值相关,再添加随机数,上传者的ip

服务端

public static void main(String[] args) throws IOException {
    System.out.println("文件上传服务端启动啦!");
    // 1.创建服务端
    ServerSocket ss = new ServerSocket(9999);
    // 2.同意客户端的请求
    Socket socket = ss.accept();

    // 3.得到输入流
    InputStream in = socket.getInputStream();

    // 让文件名和时间的毫秒值相关,再添加随机数,上传者的ip
    String ip = socket.getInetAddress().getHostAddress();
    String fileName = System.currentTimeMillis() + "_" + new Random().nextInt(88888888) + "_" + ip + ".png";
    // 4.创建文件输出流
    FileOutputStream fos = new FileOutputStream("day11demo/upload/" + fileName);

    // 5.循环读写数据
    int len;
    byte[] buf = new byte[1024];
    while ((len = in.read(buf)) != -1) {
        fos.write(buf, 0, len);
    }
    System.out.println("服务端上传完成!");

    // 6.得到输出流
    OutputStream out = socket.getOutputStream();
    // 7.发送数据给客户端
    out.write("上传完成啦啦啦啦!".getBytes());

    // 8.关闭资源
    out.close();
    fos.close();
    in.close();
    socket.close();
    ss.close();
}

客户端

public static void main(String[] args) throws IOException {
    // 1.创建客户端
    Socket socket = new Socket("127.0.0.1", 9999);
    // 2.得到Socket的输出流
    OutputStream out = socket.getOutputStream();
    // 3.创建文件输入流
    FileInputStream fis = new FileInputStream("c:/MyFileTest/xyz.png");
    // 4.循环读写数据
    int len;
    byte[] buf = new byte[1024];
    while ((len = fis.read(buf)) != -1) {
        out.write(buf, 0, len);
    }
    // 文件发送完毕了.给socket输出流写一个结束标记
    socket.shutdownOutput();
    System.out.println("客户端上传完成!");

    // 5.得到Socket的输入流
    InputStream in = socket.getInputStream();

    // 6.读取数据
    byte[] buf2 = new byte[1024];
    int len2 = in.read(buf2);
    System.out.println("客户端收到: " + new String(buf2, 0, len2));

    // 7.关闭资源
    in.close();
    fis.close();
    out.close();
    socket.close();
}
6、案例练习:文件上传多线程版本
1.如何实现多人上传?
    添加了while循环让服务端可以多个同意客户端的请求和上传(while循环实现多人上传)

2.如何实现同时上传
    采用多线程(多线程实现同时上传)
    主线程负责3个任务:
    1、循环同意客户端的请求任务 
    2、一旦同意客户端的请求后就创建一个新的线程执行上传的任务
    3、run方法外面的while循环任务

服务端

public static void main(String[] args) throws IOException {
    System.out.println("文件上传服务端启动啦!");
    // 1.创建服务端//让主线程main“做同意”的任务(此外主线程还做while循环的任务)
    ServerSocket ss = new ServerSocket(9999);

    while (true) {
        // 2.同意客户端的请求
        Socket socket = ss.accept();

        // 创建一个新的线程执行上传的任务
        UploadTask task = new UploadTask(socket);
        new Thread(task).start();
    }
}

客户端

public static void main(String[] args) throws IOException {
    // 1.创建客户端
    Socket socket = new Socket("127.0.0.1", 9999);
    // 2.得到Socket的输出流
    OutputStream out = socket.getOutputStream();
    // 3.创建文件输入流
    FileInputStream fis = new FileInputStream("c:/MyFileTest/xyz.png");
    // 4.循环读写数据
    int len;
    byte[] buf = new byte[1024];
    while ((len = fis.read(buf)) != -1) {
        out.write(buf, 0, len);
    }
    // 文件发送完毕了.给socket输出流写一个结束标记
    socket.shutdownOutput();
    System.out.println("客户端上传完成!");

    // 5.得到Socket的输入流
    InputStream in = socket.getInputStream();

    // 6.读取数据
    byte[] buf2 = new byte[1024];
    int len2 = in.read(buf2);
    System.out.println("客户端收到: " + new String(buf2, 0, len2));

    // 7.关闭资源
    in.close();
    fis.close();
    out.close();
    socket.close();
}

多线程

public class UploadTask implements Runnable {
    private Socket socket;//让主线程main“做同意”的任务(此外主线程还做while循环的任务)

    //由于服务端需要客户端的socket,但是run方法不能传参数,故解决办法是: 定义成员变量socket,并用构造方法得到该参数
    public UploadTask(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {//run方法不能抛异常,必须try...catch
        try {
            // 上传的代码放到新的线程中执行
            // 3.得到输入流
            InputStream in = socket.getInputStream();
            // 让文件名和时间的毫秒值相关,再添加随机数,上传者的ip
            String ip = socket.getInetAddress().getHostAddress();
            String fileName = System.currentTimeMillis() + "_" + new Random().nextInt(88888888) + "_" + ip + ".png";
            // 4.创建文件输出流
            FileOutputStream fos = new FileOutputStream("day11demo/upload/" + fileName);
            // 5.循环读写数据
            int len;
            byte[] buf = new byte[1024];
            while ((len = in.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            System.out.println("服务端上传完成!");
            // 6.得到输出流
            OutputStream out = socket.getOutputStream();
            // 7.发送数据给客户端
            out.write("上传完成啦啦啦啦!".getBytes());

            // 8.关闭资源
            out.close();
            fos.close();
            in.close();
            socket.close();
        } catch (Exception e) {
            System.out.println("上传失败");
        }
    }
}
7、案例练习:模拟网站服务器=
浏览器(Browser)现成的:
    地址栏输入的:http://127.0.0.1:8888/web/index.html
    http: 网络协议,可以传输文本图片,底层就是TCP协议
    127.0.0.1: 服务器的IP地址
    8888: 服务器的端口号
    web/index.html: 找服务器要这个资源(或者说客户在浏览器对服务器发出要这个资源的信息)


服务器(Server)我们要编写的 ServerSocket
    1.得到浏览器想要什么数据
    2.返回浏览器想要的数据

模拟网站服务器的核心要领

​ 1、浏览器要什么数据,服务器就给什么数据

​ 2、底层是TCP协议

public static void main(String[] args) throws IOException {
    System.out.println("服务器启动啦");
    ServerSocket ss = new ServerSocket(8888);

    while (true) {
        Socket socket = ss.accept();
        // 使用多线给浏览器返回数据

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 1.得到浏览器想要什么数据(找到它想要的数据所在的本机的路径)
                    InputStream in = socket.getInputStream();
                    System.out.println(socket.getInetAddress());//输出了一堆数据,但是我们只想要第一行数据

                    // 得到【一行数据】,需要使用字符输入缓冲流: GET /web/index.html HTTP/1.1
                    // BufferedReader(Reader r); 构造方法中需要字符流,我们socket中得到的是字节流
                    // 要将字节流转成字符流?转换流可以做这个事情
                    InputStreamReader isr = new InputStreamReader(in);//把字节流转换为字符流
                    BufferedReader br = new BufferedReader(isr);//得到字符输入缓冲流
                    String line = br.readLine(); // 读取到"GET /web/index.html HTTP/1.1"
                    //但我们只想要 index.html HTTP,故:
                    // 使用空格分割数据 -> split(" ") -> {"GET", "/web/index.html", "HTTP/1.1"}

                    String fileName = line.split(" ")[1];//获取分割的字符串数组的第二个数据
                    // System.out.println("fileName = " + fileName); // /web/index.html
                    String path = "day11demo" + fileName; // day11demo/web/index.html
                    System.out.println("path = " + path);

                    // 2.返回浏览器想要的数据
                    OutputStream out = socket.getOutputStream();
                    // 固定套路
                    out.write("HTTP/1.1 200 OK\r\n".getBytes());
                    out.write("Content-Type:text/html\r\n".getBytes());
                    out.write("\r\n".getBytes());

                    // 读取文件中的数据写给浏览器
                    FileInputStream fis = new FileInputStream(path);
                    // 循环读写
                    int len;
                    byte[] buf = new byte[1024 * 8];
                    while ((len = fis.read(buf)) != -1) {
                        out.write(buf, 0, len);
                    }

                    fis.close();
                    out.close();
                    in.close();
                    socket.close();
                } catch (Exception e) {
                    System.out.println("请求失败");
                }
            }
        }).start();

    }
}

\n".getBytes());

                // 读取文件中的数据写给浏览器
                FileInputStream fis = new FileInputStream(path);
                // 循环读写
                int len;
                byte[] buf = new byte[1024 * 8];
                while ((len = fis.read(buf)) != -1) {
                    out.write(buf, 0, len);
                }

                fis.close();
                out.close();
                in.close();
                socket.close();
            } catch (Exception e) {
                System.out.println("请求失败");
            }
        }
    }).start();

}

}














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值