鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > erlang > >

Java套接字Socket

来源:互联网 作者:佚名 时间:2016-06-11 09:26
这篇博客是本人学习《Java网络程序设计》书中第4章套接字的学习总结。初学者网友学习这篇Java套接字文章,如果难于理解文章前面理论部分,可以先运行后面的程序,边看运行后面的程序边理解前面的原理,这对初学者是最好的方法。所有源代码都在文章后面我的gi

这篇博客是本人学习《Java网络程序设计》书中第4章套接字的学习总结。初学者网友学习这篇Java套接字文章,如果难于理解文章前面理论部分,可以先运行后面的程序,边看运行后面的程序边理解前面的原理,这对初学者是最好的方法。所有源代码都在文章后面我的github链接代码中。
——惠州学院 13网络工程 吴成兵 20160607

1 流套接字概述

套接字(Socket)是由加利福尼亚大学伯克利分校首创的(University of California, Berkeley),它允许程序把网络连接看成一个(Stream),可以向这个流写入字节,也可以从这个流读取字节,也叫流套接字
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。(百度百科)
这里写图片描述
Socket是网络上运行的两个程序间双向通信的一端,这既可以接受请求,也可以发送请求。可以认为Socket是应用程序创建的一个港口码头,应用程序只要把装在货物的集装箱(要发送的数据)放在码头上,就算完成了货物的运送,剩下的工作就由货运公司(驱动程序)去处理了。对接收方来说,应用程序也要创建一个码头,然后就一直等待该码头的货物到达,最后从码头上取走货物(数据)。
java.net.Socket类是Java的基础类,用于执行客户端TCP(传输控制流协议)的操作。套接字有两种:一种套接字在服务器创建的,叫做服务器套接字(ServerSocket);还有一种在客户端被创建的,叫做客户端套接字(Socket)。

2 服务器套接字(ServerSocket)

2.1 ServerSocket的工程过程

(1) 用ServerSocket()方法在指定端口创建一个新的ServerSocket对象。
(2) ServerSocket对象调用accept()方法在指定的端口监听到来的连接。accept()一直处于阻塞状态,直到有客户端试图建立连接。这时,accept()方法返回连接客户端与服务器的Socket对象。
(3) 调用getInputStream()方法或者getOutputStream()方法或者两者全调用建立与客户端交互的输入流和输出流。具体情况要看服务器的类型而定。
(4) 服务器与客户端根据一定的协议交互,直到关闭连接。
(5) 服务器、客户端或两者都要关闭连接。
(6) 服务器回到第(2)步,继续监听下一次的连接。

2.2 ServerSocket构造方法

(1) public ServerSocket(int port) throws IOException, BindException
(2) public ServerSocket(int port, int queuelength) throws IOException, BindException
(3) public ServerSocket(int port, int queuelength, InetAddress bindaddress) throws IOException, BindException
客户端指定的端口号一定要和这个port一样。这些构造方法允许指定端口,用来保存到来连接请求队列的长度,绑定本地网络的地址。默认最大连接数目queuelength为50。

2.3 ServerSocket常用方法

(1)public Socket accept() throws IOException
该方法采用阻塞方式监听,直到有信息传过来,它才会返回一个Socket对象。接下来,服务器就可以利用这个Socket对象与客户端进行通信了。
(2)public Socket close() throws IOException
实现了服务器套接字后要使用close()关闭它。

3 客户端套接字(Socket)

3.1 Socket的7种基本操作

(1) 连接到远程服务器。
(2) 绑定到端口。
(3) 接收从远程服务器来的绑定端口上的连接。
(4) 监听到达的数据。
(5) 发送数据。
(6) 接收数据。
(7) 关闭连接。

3.2 Socket构造方法

(1) public Socket(String host, int port) throws unknowHostException, IOException
(2) public Socket(InetAddress host, int port) throws IOException
建立一个到服务器host、端口号port的套接字,连接到远程主机。

3.3 Socket常用方法

