【Java 网络编程】TCP 服务器端 客户端 简单示例

举报
韩曙亮 发表于 2022/01/11 02:08:00 2022/01/11
【摘要】 文章目录 I IntelliJ IDEA 创建 Java 项目II 客户端 Socket 创建III Socket 客户端连接服务器端IV Socket 两个端点信息获取V 控制台人机交互VI S...



I IntelliJ IDEA 创建 Java 项目



Java 程序使用 IntelliJ IDEA 进行开发 ;

创建客户端项目 :

  • 菜单栏 File -> New Project , 弹出 New Project 对话框 , 选择 Gradle 类型下的 java 项目 ;
    在这里插入图片描述

  • 输入 GroupId 和 项目名称 , 一直点击下一步到结束 , 最后一个对话框注意选择项目所在位置 ;
    在这里插入图片描述



II 客户端 Socket 创建



创建 Socket 需要设置超时时长 , 要连接的服务器端的端点信息 , 该端点包括 IP 地址和端口号 ;

            //1. 创建 Socket 对象
            Socket socket = new Socket();
            //2. 设置 Socket 的超时时间
            socket.setSoTimeout(5000);

            //3. 创建连接的端点 , 该端点包括 IP 地址和端口号
            InetSocketAddress inetSocketAddress =
                    new InetSocketAddress(
                            Inet4Address.getLocalHost(),   //本机IP地址
                            8000                      //端口号
                    );

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11


III Socket 客户端连接服务器端



调用 Socket 对象的 connect 方法 , 即可发起对服务器的连接 , 如果连接成功 , 则会继续执行 , 如果连接失败或者超时 , 会抛出异常 ;

            //4.. 连接服务器端点 , 并设置超时时间
            socket.connect(inetSocketAddress, 5000);

  
 
  • 1
  • 2


IV Socket 两个端点信息获取



1. 获取服务器端点的 IP 地址和端口号 : 调用 Socket 对象的 getInetAddress 方法获取服务器端 IP 地址 , 调用 getPort 方法获取服务器端的端口号 ;

            //1. 获取服务器 IP 地址
            InetAddress serverAddress = socket.getInetAddress();
            //2. 获取服务器端口号
            int serverPort = socket.getPort();

  
 
  • 1
  • 2
  • 3
  • 4

2. 获取客户端端点的 IP 地址和端口号 : 调用 Socket 对象的 getLocalAddress 方法 , 可以获取客户端的 IP 地址 , 调用 getLocalPort 方法可以获取客户端的端口号 ;

            //3. 获取客户端 IP 地址
            InetAddress clientAddress = socket.getLocalAddress();
            //4. 获取客户端端口号
            int clientPort = socket.getLocalPort();

  
 
  • 1
  • 2
  • 3
  • 4


V 控制台人机交互



客户端信息获取 ( 人机交互 ) : 控制台中等待用户输入 , BufferedReader 的 readLine 方法 , 可以在控制台中阻塞等待用户输入 , 用户可以在控制台输入信息 , 然后客户端将该信息传给服务器 ; 下面是键盘输入流的创建和使用过程

            //1. 获取控制台输入流
            InputStream is = System.in;
            //2. 该输入流会阻塞 , 等待用户控制台输入
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //4. 阻塞命令行 , 等待用户输入一行数据, 并存入 string 对象中
            String string = br.readLine();

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6


VI Socket 客户端与服务器端交互



服务器 -> 客户端 : 客户端阻塞等待服务器端发送数据 , 从 Socket 中获取 BufferedReader 输入流 , 根据输入流创建 BufferedReader , 调用 readLine 方法阻塞等待服务器传回信息 ;

            //1. 获取服务器端输入流
            cisFromServer = socket.getInputStream();
            //2. 将输入流转为 BufferedReader
            BufferedReader brFromServer = new BufferedReader(new InputStreamReader(isFromServer));
            //6. 阻塞控制台 , 从服务器读取一行数据
            String stringFromServer = brFromServer.readLine();

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

客户端 -> 服务器 : 从 Socket 中获取输出流 OutputStream , 根据该输出流创建 PrintStream , 可以将字符串输出到客户端 ;

            //4. 获取客户端的输出流 , 用于向服务器端写出数据
            OutputStream os = socket.getOutputStream();
            //5. 创建打印流 , 用于向服务器端写出字符
            PrintStream ps = new PrintStream(os);
            //5. 通过打印流 , 将该字符串传输给服务器端
            ps.println(string);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6


VII ServerSocket 服务器端端口监听



创建 ServerSocket 对象 , 只需要指定端口即可 , 不需要指定 IP 地址 , 其 IP 地址就是本机的 IP 地址 , 如果机器有多个 IP 地址 , 如果没有指定 IP 地址 , 那么会监听所有的 IP 地址的指定端口号 ;

            //1. 创建服务器套接字 , 只需要指定端口即可 , 不需要指定 IP 地址
            //      其 IP 地址就是本机的 IP 地址 , 如果机器有多个 IP 地址
            //      如果没有指定 IP 地址 , 那么会监听所有的 IP 地址的指定端口号
            ServerSocket serverSocket = new ServerSocket(8000);

  
 
  • 1
  • 2
  • 3
  • 4


