1. TCP通信

1.1. 简介

  • TCP指的是传输控制协议 (Transmission Control Protocol)

  • TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”

1.2. 三次握手简述

image-20230216155908838

  • 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠

    第一次握手,客户端向服务器端发出连接请求,等待服务器确认

    第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求

    第三次握手,客户端再次向服务器端发送确认信息,确认连接

  • 完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等

1.3. Java使用TCP协议

1.3.1. ServerSocket和Socket类

  • Java中的TCP通信

    • Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

    • Java为客户端提供了Socket类,为服务器端提供了ServerSocket类

1.3.2. TCP客户端发送数据

java中,客户端通过Socket类向服务端发起TCP请求

  • 构造方法

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

    方法名 说明
    InputStream getInputStream() 返回此套接字的输入流
    OutputStream getOutputStream() 返回此套接字的输出流

  • 步骤
  1. 创建客户端的 Socket对象

  2. 获取输出流(默认是字节流) 用来发送数据

  3. 获取输入流 用来接收服务器的数据

  4. 关闭流


  • 代码实现
package link.xiaomo.test7;

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

public class Demo02 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1创建客户端的  Socket对象
        //参1和参2 表示当前要连接的服务器的ip和端口
        Socket socket = new Socket("127.0.0.1", 15000);

        //2 获取输出流(默认是字节流) 用来发送数据
        OutputStream os = socket.getOutputStream();
        //发送 文字  注意要转为字节数组
        os.write("你好".getBytes("gbk"));

        //3 获取输入流 用来接收服务器的数据
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        //这里会等待接收数据
        int len = is.read(bytes);
        //打印数据
        System.out.println(new String(bytes, 0, len));

        //4 关闭流
        os.close();
        is.close();
        socket.close();
    }
}

image-20221220162419979

1.3.3. TCP服务器接收数据

java中,服务端通过ServerSocket类接收客户端的请求并响应。

  • 构造方法

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

    方法名 说明
    Socket accept() 监听要连接到此的套接字并接受它
  • 注意事项

    1. accept方法是阻塞的,作用就是等待客户端连接
    2. 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接
    3. 针对客户端来讲,一般是先往外写的,所以是输出流
      针对服务器来讲,一般是先往里读的,所以是输入流
    4. read方法也是阻塞的
    5. 客户端在关流的时候,还多了一个往服务器写结束标记的动作
    6. 最后一步断开连接,通过四次挥手协议保证连接终止

  • 步骤
  1. 创建serversocket对象
  2. 调用 accept等待 连接
  3. 获取输入流 用来接收客户端发来的数据
  4. 获取输出流 给客户端回复数据
  5. 关闭流

  • 代码实现
package link.xiaomo.test7;

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

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

        //1 创建serversocket对象
        ServerSocket ss = new ServerSocket(20000);
        //2 调用  accept等待  连接
        //一旦有连接过来,accept方法就会建立连接 然后返回一个socket对象 用来处理此次连接请求
        System.out.println("等待连接");
        Socket accept = ss.accept();
        System.out.println("连接成功, 来自于:" + accept.getRemoteSocketAddress());
        //3 获取输入流 用来接收客户端发来的数据
        InputStream is = accept.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);//等待接收数据
        System.out.println("接收到数据" + new String(bytes, 0, len));
        //4 获取输出流 给客户端回复数据
        OutputStream os = accept.getOutputStream();
        os.write("hello".getBytes());
        System.out.println("回复了数据");
        //5 关闭流
        os.close();  //关闭的输出流
        is.close(); //关闭的是输入流
        accept.close(); //关闭的是处理本次连接请求的 socket对象
        ss.close(); // 关闭是服务器
    }
}

1.4. 传输结束标记

当我们接收数据时,不知道有多少数据,所以使用循环的方式接收。那么我们要怎么知道对方的数据已经传输完毕了呢?

由于接收方数循环接收,发送方一定要把数据发送完后,再发送一个结束标记,使用socket对象调用shutdownOutput方法

1.4.1. 客户端代码

  • 注意socket.shutdownOutput();
package link.xiaomo.test8;

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