(1) void connect(SocketAddress endpoint)
将套接字连接到服务器
(2) void connect(SocketAddress endpoint, int timeout)
将套接字连接到服务器,timeout指定超时时间
(3) InetAddress getLocalAddress()
获得套接字连接的本地地址
(4) InetAddress getInetAddress()
获得套接字连接的远程地址
(5) Int getPort()
获得套接字连接的远程端口
(6) InputStream getInputStream()
获得套接字所用的输入流
(7) OutputStream getOutputStream()
获得套接字所用的输出流
(8) void close() //public synchronized void close()throws IOExption
关闭连接

4 Socket编程示例

4.1 一对一的通信

在本程序中,客户端从命令行输入一个半径值并传送到服务器。服务器根据这个半径值,计算出圆面积发送给客户,客户端显示这个值;客户端输入“end”命令结束通信。
服务器端
客户端

4.1.1 服务器程序

//--------------- 文件名:Server.java ---------------------------------
package _4_3Socket编程示例._4_4_1一对多的通信;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * _4_3Socket编程示例._4_4_1一对多的通信
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: Server.java
 * @author : Wu_Being <1040003585@qq.com>
 * Date/Time: 2016-6-7/下午02:35:45
 * Description: 服务器端接收客户端圆半径,计算圆的面积
 */
public class Server {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println("S等待连接......");
        try {
            //创建端口号9955的服务器套接字
            ServerSocket serverSocket = new ServerSocket(9955);
            //监听来处客户的连接请求
            Socket socket=serverSocket.accept();
            System.out.println("S连接请求来自:"+socket.getInetAddress().getHostAddress());
            //创建数据输入输出流
            DataInputStream dis=new DataInputStream(socket.getInputStream());
            DataOutputStream dos=new DataOutputStream(socket.getOutputStream());

            boolean goon=true;
            while(goon){        
                String string=dis.readUTF();            //从socket中读取数据
                if(string.equals("end")==false){
                    string=dealWith(string);            //服务器执行特定功能
                    dos.writeUTF(string);               //向socket dos写数据
                    dos.flush();                        //清空缓冲区,立即发送
                    System.out.println("S计算结果("+string+")已经发送");
                }else{
                    goon=false;
                    dos.writeUTF("end");
                    dos.flush();
                }

            }

            //关闭socket和流
            serverSocket.close();
            dis.close();
            dos.close();

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


    /**
     * 服务器执行特定功能函数,这里是个计算圆面积的功能
     * @param string 圆的半径
     * @return 圆的面积
     */
    public static String dealWith(String string){
        System.out.print("S接收到的半径值("+string+");");  
        double radius=0.0;
        try {
            radius=Double.parseDouble(string);          
        } catch (NumberFormatException e) {
            return "输入数据格式不对!";
        }
        if(radius<0)return "数据数据不能小于0!";
        double area=radius*radius*Math.PI;
        return Double.toString(area);

    }

}

4.1.2 客户端程序

//--------------- 文件名:Client.java ---------------------------------
package _4_3Socket编程示例._4_4_1一对多的通信;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * _4_3Socket编程示例._4_4_1一对多的通信
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: Client.java
 * @author : Wu_Being <1040003585@qq.com>
 * Date/Time: 2016-6-7/下午02:07:58
 * Description: 客户端输入圆的半径,发送到用服务器计算,并收到结果
 */
public class Client {

