Skip to content

S01-21 JavaSE-网络编程

[TOC]

网络的相关概念

网络通信

  • 概念:两台设备通过网络实现数据传输
  • Java提供java.net包用于网络通信

网络分类

类型覆盖范围
局域网(LAN)教室、机房等小范围
城域网(MAN)一个城市
广域网(WAN)全国、全球(如万维网)

IP地址

  • 唯一标识网络中的每台主机
  • 表示形式:点分十进制(xx.xx.xx.xx),每个十进制数范围0~255
  • 组成:网络地址 + 主机地址
  • IPv4:地址资源有限,已逐步被IPv6替代
  • IPv6:地址数量极多,解决地址短缺问题
  • 本机地址:127.0.0.1

IPv4地址分类

类型网络号位数主机号位数范围
A类7位24位0.0.0.0 ~ 127.255.255.255
B类14位16位128.0.0.0 ~ 191.255.255.255
C类21位8位192.0.0.0 ~ 223.255.255.255
D类多播组号-224.0.0.0 ~ 239.255.255.255
E类保留-240.0.0.0 ~ 247.255.255.255

域名

  • 作用:将IP地址映射为易记的名称(如www.baidu.com)
  • 好处:避免记忆复杂的IP地址

端口号

  • 标识计算机上的某个网络程序
  • 范围:0~65535(2个字节)
  • 0~1024:已被系统占用(如ssh 22、ftp 21、http 80)
  • 常见程序端口:tomcat 8080、mysql 3306、oracle 1521、sqlserver 1433

网络通信协议

OSI模型TCP/IP模型TCP/IP模型各层对应协议
应用层应用层HTTP、FTP、Telnet、DNS
表示层
会话层
传输层传输层(TCP)TCP、UDP
网络层网络层(IP)IP、ICMP、ARP
数据链路层物理+数据链路层
物理层Link

TCP 和 UDP 对比

特性TCP协议(传输控制协议)UDP协议(用户数据协议)
连接要求需建立连接(三次握手)无需建立连接
可靠性可靠传输不可靠传输
数据量限制无限制,适合大批量数据单个数据包最大64K,不适合大量数据
效率低(需连接和释放)高(无需连接和释放)
应用场景文件传输、聊天等需可靠传输的场景视频直播、短信等对实时性要求高的场景

InetAddress 类

常用方法

方法名功能说明
static InetAddress getLocalHost()获取本机InetAddress对象
static InetAddress getByName(String host)根据主机名/域名获取IP地址对象
String getHostName()获取主机名
String getHostAddress()获取IP地址字符串

应用案例

java
package com.hspedu.api;

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

/**
 * @author 韩顺平
 * @version 1.0
 * InetAddress应用
 */
public class API_ {
    public static void main(String[] args) throws UnknownHostException {
        // 获取本机InetAddress对象
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost); // 主机名/IP地址
        
        // 根据主机名获取
        InetAddress host2 = InetAddress.getByName("ThinkPad-PC");
        System.out.println(host2);
        
        // 根据域名获取
        InetAddress host3 = InetAddress.getByName("www.baidu.com");
        System.out.println(host3.getHostName()); // 主机名
        System.out.println(host3.getHostAddress()); // IP地址
    }
}

Socket

基本介绍

  • 套接字,是网络通信的端点
  • 通信双方都需要Socket
  • 网络通信本质是Socket间的通信
  • 可将网络连接视为流,通过IO传输数据
  • 主动发起通信的为客户端,等待通信的为服务端

TCP 网络通信编程

核心类

  • ServerSocket:服务端,用于监听端口,接收客户端连接
  • Socket:客户端,用于连接服务端,建立通信通道

应用案例1:客户端发送数据,服务端接收

服务端(SocketTCP01Server.java)
java
package com.hspedu.socket;

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * TCP服务端
 */
public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        // 在9999端口监听
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接..");
        
        // 等待客户端连接(阻塞)
        Socket socket = serverSocket.accept();
        System.out.println("服务端socket = " + socket.getClass());
        
        // 读取客户端数据
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, readLen));
        }
        
        // 关闭资源
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
客户端(SocketTCP01Client.java)
java
package com.hspedu.socket;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * TCP客户端
 */
public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        // 连接本机9999端口
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端socket返回 = " + socket.getClass());
        
        // 发送数据
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, server".getBytes());
        
        // 关闭资源
        outputStream.close();
        socket.close();
        System.out.println("客户端退出.....");
    }
}

应用案例2:客户端与服务端双向通信

服务端(SocketTCP02Server.java)
java
package com.hspedu.socket;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * TCP服务端(双向通信)
 */
@SuppressWarnings({"all"})
public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接..");
        
        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();
        
        // 读取客户端数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, readLen));
        }
        
        // 向客户端回复数据
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, client".getBytes());
        socket.shutdownOutput(); // 设置结束标记
        
        // 关闭资源
        outputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
客户端(SocketTCP02Client.java)
java
package com.hspedu.socket;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * TCP客户端(双向通信)
 */