public class Client {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1创建客户端的  Socket对象
        //参1和参2 表示当前要连接的服务器的ip和端口
        Socket socket = new Socket("127.0.0.1", 20000);

        //2 获取输出流(默认是字节流) 用来发送数据
        OutputStream os = socket.getOutputStream();
        //发送 文字  注意要转为字节数组
        os.write("你好".getBytes());
        //shutdownOutput只会关闭当前的输出流
        //这样 对方接收的代码就会得到一个-1的结束标记
        socket.shutdownOutput();

        //3 获取输入流 用来接收服务器的数据
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        //这里会等待接收数据
        int len;
        while ((len = is.read(bytes)) != -1) {
            //打印数据
            System.out.println(new String(bytes, 0, len));
        }

        //4 关闭流
        os.close();
        is.close();
        socket.close();
    }
}

1.4.2. 服务端代码

  • 注意 accept.shutdownOutput();
package link.xiaomo.test8;

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

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

        //1 创建serversocket对象
        ServerSocket ss = new ServerSocket(20000);
        //2 调用  accept等待  连接
        System.out.println("等待连接");
        Socket accept = ss.accept();
        System.out.println("连接成功, 来自于:" + accept.getRemoteSocketAddress());
        //3 获取输入流 用来接收客户端发来的数据
        InputStream is = accept.getInputStream();
        byte[] bytes = new byte[1024];
        int len;//等待接收数据
        while ((len = is.read(bytes)) != -1) {
            System.out.println("接收到数据" + new String(bytes, 0, len));
        }
        //4 获取输出流 给客户端回复数据
        OutputStream os = accept.getOutputStream();
        os.write("hello".getBytes());
        //关闭输出流 对方才能读取到-1 这个结束的标记
        accept.shutdownOutput();
        System.out.println("回复了数据");

        //5 关闭流
        os.close();  //关闭的输出流
        is.close(); //关闭的是输入流
        accept.close(); //关闭的是处理本次连接请求的 socket对象
        ss.close(); // 关闭是服务器
    }
}

image-20230216161712500

1.5. TCP文件上传案例

  • 案例需求

    客户端:连接服务器, 然后给服务器传输一个文件,然后接受服务器回复信息.

    服务器:接收客户端发来的文件数据写入本地文件,给出反馈

  • 案例分析

    • 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用shutdownOutput()方法告知服务端传输结束,会给服务器一个结束标记
    • 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后。使用输出流给客户端反馈信息
    • 客户端接受服务端的回馈信息
  • 相关方法

    名字 描述
    void shutdownOutput() 禁止用此套接字的输出流
  • 代码实现

package link.xiaomo.test9;

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