    public static void main(String[] args) {

        try {
            //创建连接到服务器的socket,服务器IP和端口如下
            Socket socket = new Socket("localhost",9955);
            //将数据输入输出流连接到socket上
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

            System.out.println("C输入半径数值发送到服务器,输入end结束");
            boolean goon=true;
            //数据从终端输入
            BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
            //反复读用户的数据并计算
            while(goon){
                String outString=bf.readLine();     //数据从终端输入
                dos.writeUTF(outString);            //写到Socket dos中
                dos.flush();                        //清空缓冲区,立即发送
                String inString=dis.readUTF();      //从Socket dis中读数据
                if(!inString.equals("end")){
                    System.out.println("C从服务器返回的结果是:"+inString);
                }else{
                    goon=false;
                    System.out.println("C服务结束!!!");
                }
            }
            //关闭socket和流
            socket.close();
            dis.close();
            dos.close();

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

}

4.2 一对多的通信

这个服务器同时能响应多个客户端请求。ServerSocket对象的accept()方法每当监听到一个连接请求时,就会产生一个Socket对象,所以只要此方法反复监听客户请求,就可以为每一个客户生成一个专用的Socket对象进行通信。服务器主程序和线程程序如下,客户端程序和一对一通信的Client.java程序一样。
服务器端
客户端1
客户端2

4.2.1 服务器主程序

//------------------- 文件名MultiServer.java -----------------------
package _4_3Socket编程示例._4_4_2一对多的通信;

import java.net.ServerSocket;
import java.net.Socket;

/**
 * _4_3Socket编程示例._4_4_2一对多的通信;
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: MultiServer.java
 * @author : Wu_Being <1040003585@qq.com>
 * Date/Time: 2016-6-7/下午11:12:51
 * Description: 这是主程序,它只要简单地启动线程就可以了。
 */
public class MultiServer {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("S启动......");
        try {
            ServerSocket serverSocket = new ServerSocket(9955);
            Socket connectToClientSocket=null;
            int i=0;
            while(++i!=0){
                //等待客户端的请求
                connectToClientSocket=serverSocket.accept();
                //每次请求都启动一个线程来处理
                new ServerThread(connectToClientSocket,i);
            }
        }
        catch (Exception e) {
        }   
    }
}

4.2.2 服务器线程程序

//------------------- 文件名ServerThread.java -----------------------
package _4_3Socket编程示例._4_4_2一对多的通信;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * _4_3Socket编程示例._4_4_2一对多的通信;
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: ServerThread.java
 * @author : Wu_Being <1040003585@qq.com>
 * Date/Time: 2016-6-7/下午11:11:32
 * Description: 利用本线程来完成服务器与客户端的通信工程
 */
public class ServerThread extends Thread {

    private Socket connectToClientSocket;
    private DataInputStream inFromClient;
    private DataOutputStream outToClient;
    private int clientname=1;   //不能用static,否则所用线程会共用clienti变量

    /**
     * 构造函数1
     * @param socket
     * @throws IOException
     */
    public ServerThread(Socket socket) throws IOException {
        super();
        connectToClientSocket=socket;
        inFromClient=new DataInputStream(connectToClientSocket.getInputStream());
        outToClient=new DataOutputStream(connectToClientSocket.getOutputStream());
        System.out.println("S连接请求来自:"+connectToClientSocket.getInetAddress().getHostAddress());
        start();        //启动run()方法
    }
    /**
     * 构造函数2
     * @param socket
     * @param ci 连接的第i个客户端
     * @throws IOException
     */
    public ServerThread(Socket socket,int cname) throws IOException {
        super();
        connectToClientSocket=socket;
        clientname=cname;
        inFromClient=new DataInputStream(connectToClientSocket.getInputStream());
        outToClient=new DataOutputStream(connectToClientSocket.getOutputStream());
        System.out.println("S连接请求来自"+clientname+":"+connectToClientSocket.getInetAddress().getHostAddress());
        start();        //启动run()方法
    }

    /**
     * 在run()方法与客户端通信
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        System.out.println("S连接Clinet"+clientname+"......");
        try {
            boolean goon=true;
            while(goon){        
                String string=inFromClient.readUTF();           //从socket中读取数据
                if(string.equals("end")==false){
                    string=dealWith(string,clientname);                 //服务器执行特定功能
                    outToClient.writeUTF(string);               //向socket dos写数据
                    outToClient.flush();                        //清空缓冲区,立即发送
                    System.out.println("SC"+clientname+"计算结果("+string+")已经发送");
                }else{
                    goon=false;
                    outToClient.writeUTF("end");
                    outToClient.flush();
                    System.out.println("SC"+clientname+"服务结束!!!");
                }

            }

            //关闭socket和流
            connectToClientSocket.close();
            inFromClient.close();
            outToClient.close();

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

    }

    /**
     * 服务器执行特定功能函数,这里是个计算圆面积的功能
     * @param string 圆的半径
     * @return 圆的面积
     */
    public static String dealWith(String string,int ci){
        System.out.print("SC"+ci+"接收到的半径值("+string+");");   
        double radius=0.0;
        try {
            radius=Double.parseDouble(string);          
        } catch (NumberFormatException e) {
            return "输入数据格式不对!";
        }
        if(radius<0)return "数据数据不能小于0!";
        double area=radius*radius*Math.PI;
        return Double.toString(area);
    }

}

4.3 带GUI界面的聊天程序

当用户输入文字时,程序如何接收对方发来的数据?解决的方法是将接收数据放到独立的线程中,它始终在后台运行,一旦对方发来了数据,就立即显示在界面上。而主界面负责输入文字和发送数据,这样发送和接收数据互不影响。
聊天程序

4.3.1 聊天服务器程程序

package _4_3Socket编程示例._4_4_4简单的聊天程序;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * 
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: .java
 * @author : Wu_Being <1040003585@qq.com>
 * Date/Time: 2016-6-9/下午12:19:57
 * Description:
 */
public class ChatServer implements ActionListener, Runnable {