@SuppressWarnings({"all"})
public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        
        // 发送数据
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, server".getBytes());
        socket.shutdownOutput(); // 设置结束标记
        
        // 接收服务端回复
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, readLen));
        }
        
        // 关闭资源
        inputStream.close();
        outputStream.close();
        socket.close();
        System.out.println("客户端退出.....");
    }
}

应用案例3:字符流双向通信

服务端(SocketTCP03Server.java)
java
package com.hspedu.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * TCP服务端(字符流)
 */
@SuppressWarnings({"all"})
public class SocketTCP03Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接..");
        
        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();
        
        // 字节流转字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);
        
        // 回复客户端
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello client 字符流");
        bufferedWriter.newLine(); // 换行符作为结束标记
        bufferedWriter.flush(); // 手动刷新
        
        // 关闭资源
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSocket.close();
    }
}
客户端(SocketTCP03Client.java)
java
package com.hspedu.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * TCP客户端(字符流)
 */
@SuppressWarnings({"all"})
public class SocketTCP03Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        
        // 发送数据
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello, server 字符流");
        bufferedWriter.newLine(); // 换行符作为结束标记
        bufferedWriter.flush(); // 手动刷新
        
        // 接收回复
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);
        
        // 关闭资源
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出.....");
    }
}

应用案例4:文件上传(TCP)

工具类(StreamUtils.java)
java
package com.hspedu.upload;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 流工具类
 */
public class StreamUtils {
    /**
     * 输入流转字节数组
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int len;
        while ((len = is.read(b)) != -1) {
            bos.write(b, 0, len);
        }
        byte[] array = bos.toByteArray();
        bos.close();
        return array;
    }
    
    /**
     * 输入流转字符串
     */
    public static String streamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            builder.append(line + "\r\n");
        }
        return builder.toString();
    }
}
服务端(TCPFileUploadServer.java)
java
package com.hspedu.upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 韩顺平
 * @version 1.0
 * 文件上传服务端
 */
public class TCPFileUploadServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在8888端口监听....");
        
        Socket socket = serverSocket.accept();
        
        // 读取客户端上传的文件
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        
        // 保存文件到src目录
        String destFilePath = "src\\abc.mp4";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);
        bos.close();
        
        // 回复客户端
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        writer.write("收到图片");
        writer.flush();
        socket.shutdownOutput();
        
        // 关闭资源
        writer.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}
客户端(TCPFileUploadClient.java)
java
package com.hspedu.upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.Socket;

/**
 * @author 韩顺平
 * @version 1.0
 * 文件上传客户端
 */
public class TCPFileUploadClient {
    public static void main(String[] args) throws Exception {
        // 连接服务端8888端口
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        
        // 读取本地文件
        String filePath = "e:\\abc.mp4";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        
        // 发送文件到服务端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);
        bis.close();
        socket.shutdownOutput(); // 设置结束标记
        
        // 接收服务端回复
        String s = StreamUtils.streamToString(socket.getInputStream());
        System.out.println(s);
        
        // 关闭资源
        socket.getInputStream().close();
        bos.close();
        socket.close();
    }
}

netstat 指令

  • netstat -an:查看当前主机网络情况(端口监听、网络连接)
  • netstat -an | more:分页显示
  • 状态说明:
    • LISTENING:端口正在监听
    • ESTABLISHED:已建立连接

UDP 网络通信编程

核心类

  • DatagramSocket:用于发送和接收数据报
  • DatagramPacket:封装UDP数据报(包含发送端/接收端IP和端口)

应用案例:UDP双向通信

接收端A(UDPReceiverA.java)
java
package com.hspedu.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * UDP接收端
 */
public class UDPReceiverA {
    public static void main(String[] args) throws IOException {
        // 在9999端口接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        
        // 构建接收数据包
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        System.out.println("接收端A等待接收数据..");
        
        // 接收数据(阻塞)
        socket.receive(packet);
        
        // 解析数据
        int length = packet.getLength();
        byte[] data = packet.getData();
        String s = new String(data, 0, length);
        System.out.println(s);
        
        // 回复发送端B
        data = "好的, 明天见".getBytes();
        DatagramPacket sendPacket = new DatagramPacket(
                data, data.length, InetAddress.getByName("192.168.12.1"), 9998
        );
        socket.send(sendPacket);
        
        // 关闭资源
        socket.close();
        System.out.println("A端退出...");
    }
}
发送端B(UDPSenderB.java)
java
package com.hspedu.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.io.IOException;

/**
 * @author 韩顺平
 * @version 1.0
 * UDP发送端
 */
@SuppressWarnings({"all"})
public class UDPSenderB {
    public static void main(String[] args) throws IOException {
        // 在9998端口发送数据
        DatagramSocket socket = new DatagramSocket(9998);
        
        // 构建发送数据包
        byte[] data = "hello 明天吃火锅~".getBytes();
        DatagramPacket packet = new DatagramPacket(
                data, data.length, InetAddress.getByName("192.168.12.1"), 9999
        );
        socket.send(packet);
        
        // 接收接收端A的回复
        byte[] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
        
        // 解析回复
        int length = packet.getLength();
        data = packet.getData();
        String s = new String(data, 0, length);
        System.out.println(s);
        
        // 关闭资源
        socket.close();
        System.out.println("B端退出...");
    }
}