VIII ServerSocket 服务器端参数获取



调用 ServerSocket 对象的 getInetAddress 获取服务器端的 IP 地址 , 调用该对象的 getLocalPort 可以获取服务器端监听的端口号 ;

            //2. 获取服务器端 IP 地址
            InetAddress inetAddress = serverSocket.getInetAddress();

            //3. 获取服务器端口号
            int localPort = serverSocket.getLocalPort();

  
 
  • 1
  • 2
  • 3
  • 4
  • 5


IX ServerSocket 服务器端同时与多个客户端交互方案



将与单个客户端交互的操作封装到线程中 , 每当与一个新的客户端建立连接 , 就开启一个异步线程处理与该客户端之间的交互 ;

客户端处理线程 :

    /**
     * 异步线程 , 处理单个客户端连接
     * 如果多个客户端连接 , 就需要创建多个该类, 同时处理多个客户端连接
     */
    public static class ClientHandler extends Thread{

        //1. 客户端 Socket 连接
        private Socket clientSocket;
        
        public ClientHandler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }
    }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

无限循环等待客户端连接 , 一旦连接成功 , 就开启一个异步线程 ;

            //II. 等待客户端连接, 注意此处是无限循环连接

            while(true){
                //1. 收到一个客户端连接请求 , 获取 客户端 Socket 连接
                Socket clientSocket = serverSocket.accept();

                //2. 将 Socket 连接传入 ClientHandler 线程 , 异步处理与客户端交互操作
                ClientHandler clientHandler = new ClientHandler(clientSocket);

                //3. 启动 与客户端 Socket 交互处理 异步线程
                clientHandler.start();
            }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12


X Socket 客户端代码示例



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

/**
 * 客户端
 */
public class Client {

    /**
     * 客户端入口函数
     * @param args
     */
    public static void main(String[] args){

        try {
            //I. 连接

            //1. 创建 Socket 对象
            Socket socket = new Socket();
            //2. 设置 Socket 的超时时间
            socket.setSoTimeout(5000);

            //3. 创建连接的端点 , 该端点包括 IP 地址和端口号
            InetSocketAddress inetSocketAddress =
                    new InetSocketAddress(
                            Inet4Address.getLocalHost(),   //本机IP地址
                            8000                      //端口号
                    );

            //4.. 连接服务器端点 , 并设置超时时间
            socket.connect(inetSocketAddress, 5000);

            //5. 如果连接成功会继续执行下面的操作, 如果失败会根据失败情况抛出异常


            //II. 获取 Socket 连接两个端点的属性, IP 地址和端口号

            //1. 获取服务器 IP 地址
            InetAddress serverAddress = socket.getInetAddress();
            //2. 获取服务器端口号
            int serverPort = socket.getPort();

            //3. 获取客户端 IP 地址
            InetAddress clientAddress = socket.getLocalAddress();
            //4. 获取客户端端口号
            int clientPort = socket.getLocalPort();


            System.out.println("服务器连接成功\n服务器地址 : " + serverAddress +
                    " , 服务器端口号 : " + serverAddress +
                    "\n客户端地址 : " + clientAddress + " , 客户端端口号 : " + clientPort);

            //向服务器端发送数据
            sendToServer(socket);

            //III. 关闭 Socket 连接
            socket.close();
            System.out.println("客户端 Socket 连接关闭");

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //客户端可能挂了 , 需要重启
        }
    }