    JTextField msgTextField;
    JTextArea showTextArea;
    JFrame mainJFrame;
    JButton sentButton;
    JScrollPane jSPane;
    JPanel panel;// 嵌板
    Container container;// 容器

    Thread thread = null;
    ServerSocket serverSocket;
    Socket connectToClientSocket;
    DataInputStream inFromClient;
    DataOutputStream outToClient;

    public ChatServer() {
        // 设置界面,包含容器
        mainJFrame = new JFrame("聊天——服务器端(黑猫顺:爱上你的专业,精通专业技能。)");
        container = mainJFrame.getContentPane();
        // 聊天信息展示框
        showTextArea = new JTextArea();
        showTextArea.setEditable(false); // 不可编辑
        showTextArea.setLineWrap(true); // 自动换行
        jSPane = new JScrollPane(showTextArea);
        // 聊天信息输入框
        msgTextField = new JTextField();
        msgTextField.setColumns(30); // 输入框长度
        msgTextField.addActionListener(this);/**/// ?
        // 发送按键
        sentButton = new JButton("发送");
        sentButton.addActionListener(this);/**/
        // 嵌板有聊天信息输入框和发送按键
        panel = new JPanel();
        panel.setLayout(new FlowLayout());
        panel.add(msgTextField);
        panel.add(sentButton);
        // 容器包含聊天信息展示框和嵌板
        container.add(jSPane, BorderLayout.CENTER);
        container.add(panel, BorderLayout.SOUTH);
        // 主界面,要定义在后面
        mainJFrame.setSize(500, 400);
        mainJFrame.setVisible(true);
        mainJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        try {
            // 创建服务器套接字
            serverSocket = new ServerSocket(9955);
            showTextArea.append("正在等待对话请求..." + getTime() + "\n");
            // 监听客户端的连接
            connectToClientSocket = serverSocket.accept();
            inFromClient = new DataInputStream(connectToClientSocket
                    .getInputStream());
            outToClient = new DataOutputStream(connectToClientSocket
                    .getOutputStream());
            // 启动线程在后台来接收对方的消息
            thread = new Thread(this);
            thread.setPriority(Thread.MAX_PRIORITY);
            thread.start();
            /**
             * public final static int MIN_PRIORITY = 1; public final static int
             * NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10;
             * setPriority的参数在1 - 10 之间就可以, 否则会抛异常.
             * setPriority不一定起作用的,在不同的操作系统不同的jvm上,效果也可能不同。
             * 现在很多jvm的线程的实现都使用的操作系统线程,设置优先级也是使用的操作系统优先级,
             * java层面有10个优先级别,假设操作系统只有3个优先级别, 那么jvm可能将1-4级映射到操作系统的1级,
             * 5-7级映射到操作系统的2级, 剩下的映射到3级,这样的话,在java层面,将优先级设置为5,6,7,其实本质就是一样的了。
             */

        } catch (IOException e) {
            showTextArea.append("对不起,不能创建服务器" + getTime() + "");
            msgTextField.setEditable(false); // 不可编辑
            msgTextField.setEnabled(false); // 不可见
        }

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new ChatServer();
    }

    // 响应按钮事件,发送消息给对方
    @Override
    public void actionPerformed(ActionEvent e) {

        String string = msgTextField.getText();
        if (string.length() > 0) {
            try {
                outToClient.writeUTF(string);
                outToClient.flush();
                showTextArea.append("黑猫顺说(" + string + ")" + getTime() + "\n");
                msgTextField.setText(null);
            } catch (IOException e1) {
                showTextArea.append("你的消息(" + string + ")未能发送出去" + getTime()
                        + "\n");
            }
        }
    }

