java - 网络编程之TCP、UDP和URL

本文详细介绍了Java的网络编程,包括IP地址与端口号的使用,TCP和UDP协议的特点及代码示例,以及URL的构成与操作。通过示例展示了TCP的三次握手、文件传输和回复机制,以及UDP的无连接数据报发送与接收。同时,文章还涵盖了URL的基本结构和资源下载方法。

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

Java - 网络编程概述

1. 概述

  • java是Internet上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序
  • Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在Java的本级安装系统里,由JVM进行控制。
    并且Java实现了一个跨平台的网卡库,程序员面对的是一个统一的网络编程环境。

2. 网络通信要素要素1:IP地址和端口号

import org.junit.Test;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @ClassName: InetAddressTest
 * @Description: Java - 网络编程测试
 * @author: Mr.zhi
 * @version: v1.0
 * @data: 2022/3/19 17:31
 * @node:
 *          1. 在Java中使用InetAddress类表示IP
 *          2. 可以通过InetAddress.getByName("192.168.0.1"); 来获取IP地址
 *          3. 使用域名:www.baidu.com  www.sina.com
 *          4. 本级IP:loaclhost / 127.0.0.1
 *          5. 如何实例化InetAddress:
 *              5.1 getByName(String host)
 *              5.2 getLocalHost();
 *           6. 两个常用方法:getHostName() / getHostAddress() : 获取域名和Ip地址
 *
 *          7. 端口号:要求不同的进程有不同的端口号:0~65535
 *
 */
public class InetAddressTest {
    @Test
    public void InetAddressTest01() {

        try {
            // 网络IP地址获取测试1
            InetAddress inet1 = InetAddress.getByName("192.168.0.1");
            System.out.println(inet1);

            InetAddress[] inet2 = InetAddress.getAllByName("www.baidu.com");
            for (int i = 0; i < inet2.length; ++i) {
                System.out.println(inet2[i]);
            }

            InetAddress inet3 = InetAddress.getByName("127.0.0.1");
            System.out.println(inet3);

            // 可通过 直接获取本地的IP地址
            InetAddress inet4 = InetAddress.getLocalHost();
            System.out.println(inet4);

            // getHostName() : 获取Ip地址的域名
            System.out.println(inet2[0].getHostName());

            // getHostAddress() : 获取域名的IP地址
            System.out.println(inet4.getHostName());
            System.out.println(inet4.getHostAddress());

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

    }

}

3. 通信要素2:网络协议

3.1 TCP

  • 使用TCP协议前,须先建立TCP连接,形成传输数据通道
  • 传输前,采用“三次握手”方式,点对点通信,是可靠的
  • TCP协议进行通信的两个应用进程:客户端、服务器
  • 在连接中可进行大数据量的传输
  • 传输完毕后,需释放已建立的连接,效率低

3.2 UDP

  • 将数据、源、目的封装成数据包,不需要建立连接
  • 每个数据报的大小限制在64K内
  • 发送不管对方会是否准备好,接收方收到也不确认,故为不可靠的
  • 可以广播发送
  • 发送数据结束时无序释放资源,开销小,速度快

3.3 TCP代码测试