    public static void sendToServer(Socket socket){

        try {

            //I. 获取用户控制台输入信息

            //1. 获取控制台输入流
            InputStream is = System.in;
            //2. 该输入流会阻塞 , 等待用户控制台输入
            BufferedReader br = new BufferedReader(new InputStreamReader(is));


            //II. 将用户输入信息上传到服务器

            //4. 获取客户端的输出流 , 用于向服务器端写出数据
            OutputStream os = socket.getOutputStream();
            //5. 创建打印流 , 用于向服务器端写出字符
            PrintStream ps = new PrintStream(os);



            //III. 从服务器获取信息 , 这里循环读取数据, 接收到 服务器端的 quit 字符串才退出

            //1. 获取服务器端输入流
            InputStream isFromServer = socket.getInputStream();
            //2. 将输入流转为 BufferedReader
            BufferedReader brFromServer = new BufferedReader(new InputStreamReader(isFromServer));

            //3. 循环控制变量 , 退出时设置为 false
            boolean isReadFromServer = true;
            while (isReadFromServer){

                //4. 阻塞命令行 , 等待用户输入一行数据, 并存入 string 对象中
                String string = br.readLine();
                //5. 通过打印流 , 将该字符串传输给服务器端
                ps.println(string);

                //6. 阻塞控制台 , 从服务器读取一行数据
                String stringFromServer = brFromServer.readLine();
                //7. 根据服务器返回的数据进行不同操作
                if("quit".equals(stringFromServer)){
                    //停止循环
                    isReadFromServer = false;
                }else{
                    System.out.println(stringFromServer);
                }
            }

            //IV. 释放资源
            br.close();
            ps.close();
            brFromServer.close();



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

    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131


XI Socket 服务器端代码示例



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

/**
 * 服务器端
 */
public class Server {

    /**
     * 服务器端入口函数
     * @param args
     */
    public static void main(String[] args){

        try {
            //I. 创建服务器套接字, 并监听端口

            //1. 创建服务器套接字 , 只需要指定端口即可 , 不需要指定 IP 地址
            //      其 IP 地址就是本机的 IP 地址 , 如果机器有多个 IP 地址
            //      如果没有指定 IP 地址 , 那么会监听所有的 IP 地址的指定端口号
            ServerSocket serverSocket = new ServerSocket(8000);

            //2. 获取服务器端 IP 地址
            InetAddress inetAddress = serverSocket.getInetAddress();

            //3. 获取服务器端口号
            int localPort = serverSocket.getLocalPort();

            System.out.println("服务器开启\nIP 地址 : " + inetAddress + " , 端口号 : " + localPort);


            //II. 等待客户端连接, 注意此处是无限循环连接

            while(true){
                //1. 收到一个客户端连接请求 , 获取 客户端 Socket 连接
                Socket clientSocket = serverSocket.accept();

                //2. 将 Socket 连接传入 ClientHandler 线程 , 异步处理与客户端交互操作
                ClientHandler clientHandler = new ClientHandler(clientSocket);

                //3. 启动 与客户端 Socket 交互处理 异步线程
                clientHandler.start();
            }

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

    }

    /**
     * 异步线程 , 处理单个客户端连接
     * 如果多个客户端连接 , 就需要创建多个该类, 同时处理多个客户端连接
     */
    public static class ClientHandler extends Thread{

        //1. 客户端 Socket 连接
        private Socket clientSocket;

        //2. 循环控制变量
        private boolean loopFlag = true;

        public ClientHandler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {
            super.run();

            //I. 获取客户端相关信息

            //1. 获取客户端的 IP 地址
            InetAddress inetAddress = clientSocket.getInetAddress();

            //2. 获取客户端的端口号
            int port = clientSocket.getPort();

            //3. 打印客户端的信息
            System.out.println("客户端信息 : \nIP 地址 : " + inetAddress + " , 端口号 : " + port);



            try {

                //II. 创建与客户端交互的输入输出流

                //1. 获取客户端 Socket 输出流 , 用于向客户端发送数据
                OutputStream os = clientSocket.getOutputStream();

                //2. 创建打印流 , 用于向客户端输出数据
                PrintStream ps = new PrintStream(os);

                //3. 获取客户端 Socket 输入流 , 用于接收客户端数据
                InputStream is = clientSocket.getInputStream();

                //4. 获取客户端的字符输入流 , 该输入流可以阻塞等待客户端输入
                BufferedReader br = new BufferedReader(new InputStreamReader(is));

                //III. 循环处理与客户端交互的信息

                while (loopFlag){

                    //1. 等待客户端输入
                    String string = br.readLine();

                    //2. 处理客户端输入
                    if("quit".equals(string)){
                        //如果客户端发送退出, 那么停止循环, 将该信息在送回客户端
                        loopFlag = false;
                        //将信息发送到客户端
                        ps.println("quit");
                    }else{
                        //将信息打印到控制台
                        System.out.println(string);
                        //将发送成功信息返回给客户端
                        ps.println("发送成功 , 大小 " + string.length() + " 字节");
                    }

                }

                //IV. 关闭相关资源
                ps.close();
                br.close();

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    //如果出现异常 , 将该 Socket 关闭
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("客户端退出 : \nIP 地址 : " + inetAddress + " , 端口号 : " + port);

        }
    }

}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145


XII 运行客户端与服务器端代码



1. 编译两个 Java 代码 : 选择菜单栏 -> Build -> Rebuild Project 选项 , 获取到 Client.class 和 Server.class 文件 ;

在这里插入图片描述

2. 字节码文件地址 : 编译后的 class 字节码文件在工程目录的 out\production\classes 目录下 ;

在这里插入图片描述

打开两个命令行界面 , 首先进入该目录 , 先运行服务器端 , 在命令行中输入 java Server , 即开启了服务器端 ; 再打开客户端 , 在另一个命令行中运行 java Client , 即开启了客户端 ;

在这里插入图片描述

客户端输入文字 , 与服务器端交互 :

在这里插入图片描述

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/100130633

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。