Java网络编程

什么是网络编程?

在网络通讯协议下,不同计算机上运行的程序,进行的数据传输。

  • 应用场景:即时通信、网游对战、金融证券、国际贸易、邮件等等。

不管是什么场景,都是计算机跟计算机之间通过网络进行数据传输。

  • Java 中可以使用 java.net 包下的技术轻松开发出常见的网络应用程序。

常见的软件架构

屏幕截图 2026-05-14 140815

BS 架构的优缺点

  1. 不需要开发客户端,只需要页面 + 服务器
  2. 用户不需要下载,打开浏览器就能使用
  3. 如果应用过大,用户体验受到影响(缺点)

CS 架构的优缺点

  1. 画面可以做得非常精美,用户体验好(优点)
  2. 需要开发客户端,也需要开发服务端
  3. 用户需要下载和更新的时候太麻烦

网络编程三要素

IP:确定对方电脑在互联网上的地址

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

端口号:确定接收数据的软件

  • 应用程序在设备中唯一的标识

协议:确定网络传输的规则

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

IP

全称:Internet Protocol,是互联网协议地址,也称 IP 地址。

是分配给上网设备的数字标签。

通俗理解:上网设备在网络中的地址,是唯一的

常见的 IP 分类为:IPv4、IPv6

IPv4

全称:Internet Protocol version 4,互联网通信协议第四版

采用32位地址长度,分成4组

屏幕截图 2026-05-14 193125

IPv4 的地址分类形式

  • 公网地址(万维网使用)和私有地址(局域网使用)
  • 192.168.开头的就是私有地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用,以此节省 IP

特殊 IP 地址

127.0.0.1,也可以是 localhost:是回送地址也称本地回环地址,也称本地 IP,永远只会寻找当前所在本机。

常用的 CMD 命令

  • ipconfig:查看本机 IP 地址
  • ping:检查网络是否连通

IPv6

全称:Internet Protocol version 6,互联网通信协议第六版。

由于互联网的蓬勃发展,IP 地址的需求量愈来愈大,而 IPv4 的模式下 IP 的总数是有限的。

采用128位地址长度,分成8组

屏幕截图 2026-05-14 193849

InetAddress 的使用

package com.faxont.inetaddress;

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

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

        // 1. 获取 InetAddress 的对象
        // IP 的对象,也可以看着一台电脑的对象
        InetAddress address = InetAddress.getByName("faxont");
        System.out.println(address);
        System.out.println(address.getHostName());
        System.out.println(address.getHostAddress());
    }
}

端口号

应用程序在设备中唯一的标识

端口号:由两个字节表示的整数,取值范围:0~65535

​ 其中0~1023之间的端口号用于一些知名的网络服务或者应用

​ 我们自己使用1024以上的端口号就可以了

注意:一个端口号只能被一个应用程序使用

协议

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

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

屏幕截图 2026-05-15 083654

UDP 协议

  • 用户数据协议(User Datagram Protocol)
  • UDP 是面向无连接通信协议

​ 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据

UDP 通信程序(发送数据)

  1. 创建发送端的 DatagramSocket 对象
  2. 数据打包(DatagramPacket)
  3. 发送数据
  4. 释放资源
package com.faxont.udp;

import java.io.IOException;
import java.net.*;

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 address = InetAddress.getByName("127.0.0.1");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        // 3. 发送数据
        ds.send(dp);
        
        // 4. 释放资源
        ds.close();
    }
}

UDP 通信程序(接收数据)

  1. 创建接收端的 DatagramSocket 对象
  2. 接收打包好的数据
  3. 解析数据包
  4. 释放资源
package com.faxont.udp;

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