本章作业

Homework01.java(TCP字符流聊天)

服务端(Homework01Server.java)

java
package com.hspedu.homework;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;

/**
 * TCP服务端(多轮聊天)
 */
@SuppressWarnings({"all"})
public class Homework01Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在9999端口监听...");
        
        Socket socket = serverSocket.accept();
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        
        String line;
        while ((line = br.readLine()) != null) {
            // 根据客户端请求回复
            String response;
            if ("name".equals(line)) {
                response = "我是nova";
            } else if ("hobby".equals(line)) {
                response = "编写java程序";
            } else {
                response = "你说啥呢";
            }
            bw.write(response);
            bw.newLine();
            bw.flush();
        }
        
        // 关闭资源
        bw.close();
        br.close();
        socket.close();
        serverSocket.close();
    }
}

客户端(Homework01Client.java)

java
package com.hspedu.homework;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.io.IOException;
import java.util.Scanner;

/**
 * TCP客户端(多轮聊天)
 */
@SuppressWarnings({"all"})
public class Homework01Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        Scanner scanner = new Scanner(System.in);
        
        while (true) {
            System.out.print("请输入请求(输入exit退出):");
            String line = scanner.nextLine();
            if ("exit".equals(line)) {
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
            
            // 接收回复
            String response = br.readLine();
            System.out.println("服务端回复:" + response);
        }
        
        // 关闭资源
        scanner.close();
        bw.close();
        br.close();
        socket.close();
    }
}

Homework02.java(UDP问答)

接收端A(Homework02ReceiverA.java)

java
package com.hspedu.homework;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.io.IOException;

/**
 * UDP接收端(问答)
 */
public class Homework02ReceiverA {
    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(8888);
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        
        System.out.println("接收端A等待接收数据...");
        socket.receive(packet);
        
        // 解析问题
        String question = new String(packet.getData(), 0, packet.getLength());
        System.out.println("收到问题:" + question);
        
        // 准备回答
        String answer;
        if ("四大名著是哪些".equals(question)) {
            answer = "四大名著是《红楼梦》《西游记》《水浒传》《三国演义》";
        } else {
            answer = "what?";
        }
        
        // 发送回答
        byte[] data = answer.getBytes();
        DatagramPacket sendPacket = new DatagramPacket(
                data, data.length, packet.getAddress(), packet.getPort()
        );
        socket.send(sendPacket);
        
        // 关闭资源
        socket.close();
    }
}

发送端B(Homework02SenderB.java)

java
package com.hspedu.homework;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.io.IOException;

/**
 * UDP发送端(问答)
 */
public class Homework02SenderB {
    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(8889);
        
        // 发送问题
        String question = "四大名著是哪些";
        byte[] data = question.getBytes();
        DatagramPacket packet = new DatagramPacket(
                data, data.length, InetAddress.getByName("127.0.0.1"), 8888
        );
        socket.send(packet);
        
        // 接收回答
        byte[] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
        
        String answer = new String(packet.getData(), 0, packet.getLength());
        System.out.println("收到回答:" + answer);
        
        // 关闭资源
        socket.close();
    }
}

Homework03.java(文件下载)

服务端(Homework03Server.java)

java
package com.hspedu.homework;

import com.hspedu.upload.StreamUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;

/**
 * 文件下载服务端
 */
public class Homework03Server {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在9999端口监听...");
        
        Socket socket = serverSocket.accept();
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        String fileName = StreamUtils.streamToString(bis).trim();
        
        // 准备文件(存在则发送,不存在则发送默认文件)
        String filePath;
        if ("高山流水".equals(fileName)) {
            filePath = "e:\\高山流水.mp3";
        } else {
            filePath = "e:\\默认音乐.mp3"; // 默认文件
        }
        
        // 读取文件并发送
        BufferedInputStream fileBis = new BufferedInputStream(new FileInputStream(filePath));
        byte[] fileBytes = StreamUtils.streamToByteArray(fileBis);
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(fileBytes);
        fileBis.close();
        socket.shutdownOutput();
        
        // 关闭资源
        bos.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}

客户端(Homework03Client.java)

java
package com.hspedu.homework;

import com.hspedu.upload.StreamUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.io.IOException;
import java.util.Scanner;

/**
 * 文件下载客户端
 */
public class Homework03Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        
        // 输入要下载的音乐名
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要下载的音乐名:");
        String fileName = scanner.nextLine();
        
        // 发送音乐名给服务端
        OutputStreamWriter osw = new OutputStreamWriter(socket.getOutputStream());
        osw.write(fileName);
        osw.flush();
        socket.shutdownOutput();
        
        // 接收文件并保存到e盘
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] fileBytes = StreamUtils.streamToByteArray(bis);
        String destFilePath = "e:\\" + fileName + ".mp3";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(fileBytes);
        
        // 关闭资源
        bos.close();
        bis.close();
        osw.close();
        scanner.close();
        socket.close();
        System.out.println("文件下载完成,保存路径:" + destFilePath);
    }
}