    @Override
    public void run() {
        try {
            while (true) {
                showTextArea.append("吴兵说(" + inFromClient.readUTF() + ")"
                        + getTime() + "\n");
                Thread.sleep(1000);
            }
        } catch (IOException e) {
        } catch (InterruptedException e) {
            thread.start();
        }
    }

    /**
     * Java代码中获得当前时间
     * 
     * @return 当前时期时间
     */
    private String getTime() {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = format.format(date);
        return time;
    }
}

4.3.2 聊天客户端程程序

package _4_3Socket编程示例._4_4_4简单的聊天程序;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * 
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: .java
 * @author : Wu_Being <1040003585@qq.com>
 * Date/Time: 2016-6-9/下午12:20:18
 * Description:
 */
public class ChatClient implements ActionListener, Runnable {

    JTextField msgTextField;
    JTextArea showTextArea;
    JFrame mainJFrame;
    JButton sentButton;
    JScrollPane jSPane;
    JPanel panel;// 嵌板
    Container container;// 容器

    Thread thread = null;
    ServerSocket serverSocket;
    Socket connectToClientSocket;
    DataInputStream inFromClient;
    DataOutputStream outToClient;

    public ChatClient() {
        // 设置界面,包含容器
        mainJFrame = new JFrame("聊天——客户端(吴兵)");
        container = mainJFrame.getContentPane();
        // 聊天信息展示框
        showTextArea = new JTextArea();
        showTextArea.setEditable(false); // 不可编辑
        showTextArea.setLineWrap(true); // 自动换行
        jSPane = new JScrollPane(showTextArea);
        // 聊天信息输入框
        msgTextField = new JTextField();
        msgTextField.setColumns(30); // 输入框长度
        msgTextField.addActionListener(this);/**/// ?
        // 发送按键
        sentButton = new JButton("发送");
        sentButton.addActionListener(this);/**/
        // 嵌板有聊天信息输入框和发送按键
        panel = new JPanel();
        panel.setLayout(new FlowLayout());
        panel.add(msgTextField);
        panel.add(sentButton);
        // 容器包含聊天信息展示框和嵌板
        container.add(jSPane, BorderLayout.CENTER);
        container.add(panel, BorderLayout.SOUTH);
        // 主界面,要定义在后面
        mainJFrame.setSize(500, 400);
        mainJFrame.setVisible(true);
        mainJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        try {
            // 创建套接字连接到服务器
            connectToClientSocket = new Socket("localhost", 9955);/**/
            inFromClient = new DataInputStream(connectToClientSocket
                    .getInputStream());
            outToClient = new DataOutputStream(connectToClientSocket
                    .getOutputStream());
            showTextArea.append("连接成功,请说话..." + getTime() + "\n");

            // 创建线程在后台来接收对方的消息
            thread = new Thread(this);
            thread.setPriority(Thread.MAX_PRIORITY);
            thread.start();

        } catch (IOException e) {
            showTextArea.append("对不起,没能连接到服务器" + getTime() + "");
            msgTextField.setEditable(false); // 不可编辑
            msgTextField.setEnabled(false); // 不可见
        }

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new ChatClient();
    }

    // 响应按钮事件,发送消息给对方
    @Override
    public void actionPerformed(ActionEvent e) {

        String string = msgTextField.getText();
        if (string.length() > 0) {
            try {
                outToClient.writeUTF(string);
                outToClient.flush();
                showTextArea.append("吴兵说(" + string + ")" + getTime() + "\n");
                msgTextField.setText(null);
            } catch (IOException e1) {
                showTextArea.append("你的消息(" + string + ")未能发送出去" + getTime()
                        + "\n");
            }
        }
    }

    @Override
    public void run() {
        try {
            while (true) {
                showTextArea.append("黑猫顺说(" + inFromClient.readUTF() + ")"
                        + getTime() + "\n");
                Thread.sleep(1000);
            }
        } catch (IOException e) {
        } catch (InterruptedException e) {
            thread.start();
        }
    }

    /**
     * Java代码中获得当前时间
     * 
     * @return 当前时期时间
     */
    private String getTime() {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = format.format(date);
        return time;
    }
}


文中所有源代码链接

网友评论
<