public class ReceiveMessageDemo {
    public static void main(String[] args) throws IOException {
        // 接收数据
        // 1. 创建 DatagramSocket 对象
        // 细节:
        // 在接收的时候,一定要绑定端口
        // 而且绑定的端口一定要跟发送的端口保持一致
        DatagramSocket ds = new DatagramSocket(10086);

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

        // 3. 调用 DatagramSocket 对象的方法,接收数据
        // 细节:
        // 该方法是阻塞的
        // 程序执行到这一步的时候,会在这里死等
        // 等发送端发送数据之后,才会执行下面的代码
        System.out.println("等待数据...");
        ds.receive(dp);
        System.out.println("数据接收完毕...");

        // 4. 解析数据包
        byte[] data = dp.getData();
        int len = dp.getLength();
        InetAddress address = dp.getAddress();
        int port = dp.getPort();

        System.out.println("接收到数据:" + new String(data, 0, len));
        System.out.println("来自" + address.getHostAddress() + ":" + port);

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

练习:聊天室

按照下面的要求实现程序

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

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

package com.faxont.com.practice1;

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

public class ChatRoom {
    private static final int BYTES_LENGTH = 1024;
    private static final String HOST = "127.0.0.1";

    public static void chat(int localPort, int targetPort){
        System.out.println("聊天室已启动!");
        try{
            new Thread(() -> receiveMessage(localPort), "接收线程").start();
            sendMessage(targetPort);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private static void sendMessage(int targetPort){
        try(DatagramSocket ds = new DatagramSocket()){
            Scanner sc = new Scanner(System.in);
            while (true) {
                String str = sc.next();
                byte[] bytes = str.getBytes();
                DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(HOST), targetPort);
                ds.send(dp);
                System.out.println("发送了:" + str);
                if (str.equals("886")) {
                    break;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private static void receiveMessage(int localPort){
        try(DatagramSocket ds = new DatagramSocket(localPort)){
            byte[] bytes = new byte[BYTES_LENGTH];
            DatagramPacket dp = new DatagramPacket(bytes, bytes.length);

            while(true){
                System.out.println("对方正在输入中...");
                ds.receive(dp);
                System.out.println("消息加载中...");

                byte[] data = dp.getData();
                int len = dp.getLength();
                InetAddress address = dp.getAddress();
                int sendPort = dp.getPort();
                String str = new String(data, 0, len);

                System.out.println("\n【对方消息】" + str + " —— 来自:" + address.getHostAddress() + ":" + sendPort);
                if(str.equals("886")){
                    System.out.println("对方离开了聊天室...");
                    break;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package com.faxont.com.practice1;

import java.io.IOException;

public class Chat1 {
    public static void main(String[] args) throws IOException {

        ChatRoom chatRoom = new ChatRoom();
        chatRoom.chat(8888, 9999);
    }
}
package com.faxont.com.practice1;

import java.io.IOException;

public class Chat2 {
    public static void main(String[] args) throws IOException {

        ChatRoom chatRoom = new ChatRoom();
        chatRoom.chat(9999, 8888);
    }
}

UDP 的三种通信方式(代码实现)

  1. 单播

​ 之前的代码就是单播

  1. 组播

​ 组播地址:224.0.0.0 ~ 239.255.255.255

​ 其中224.0.0.0 ~ 224.0.0.255为预留的组播地址

package com.faxont.udp.udpdemo2;

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

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        /*
            组播发送端代码
         */

        // 创建 MulticastSocket 对象
        MulticastSocket ms = new MulticastSocket();

        // 创建 DatagramPacket 对象
        String s = "你好,你好!";
        byte[] bytes = s.getBytes();
        InetAddress address = InetAddress.getByName("224.0.0.1");
        int port = 10000;

        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, address, port);

        // 调用 MulticastSocket 对象发送数据
        ms.send(datagramPacket);

        // 释放资源
        ms.close();
    }
}
package com.faxont.udp.udpdemo2;

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

public class ReceiveMessageDemo1 {
    public static void main(String[] args) throws IOException {
        /*
            组播接收端代码
         */

        // 1. 创建 MulticastSocket 对象
        MulticastSocket ms = new MulticastSocket(10000);

        // 2. 将当前本机添加到224.0.0.1这个组播组中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

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

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

        // 5. 解析数据
        byte[] data = dp.getData();
        int len = dp.getLength();
        String ip = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();

        System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));

        // 6. 释放资源
        ms.close();
    }
}
package com.faxont.udp.udpdemo2;

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

public class ReceiveMessageDemo2 {
    public static void main(String[] args) throws IOException {
        /*
            组播接收端代码
         */

        // 1. 创建 MulticastSocket 对象
        MulticastSocket ms = new MulticastSocket(10000);

        // 2. 将当前本机添加到224.0.0.2这个组播组中
        InetAddress address = InetAddress.getByName("224.0.0.2");
        ms.joinGroup(address);

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

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

        // 5. 解析数据
        byte[] data = dp.getData();
        int len = dp.getLength();
        String ip = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();

        System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));

        // 6. 释放资源
        ms.close();
    }
}
package com.faxont.udp.udpdemo2;

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

public class ReceiveMessageDemo3 {
    public static void main(String[] args) throws IOException {
        /*
            组播接收端代码
         */

        // 1. 创建 MulticastSocket 对象
        MulticastSocket ms = new MulticastSocket(10000);

        // 2. 将当前本机添加到224.0.0.1这个组播组中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

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

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

        // 5. 解析数据
        byte[] data = dp.getData();
        int len = dp.getLength();
        String ip = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();

        System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));

        // 6. 释放资源
        ms.close();
    }
}
  1. 广播

​ 广播地址:255.255.255.255

TCP 协议

  • 传输控制协议 TCP(Transmission Control Protocol)
  • TCP 协议是面向连接的通信协议

​ 速度慢,没有大小限制,数据安全

TCP 通信程序

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

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

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

发送数据

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

​ Socket(String host, int port)

  1. 获取输出流,写数据

​ OutputStream getOutputStream()

  1. 释放资源

​ void close

接收数据

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

​ ServerSocket(int port)

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

​ Socket accept()

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

​ InputStream getInputStream()

  1. 释放资源

​ void close()

package com.faxont.tcp.tcpdemo1;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {
        // TCP 协议,发送数据

        // 1. 创建 Socket 对象
        // 细节:在创建对象的同时会连接服务端
        //      如果连接不上,代码会报错
        try(Socket socket = new Socket("127.0.0.1", 10000);
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
        ){
            pw.println("你好你好");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package com.faxont.tcp.tcpdemo1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;

public class Server {
    public static void main(String[] args) {
        // TCP 协议,接收数据

        // 1. 创建 ServerSocket 对象
        try(ServerSocket ss = new ServerSocket(10000);
            BufferedReader br = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()))
        ){
            String line;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习1 --- 多发多收

客户端:多次发送数据

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

package com.faxont.com.practice2;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        /*
            客户端:多次发送数据
            服务器:多次接收数据,并打印
         */

        try(Socket socket = new Socket("127.0.0.1", 10000);
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)
        ){
            Scanner sc = new Scanner(System.in);
            while(true){
                String line = sc.nextLine();
                pw.println(line);
                if("exit".equals(line)){
                    break;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package com.faxont.com.practice2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000);
            BufferedReader br = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()))
        ){
            String line;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习2 --- 接收和反馈

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

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

package com.faxont.com.practice3;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {
        /*
            客户端:发送一条数据,接收服务器反馈的消息并打印
            服务器:接收数据并打印,再给客户端反馈消息
         */

        try(Socket socket = new Socket("127.0.0.1", 10000);
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
        ){
            String str = "你好,服务器!!";
            pw.println(str);
            socket.shutdownOutput();
            try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
                String line;
                while((line = br.readLine()) != null){
                    System.out.println(line);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package com.faxont.com.practice3;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            Socket socket = ss.accept();
            try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
                String line;
                // 细节:
                // read() 和 readLine() 方法会从连接通道中读取数据
                // 但是,需要有一个结束标记,此处的循环才会停止
                // 否则,程序就会一直停在 read() 和 readLine() 方法这里,等待读取下面的数据
                while((line = br.readLine()) != null){
                    System.out.println(line);
                }
                try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
                    String str = "你好,客户端!!";
                    pw.println(str);
                }
            }

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

练习:TCP 通信练习3 --- 上传文件

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

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

屏幕截图 2026-05-15 150622

package com.faxont.com.practice4;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {
        /*
            客户端:将本地文件上传到服务器,接收服务器的反馈
            服务器:接收客户端上传的文件,上传完毕之后给出反馈
         */
        try(Socket socket = new Socket("127.0.0.1", 10000);
            FileInputStream fis = new FileInputStream("D:\\java\\workspace\\csb.txt")
        ){
            byte[] bytes = new byte[1024];
            int len;
            while((len = fis.read(bytes)) != -1){
                socket.getOutputStream().write(bytes, 0, len);
            }
            socket.shutdownOutput();
            try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
                String msg = br.readLine();
                System.out.println("客户端收到服务器反馈:" + msg);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package com.faxont.com.practice4;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            Socket socket = ss.accept();
            try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test.txt")){
                byte[] bytes = new byte[1024];
                int len;
                while((len = socket.getInputStream().read(bytes)) != -1){
                    fos.write(bytes, 0, len);
                }
            }
            try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
                pw.println("服务器已收到文件,上传成功!");
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习4 --- 上传文件(文件名重复问题)

解决上一题文件名重复问题

package com.faxont.com.practice5;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            Socket socket = ss.accept();
            // 获取系统时间戳
//            String timeStamp = String.valueOf(System.currentTimeMillis());
//            try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
            // 利用 UUID 生成文件名
            String uuid = UUID.randomUUID().toString().replace("-", "");
            try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + uuid + ".txt")){
                byte[] bytes = new byte[1024];
                int len;
                while((len = socket.getInputStream().read(bytes)) != -1){
                    fos.write(bytes, 0, len);
                }
            }
            try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
                pw.println("服务器已收到文件,上传成功!");
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习5 --- 上传文件(多线程版)

想要服务器不停止,能接收很多用户上传的图片

该怎么做呢?

提示:可以用循环或者多线程

但是仅仅用循环不合理,最优解法是用(循环+多线程)改写

利用 Lambda 表达式 + new Thread

package com.faxont.com.practice6;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            System.out.println("服务器接收系统启动");
            // 获取系统时间戳
            while(true){
                Socket socket = ss.accept();
                System.out.println("新客户端连接:" + socket.getInetAddress().getHostAddress());
                new Thread(() -> {
                    try(socket){
                        String timeStamp = String.valueOf(System.currentTimeMillis());
                        try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
                            byte[] bytes = new byte[1024];
                            int len;
                            while((len = socket.getInputStream().read(bytes)) != -1){
                                fos.write(bytes, 0, len);
                            }
                            System.out.println("文件上传成功!");
                        }
                        try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
                            pw.println("服务器已收到文件,上传成功!");
                        }
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                }).start();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

利用 Runnable

package com.faxont.com.practice6;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.UUID;

/**
 * Record 是什么?
 * Java 16 正式出的新语法,专门用来做「纯数据类」。
 * 什么叫纯数据类?
 * 只存属性
 * 不写复杂业务逻辑
 * 不需要写构造器、getter、equals、hashCode、toString
 * Java 自动帮你全部生成
 *
 * 特点:
 * 1. 所有成员变量默认都是 final
 * 2. 不能改值,天然不可变。
 * 3. 只能在括号里定义属性,不能再随便加普通成员变量
 * 4. 不能显式继承其他类,默认继承 java.lang.Record
 * 5. 适合:做实体、做参数载体、临时存数据
 *      不适合:复杂业务类、需要增删改成员变量的类
 */
public record MyRunnable(Socket socket) implements Runnable{
    @Override
    public void run() {
        try(socket){
            while(true){
                // 获取系统时间戳
//            String timeStamp = String.valueOf(System.currentTimeMillis());
//            try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
                // 利用 UUID 生成文件名
                String uuid = UUID.randomUUID().toString().replace("-", "");
                try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + uuid + ".txt")){
                    byte[] bytes = new byte[1024];
                    int len;
                    while((len = socket.getInputStream().read(bytes)) != -1){
                        fos.write(bytes, 0, len);
                    }
                }
                try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
                    pw.println("服务器已收到文件,上传成功!");
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package com.faxont.com.practice6;

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

public class ServerRunnable {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            System.out.println("服务器接收系统启动");
            while(true){
                Socket socket = ss.accept();
                new Thread(new MyRunnable(socket)).start();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习6 --- 上传文件(线程池优化)

频繁创建线程并销毁非常浪费系统资源,所以需要用线程池优化

package com.faxont.com.practice7;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            System.out.println("服务器接收系统启动");

            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                    3,// 核心线程数
                    16,// 线程池总大小
                    60,// 空闲时间(值)
                    TimeUnit.SECONDS,// 空闲时间(单位)
                    new ArrayBlockingQueue<>(2),// 队列
                    Executors.defaultThreadFactory(),// 线程工厂
                    new ThreadPoolExecutor.AbortPolicy()// 阻塞队列
            );

            // 获取系统时间戳
            while(true){
                Socket socket = ss.accept();
                System.out.println("新客户端连接:" + socket.getInetAddress().getHostAddress());
                threadPool.submit(() -> {
                    try(socket){
                        String timeStamp = String.valueOf(System.currentTimeMillis());
                        try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
                            byte[] bytes = new byte[1024];
                            int len;
                            while((len = socket.getInputStream().read(bytes)) != -1){
                                fos.write(bytes, 0, len);
                            }
                            System.out.println("文件上传成功!");
                        }
                        try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
                            pw.println("服务器已收到文件,上传成功!");
                        }
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                });
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习7 --- BS(接收浏览器的消息并打印)

客户端:不需要写

服务器:接收数据并打印

package com.faxont.com.practice8;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;

public class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000);
            BufferedReader br = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()))
        ){
            String line;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

练习:TCP 通信练习8 --- 聊天室优化

控制台版的聊天室

package com.faxont.com.practice9;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Server {
    public static List<Socket> socketList = Collections.synchronizedList(new ArrayList<>());
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(10000)){
            while(true){
                Socket socket = ss.accept();
                System.out.println("有新用户连接:" + socket.getInetAddress().getHostAddress());

                socketList.add(socket);
                System.out.println("当前在线人数:" + socketList.size());

                new Thread(new ClientHandler(socket)).start();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class ClientHandler implements Runnable{
    private final Socket socket;

    public ClientHandler(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
            String msg;
            while((msg = br.readLine()) != null){
                System.out.println("收到:" + msg);
                sendAll(msg);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private void sendAll(String msg){
        synchronized (Server.socketList){
            for (Socket socket : Server.socketList) {
                try{
                    PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
                    pw.println(msg);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

}
package com.faxont.com.practice9;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    private static final Scanner SC = new Scanner(System.in);
    public void start(){
        Socket socket = null;
        try{
            socket = new Socket("127.0.0.1", 10000);
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            new Thread(() -> {
                String msg;
                try{
                    while((msg = br.readLine()) != null){
                        System.out.println("收到:" + msg);
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
            }).start();
            String line;
            while(!"886".equals(line = SC.nextLine())){
                pw.println(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(socket != null){
                try{
                    socket.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
package com.faxont.com.practice9;

import java.io.*;
import java.util.Scanner;

public class Choose {
    private static final Scanner SC = new Scanner(System.in);
    private static final File SRC_FILE = new File("soketnet\\userInfo.txt");
    public void choose(){
        System.out.println("欢迎来到聊天室系统!");
        while(true){
            System.out.println("请选择你要进行的操作:");
            System.out.println("1. 登录");
            System.out.println("2. 注册");
            System.out.println("3. 退出");
            int choose = SC.nextInt();
            switch (choose){
                case 1-> loginCase();
                case 2 -> registerCase();
                case 3 -> {
                    System.out.println("退出聊天室系统!");
                    System.exit(0);
                }
                default -> System.out.println("输入错误!请重新输入!");
            }
        }
    }

    private void loginCase(){
        System.out.println("请输入用户名:");
        String username = SC.next();
        System.out.println("请输入密码:");
        String password = SC.next();
        if(login(username, password)){
            System.out.println("登录成功!用户" + username + "欢迎您!");
            new Client().start();
        }else{
            System.out.println("登录失败!");
        }
    }

    private void registerCase(){
        System.out.println("请输入用户名:");
        String username = SC.next();
        System.out.println("请输入密码:");
        String password = SC.next();
        if(username.matches("^[a-zA-Z0-9][a-zA-Z0-9_]{3,15}$") && password.matches("^(?=.*[A-Z])[a-zA-Z0-9][a-zA-Z0-9_]{3,15}$")){
            if(!isExist(SRC_FILE, username)){
                register(username, password);
                System.out.println("注册成功!用户" + username + "欢迎您!");
            }else{
                System.out.println("用户名已存在!请重新输入!");
            }
        }else{
            System.out.println("用户名或密码格式错误!请重新输入!");
        }
    }

    private boolean login(String username, String password){
        try(BufferedReader userReader = new BufferedReader(new FileReader(SRC_FILE))){
            String line;
            while((line = userReader.readLine()) != null){
                String[] userInfo = parseLoginData(line);
                if(username.equals(userInfo[0]) && password.equals(userInfo[1])){
                    return true;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解析登录字符串:username=zhangsan&password=123456
     * 返回 String[0] = 用户名
     * 返回 String[1] = 密码
     */
    private String[] parseLoginData(String data) {
        // 1. 用 & 分割
        String[] parts = data.split("&");
        // parts[0] = "username=zhangsan"
        // parts[1] = "password=123456"

        String username = null;
        String password = null;

        // 2. 遍历分割后的每一段,再用 = 分割
        for (String part : parts) {
            String[] keyValue = part.split("=");
            // keyValue[0] = 键(username/password)
            // keyValue[1] = 值

            if (keyValue[0].equals("username")) {
                username = keyValue[1];
            } else if (keyValue[0].equals("password")) {
                password = keyValue[1];
            }
        }

        // 返回 用户名、密码
        return new String[]{username, password};
    }

    private void register(String username, String password){
        try(BufferedWriter userWriter = new BufferedWriter(new FileWriter(SRC_FILE, true))){
            userWriter.write("username=" + username + "&password=" + password);
            userWriter.newLine();
            userWriter.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private boolean isExist(File srcFile, String username){
        try(BufferedReader userReader = new BufferedReader(new FileReader(srcFile))){
            String line;
            while((line = userReader.readLine()) != null){
                String[] userInfo = parseLoginData(line);
                if(username.equals(userInfo[0])){
                    return true;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        return false;
    }
}
package com.faxont.com.practice9;

public class Test {
    public static void main(String[] args) {
        Choose choose = new Choose();
        choose.choose();
    }
}
posted @ 2026-05-16 08:40  弋湖  阅读(15)  评论(0)    收藏  举报