//服务器 接收客户端传来的文件. 保存到本地
public class Server {
    public static void main(String[] args) {
        try ( //1 创建服务器的socket对象
              ServerSocket ss = new ServerSocket(20000);
              //2 接收客户端的连接
              Socket accept = ss.accept();
              //4 创建一个文件的字节输出流 用来把接收的数据 保存到本地  这里保存到了桌面
              FileOutputStream fos = new FileOutputStream("C:\\Users\\halon\\Desktop\\111.png");

        ) {

            //3 获取网络输入流 用来接收客户端传来的文件数据
            InputStream is = accept.getInputStream();
            byte[] bytes = new byte[1024 * 8];
            int len;

            //读取数据 保存到本地
            while ((len = is.read(bytes)) != -1) {
                //把接收的数据 保存到本地
                fos.write(bytes, 0, len);
            }
            //5 获取网络输出流 给客户端回复一个  上传成功
            OutputStream os = accept.getOutputStream();
            os.write("上传成功".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

package link.xiaomo.test9;

import java.io.*;
import java.net.Socket;

//客户端 把mymodule里的111.png上传到服务器
public class Client {
    public static void main(String[] args) {
        try ( //1创建socket对象 连接服务器
              Socket socket = new Socket("127.0.0.1", 20000);
              //2 创建字节输入流读取111.png
              FileInputStream fis = new FileInputStream("mymodule/111.png");) {

            //3 读取文件内容  要发送出去
            byte[] bytes = new byte[1024 * 8];
            int len;
            //4获取网络输出流
            OutputStream os = socket.getOutputStream();
            while ((len = fis.read(bytes)) != -1) {
                //5 把读取的数据 通过输出流发送出去
                os.write(bytes, 0, len);
            }
            //5结束输出流 让对方得到结束标记
            socket.shutdownOutput();

            //6 获取网络输入流 用来得到服务器的回复数据
            InputStream is = socket.getInputStream();
            byte[] bytesReceive = new byte[1024];
            int len1 = is.read(bytesReceive);
            System.out.println(new String(bytesReceive, 0, len1));

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

1.5.1. 优化一:随机文件名

  • 问题:

    第二次上传文件的时候,会把第一次的文件给覆盖。

  • 解决方案

    UUID. randomUUID()方法生成随机的文件名

  • 代码实现

package link.xiaomo.test9;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

//服务器 接收客户端传来的文件. 保存到本地
public class Server {
    public static void main(String[] args) {
//        利用了UUID给保存的文件 起一个不重复的名字
        String uuid = UUID.randomUUID().toString().replace("-", "");
        System.out.println(uuid);

        try ( //1 创建服务器的socket对象
              ServerSocket ss = new ServerSocket(20000);
              //2 接收客户端的连接
              Socket accept = ss.accept();
              //4 创建一个文件的字节输出流 用来把接收的数据 保存到本地  这里保存到了桌面
              FileOutputStream fos = new FileOutputStream("C:\\Users\\halon\\Desktop\\" + uuid);
        ) {

            //3 获取网络输入流 用来接收客户端传来的文件数据
            InputStream is = accept.getInputStream();
            byte[] bytes = new byte[1024 * 8];
            int len;

            //读取数据 保存到本地
            while ((len = is.read(bytes)) != -1) {
                //把接收的数据 保存到本地
                fos.write(bytes, 0, len);
            }
            //5 获取网络输出流 给客户端回复一个  上传成功
            OutputStream os = accept.getOutputStream();
            os.write("上传成功".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.5.2. 优化二:服务端处理后不停止

  • 问题:

    • 服务器目前只能处理一个客户端请求,接收完一个文件之后,服务器就关闭了。
  • 解决方案

    • 使用循环, 注意哪些代码写在循环里面
  • 代码实现

package link.xiaomo.test10;

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

//服务器 接收客户端传来的文件. 保存到本地
public class Server {
    public static void main(String[] args) {

        //1 创建服务器的socket对象
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(20000);
        } catch (IOException e) {
            e.printStackTrace();
        }

        FileOutputStream fos = null;
        Socket accept = null;
        while (true) {
            try {
                //2 接收客户端的连接
                accept = ss.accept();
                System.out.println("接收到连接");
                //3 获取网络输入流 用来接收客户端传来的文件数据
                InputStream is = accept.getInputStream();
                byte[] bytes = new byte[1024 * 8];
                int len;

                //4 创建一个文件的字节输出流 用来把接收的数据 保存到本地  这里保存到了桌面
                // 利用了UUID给保存的文件 起一个不重复的名字dd
                String uuid = UUID.randomUUID().toString().replace("-", "");
                fos = new FileOutputStream("C:\\Users\\halon\\Desktop\\" + uuid);
                //读取数据 保存到本地
                System.out.println("开始接收文件");
                while ((len = is.read(bytes)) != -1) {
                    //把接收的数据 保存到本地
                    fos.write(bytes, 0, len);
                }
                System.out.println("接收文件完毕");
                //5 获取网络输出流 给客户端回复一个  上传成功
                OutputStream os = accept.getOutputStream();
                os.write("上传成功".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
                if (accept != null) {
                    try {
                        accept.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }

    }
}

1.5.3. 优化三:结合多线程

  • 问题

    使用循环虽然可以让服务器处理多个客户端请求。但是还是无法同时跟多个客户端进行通信。

  • 解决方案

    开启多线程处理

  • 代码实现

package link.xiaomo.test11;

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

class ProcessAccept implements Runnable {
    public Socket accept;

    public ProcessAccept(Socket accept) {
        this.accept = accept;
    }

    @Override
    public void run() {
        FileOutputStream fos = null;
        try {
            //3 获取网络输入流 用来接收客户端传来的文件数据
            InputStream is = accept.getInputStream();
            byte[] bytes = new byte[1024 * 8];
            int len;
            //4 创建一个文件的字节输出流 用来把接收的数据 保存到本地  这里保存到了桌面
            // 利用了UUID给保存的文件 起一个不重复的名字dd
            String uuid = UUID.randomUUID().toString().replace("-", "");
            fos = new FileOutputStream("C:\\Users\\halon\\Desktop\\" + uuid);
            //读取数据 保存到本地
            System.out.println("开始接收文件");
            while ((len = is.read(bytes)) != -1) {
                //把接收的数据 保存到本地
                fos.write(bytes, 0, len);
            }
            System.out.println("接收文件完毕");
            //5 获取网络输出流 给客户端回复一个  上传成功
            OutputStream os = accept.getOutputStream();
            os.write("上传成功".getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (accept != null) {
                try {
                    accept.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

//服务器 接收客户端传来的文件. 保存到本地
public class Server {
    public static void main(String[] args) {

        //1 创建服务器的socket对象
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(20000);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Socket accept = null;
        while (true) {
            try {
                //2 接收客户端的连接
                System.out.println("等待连接");
                accept = ss.accept();
                System.out.println("接收到连接");
                //把当前处理客户端连接的accept对象 传入到一个子线程任务中去运行
                new Thread(new ProcessAccept(accept)).start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

1.5.4. 优化四:结合线程池

  • 需求

    使用多线程虽然可以让服务器同时处理多个客户端请求。但是资源消耗太大。

  • 解决方案

    加入线程池

  • 代码实现

package link.xiaomo.test12;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class ProcessAccept implements Runnable {
    public Socket accept;

    public ProcessAccept(Socket accept) {
        this.accept = accept;
    }

    @Override
    public void run() {
        FileOutputStream fos = null;
        try {
            //3 获取网络输入流 用来接收客户端传来的文件数据
            InputStream is = accept.getInputStream();
            byte[] bytes = new byte[1024 * 8];
            int len;
            //4 创建一个文件的字节输出流 用来把接收的数据 保存到本地  这里保存到了桌面
            // 利用了UUID给保存的文件 起一个不重复的名字dd
            String uuid = UUID.randomUUID().toString().replace("-", "");
            fos = new FileOutputStream("C:\\Users\\halon\\Desktop\\" + uuid);
            //读取数据 保存到本地
            System.out.println("开始接收文件");
            while ((len = is.read(bytes)) != -1) {
                //把接收的数据 保存到本地
                fos.write(bytes, 0, len);
            }
            System.out.println("接收文件完毕");
            //5 获取网络输出流 给客户端回复一个  上传成功
            OutputStream os = accept.getOutputStream();
            os.write("上传成功".getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (accept != null) {
                try {
                    accept.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

//服务器 接收客户端传来的文件. 保存到本地
public class Server {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量
                10,   //线程池的总数量
                60,   //临时线程空闲时间
                TimeUnit.SECONDS, //临时线程空闲时间的单位
                new ArrayBlockingQueue<>(5),//阻塞队列
                Executors.defaultThreadFactory(),//创建线程的方式
                new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
        );
        //1 创建服务器的socket对象
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(20000);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Socket accept = null;
        while (true) {
            try {
                //2 接收客户端的连接
                System.out.println("等待连接");
                accept = ss.accept();
                System.out.println("接收到连接");
                //把当前处理客户端连接的accept对象的任务 交给线程池
                pool.execute(new ProcessAccept(accept));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

1.6. 使用字符流完成TCP

  • 通过转换流 转为缓冲字符流
    • BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream(), "gbk"));
    • BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream(), "gbk"));
  • 因为是缓冲流 所以写完数据要flush

1.6.1. 服务端

package link.xiaomo.test3;

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

public class Server {
    public static void main(String[] args) throws IOException {
        //tcp服务器代码
        //1创建ServerSocket对象 指定服务器运行的端口
        ServerSocket ss = new ServerSocket(20000);
        //2 accept方法等待客户端连接  这个方法是阻塞的 如果连接成功会返回一个socket对象
        //这个socket对象用来专门处理此次客户的连接
        System.out.println("等待客户端连接 ");
        Socket accept = ss.accept();
        System.out.println("收到一个客户端的连接,来自于:" +
                accept.getRemoteSocketAddress());

        //3 先收一条数据
        BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream(), "gbk"));
        char[] cs = new char[10];
        int len;
        while ((len = br.read(cs)) != -1) {
            System.out.println("收到数据" + new String(cs, 0,
                    len));
        }

        System.out.println("服务器给客户端回复数据:");
        //4 发一条数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream(), "gbk"));
        bw.write("你好fsdfdsjFJdsljfL发到上交垮落法sdfsdklfjkSLSF");
        bw.flush();
        accept.shutdownOutput();

        br.close();
        bw.close();
        accept.close();//仅此次服务结束
        ss.close();//表示服务器关闭
    }
}

1.6.2. 客户端

package link.xiaomo.test3;

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        //1 创建socket对象 参1 是要连接的服务器的ip 参2 是要连接的服务器的端口
        Socket socket = new Socket("127.0.0.1", 20000);

        //2 获取输出流 用来发送数据  获取的是字节流对象 然后通过转换流 最后转为BufferedWriter对象
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "gbk"));
        //3 发送数据
        bw.write("你好fdsfds京东方思考理解分离式的fsdjflksjlkfs");
        bw.flush();
        //发送完数据 要给接收方再发一个结束标记 表示此次发送结束了  shutdownOutput
        socket.shutdownOutput();

        //4 接收服务器返回的数据
        //获取输入流 通过转换流InputStreamReader 最后转为BufferedReader对象
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "gbk"));
        char[] cs = new char[10];
        int len;
        System.out.println("接收服务器返回的数据:");
        while ((len = br.read(cs)) != -1) {
            System.out.println(new String(cs, 0, len));
        }

        bw.close();
        br.close();
        socket.close();
    }
}

1.7. 多线程服务器 同时接受多个客户端连接

  • 有于命令窗口只有一个 ,会导致客户多的时候,只能回复一个
//同时可以接收多个客户端连接

//服务器的发送功能
class ServerSend implements Runnable {

    //处理此次服务的socket对象 通过构造方法传入
    private Socket accept;
    Scanner sc = new Scanner(System.in);

    public ServerSend(Socket accept) {
        this.accept = accept;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("服务器给客户端回复数据:");
            try {
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream(), "gbk"));
                String text = sc.nextLine();//输入服务器回复的话
                //4 发一条数据
                bw.write(text);
                bw.flush();
                //accept.shutdownOutput(); 循环发送不能关闭
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

//服务器的接收功能
class ServerReceive implements Runnable {
    //处理此次服务的socket对象 通过构造方法传入
    private Socket accept;

    public ServerReceive(Socket accept) {
        this.accept = accept;
    }

    @Override
    public void run() {
        //3 先收一条数据
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream(), "gbk"));
            char[] cs = new char[10];
            int len;
            while ((len = br.read(cs)) != -1) {
                System.out.println("收到数据" + new String(cs, 0,
                        len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class Server {
    public static void main(String[] args) throws IOException {
        //tcp服务器代码
        //1创建ServerSocket对象 指定服务器运行的端口
        ServerSocket ss = new ServerSocket(20000);

        while (true) {
            //2 accept方法等待客户端连接  这个方法是阻塞的 如果连接成功会返回一个socket对象
            //这个socket对象用来专门处理此次客户的连接
            System.out.println("等待客户端连接 ");
            Socket accept = ss.accept();
            System.out.println("收到一个客户端的连接,来自于:" +
                    accept.getRemoteSocketAddress());

            new Thread(new ServerReceive(accept)).start();
            new Thread(new ServerSend(accept)).start();
            //accept.close();//仅此次服务结束
            //ss.close();//这里服务器不能关闭 因为要循环回去 接收下一个客户
        }
    }
}
如人饮水,冷暖自知。
最后更新于 2023-08-05