  • Server端
public void server() {
    ServerSocket ssc = null;
    Socket sc = null;
    InputStream is = null;
    ByteArrayOutputStream bios = null;
    try {
        // 1. 创建服务端Socket,必须指定端口号,否则无法创建Socket套接字
        ssc = new ServerSocket(7859);

        // 2. 接收客户端的socket
        sc = ssc.accept();

        // 3. 获取输入流
        is = sc.getInputStream();
        bios = new ByteArrayOutputStream();
        byte[] buffer = new byte[100];
        int len = -1;

        while ((len = is.read(buffer)) != -1) {
            bios.write(buffer, 0, len);
        }

        // 4. 打印数据
        if (bios.size() != 0) {
            System.out.print("服务器接收到:" + sc.getInetAddress().getHostAddress() + " 发送过来的数据: ");
            System.out.println(bios);
            bios.reset();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 5. 关闭流和套接字
        if (bios != null) {
            try {
                bios.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (sc != null) {
            try {
                sc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (ssc != null) {
            try {
                ssc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • client端
public void client() {
    Socket sc = null; // 指明IP地址和端口号
    OutputStream os = null;
    try {
        // 1. 创建Socket对象,指明服务器端的IP地址和端口号
        InetAddress addr = InetAddress.getByName("127.0.0.1");
        sc = new Socket(addr, 7859);
    
        // 2. 获取一个输出流,用于输出数据
        os = sc.getOutputStream();
    
        // 3. 写出数据
        // 循环写出数据,直到输入exit
        StringBuilder stringBuilder = null;
        os.write(new String("栋梁栋梁!这里是TCP的Client客户端,收到请回复!").getBytes(StandardCharsets.UTF_8));

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 4. 关闭Socket对象
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        if (sc != null) {
            try {
                sc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.4 TCP测试二:实现文件的复制

// note: 在实际开发中,一定要使用try-catch-finally进行异常的处理,而不是直接抛出。这里为了显示代码逻辑进行了直接抛出
@Test
public void client() throws IOException {
    Socket sc = new Socket( InetAddress.getByName("127.0.0.1"), 8001);

    OutputStream outputStream = sc.getOutputStream();

    FileInputStream fis = new FileInputStream("photo.jpg");

    byte[] data = new byte[1024];
    int len = -1;
    while ((len = fis.read(data)) != -1) {
        outputStream.write(data,0, len);
    }
    System.out.println("文件复制完成");

    fis.close();
    outputStream.close();
    sc.close();
}

@Test
public void server() throws IOException {
    ServerSocket serverSocketc = new ServerSocket(8001);

    Socket socket = serverSocketc.accept();

    InputStream inputStream = socket.getInputStream();

    FileOutputStream fos = new FileOutputStream("photo2.jpg");

    byte[] buffer = new byte[1024];
    int len = -1;
    while ((len = inputStream.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }

    System.out.println("文件复制完成");

    fos.close();
    inputStream.close();
    socket.close();
    serverSocketc.close();

}

3.5 TCP测试三:当服务端收到通知后回复客户端

  • note: 需要注意的是,因为read()为阻塞式的,因此服务端需要知道客户端什么时候发送结束。
  • 因此在客户端需要使用socket.shutdownOutput()方法
// note: 在实际开发中,一定要使用try-catch-finally进行异常的处理,而不是直接抛出。这里为了显示代码逻辑进行了直接抛出
@Test
public void client() throws IOException {
    Socket sc = new Socket( InetAddress.getByName("127.0.0.1"), 8001);

    OutputStream outputStream = sc.getOutputStream();

    outputStream.write("栋梁栋梁!这里是Clinet客户端,收到请回复!".getBytes(StandardCharsets.UTF_8));
    // 当客户端想要接收数据时,需要告诉服务端什么时候发送完成
    // 因为read()为阻塞式的
    sc.shutdownOutput();

    InputStream inputStream = sc.getInputStream();
    byte[] buffer = new byte[1024];
    int len = -1;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    while ((len = inputStream.read(buffer)) != -1) {
        baos.write(buffer, 0, len);
    }
    System.out.println(baos.toString());

    baos.close();
    inputStream.close();
    outputStream.close();
    sc.close();
}

@Test
public void server() throws IOException {
    ServerSocket serverSocketc = new ServerSocket(8001);

    Socket socket = serverSocketc.accept();

    InputStream inputStream = socket.getInputStream();

    byte[] buffer = new byte[1024];
    int len = -1;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    while ((len = inputStream.read(buffer)) != -1) {
        baos.write(buffer, 0, len);
    }
    System.out.println(baos);

    OutputStream outputStream = socket.getOutputStream();
    outputStream.write("收到回复!".getBytes(StandardCharsets.UTF_8));
    System.out.println("已回复");

    outputStream.close();
    inputStream.close();
    socket.close();
    serverSocketc.close();

}

结果:
TCP服务端:
在这里插入图片描述
TCP客户端:
在这里插入图片描述

4. UDP 网络通信测试

  • 类 DatagramSocket 和 DatagramPacket 实现了基于UDP协议网络程序
  • UDP数据报通过套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
  • DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
  • UDP协议中每个数据报都给出了完成的地址信息,因此无序建立发送方和接收方的连接。

4.1 UDP代码测试一:

import org.junit.Test;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;

/**
* @ClassName: UDPTest
* @Description: Java - 网络编程之UDP测试
* @author: Mr.zhi
* @version: v1.0
* @data: 2022/3/20 21:50
* @node:
*          - 类 DatagramSocket 和 DatagramPacket 实现了基于UDP协议网络程序
*          - UDP数据报通过套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
*          - DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
*          - UDP协议中每个数据报都给出了完成的地址信息,因此无序建立发送方和接收方的连接。
*/
public class UDPTest {
@Test
// UDP的发送端
public void sender() {
DatagramSocket datagramSocket = null; // 此时可以不用指定IP地址和端口号
try {
// 1. 使用类DatagramSocket 建立UDP的套接字
datagramSocket = new DatagramSocket();

            // 2. 使用类DatagramPacket 建立UDP的数据报,来存储数据
            // 如果 datagramSocket 没有指定IP和端口号,则需要在这里进行绑定
            String str = new String("Hello, 这是使用UDP发送的数据报!");
            byte[] data = str.getBytes(StandardCharsets.UTF_8);

            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            DatagramPacket datagramPacket = new DatagramPacket(data, 0, data.length, inetAddress, 8081);

            // 3. 调用DatagramSocket的send()方法发送数据
            datagramSocket.send(datagramPacket);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭套接字
            if (datagramSocket != null) {
                datagramSocket.close();
            }
        }
    }

    // UDP的接收端
    @Test
    public void receiver() {
        DatagramSocket socket = null;
        try {
            // 1. 建立UDP套接字,因为是服务端,所以需要指定端口号
            socket = new DatagramSocket(8081);

            // 2. 建立数据报
            byte[] buffer = new byte[100]; // 因此使用UDP接收数据报时,需要一次性全部接收,如果接收不完则会抛弃,因此可以将该数组设置大一点
            DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

            // 3. 调用DatagramSocket的receive()方法进行数据接收,并存放如packet中
            socket.receive(packet);

            // 4. 将数据进行打印
            System.out.println(new String(packet.getData(), 0, packet.getLength()));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 关闭套接字
            socket.close();
        }


    }
}

UDP服务端的控制台输出:
UDP服务端的控制台

4.2 UDP代码测试二:可以连续发送数据

public class UDPTest {
    public static void main(String[] args) {
        UDPTest.sender();
    }

//    @Test
    // UDP的发送端
    public static void sender() {
        DatagramSocket datagramSocket = null; // 此时可以不用指定IP地址和端口号
        try {
            // 1. 使用类DatagramSocket 建立UDP的套接字
            datagramSocket = new DatagramSocket();

            // 2. 使用类DatagramPacket 建立UDP的数据报,来存储数据
            // 如果 datagramSocket 没有指定IP和端口号,则需要在这里进行绑定
            String str = new String("Hello, 这是使用UDP发送的数据报!");
            byte[] data = str.getBytes(StandardCharsets.UTF_8);
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            DatagramPacket datagramPacket = null;

            datagramPacket = new DatagramPacket(data, 0, data.length, inetAddress, 8081);
            datagramSocket.send(datagramPacket);

            // 连续发送数据
            while (true) {
                str = new String(new Scanner(System.in).nextLine());
                data = str.getBytes(StandardCharsets.UTF_8);
                datagramPacket = new DatagramPacket(data, 0, data.length, inetAddress, 8081);

                System.out.println(str);
                // 3. 调用DatagramSocket的send()方法发送数据
                datagramSocket.send(datagramPacket);
                if ("exit".equalsIgnoreCase(str) || "e".equalsIgnoreCase(str)) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭套接字
            if (datagramSocket != null) {
                datagramSocket.close();
            }
        }
    }

    // UDP的接收端
    @Test
    public void receiver() {
        DatagramSocket socket = null;
        try {
            // 1. 建立UDP套接字,因为是服务端,所以需要指定端口号
            socket = new DatagramSocket(8081);

            // 2. 建立数据报
            byte[] buffer = null; // 因此使用UDP接收数据报时,需要一次性全部接收,如果接收不完则会抛弃,因此可以将该数组设置大一点
            DatagramPacket packet = null;

            // 连续接收数据,直到收到exit或e后退出
            while (true) {
                buffer = new byte[100];
                packet = new DatagramPacket(buffer, 0, buffer.length);

                // 3. 调用DatagramSocket的receive()方法进行数据接收,并存放如packet中
                socket.receive(packet);

                // 4. 将数据进行打印
                String str = new String(new String(packet.getData(), 0, packet.getLength()));
                if ("exit".equalsIgnoreCase(str) || "e".equalsIgnoreCase(str)) {
                    break;
                }
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 关闭套接字
            socket.close();
        }


    }
}

5. URL 编程

5.1 URL概述

  • URL(Uniform Resource Locator) : 统一资源定位符,它表示Internet上某一资源的地址
  • 它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何loacte这个资源。
  • 通过URL我们可以访问Internet上的各种网络资源,比如最常见的www, ftp站点。
    浏览通过解析给定的URL可以在网络上查找相应的文件或其他资源。
  • URL的基本结构由5部分组成:
    • <传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
    • 例如: http://192.168.0.100:8080/helloworld/index.jsp#a?username=zhilx&passwd=123456
    • #片段名:即锚点,例如看小说,直接定位到章节
    • 参数列表格式:参数名=参数值&参数名=参数值…

5.2 URL常用方法代码测试:

public void test01() {
  // 1. 创建URL标识
  try {
  	URL url = new URL("http://192.168.0.100:8080/helloworld/index.jsp#a?username=zhilx&passwd=123456");

  	// 2. URL常用方法测试
  	// 2.1 使用 getProtocol获取URL的协议名
  	System.out.println(url.getProtocol());

  	// 2.2 使用getHost()获取URL的主机名
  	System.out.println(url.getHost());

  	// 2.3 使用getPost()获取URL的端口号
  	System.out.println(url.getProtocol());

  	// 2.4 使用getPath()获取文件路径
  	System.out.println(url.getPath());

  	// 2.5 使用getFile()获取URL的文件名
  	System.out.println(url.getFile());

  	// 2.6 使用getQuery() 获取URL的查询名
  	System.out.println(url.getQuery());

  } catch (MalformedURLException e) {
  	e.printStackTrace();
  }
}

5.3 URL方法测试:从指定页面下载文件

// 2. URL编程实现网络资源的下载
// TCP、UDP的博客中的图片: https://i-blog.csdnimg.cn/blog_migrate/b0db95a88d3e0fe8988ac2279c931c4c.png
public void test02(){
	InputStream inputStream = null;
	FileOutputStream fos = null;
	try {
		// 1. 指定URL的指定文件位置
		URL url = new URL(" https://i-blog.csdnimg.cn/blog_migrate/b0db95a88d3e0fe8988ac2279c931c4c.png");

		// 2. 获取URL连接
		HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

		// 3. 建立连接(一定要有这个步骤)
		urlConnection.connect();

		// 4. 下载数据
		inputStream = urlConnection.getInputStream();
		fos = new FileOutputStream("7-网络编程\\src\\figure1.png");

		// 5. 将数据写入到文件中
		byte[] buffer = new byte[1024];
		int len = -1;
		while ((len = inputStream.read(buffer)) != -1) {
			fos.write(buffer, 0, len);
		}

		System.out.println("文件下载完成!");
	} catch (IOException e) {
		e.printStackTrace();
	} finally {// 6. 关闭流和套接字
		if (fos != null) {
			try {
				fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		if (inputStream != null) {
			try {
				inputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值