Java网络编程小结

Java网络编程小结,第1张

Java网络编程小结 1.多线程

1)创建多线程 两种方式
public class thread {
    public static void main(String[] args) {
        new ThreadA().start();
        new Thread(new RA()).start();
    }
}
class ThreadA extends Thread {
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print(i+" ");
        }
    }
}
class RA implements Runnable {
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.print(i+" ");
        }
    }
}
2)线程同步

同一进程的多个线程共享同一片存储空间,为了避免访问冲突的情况,JAVA提供了一种同步机制的办法,用synchronized关键字来修饰共享的代码块,确保在某个时刻只有一个线程允许执行特定的代码块。
当一个线程在访问被synchronized修饰的数据时,会将其“上锁”,阻止其他线程访问。只有当前线程访问完这部分数据后释放锁标志,其他线程才可以访问。
注意:无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或方法当作锁。

3)线程池

①线程池创建

Executors.newFixedThreadPool(int nThreads)
当nThreads=5时,意味着线程池每次只能同时执行5个线程,相应处理5个任务;

ExecutorService pool  = Executors.newFixedThreadPool(5);
②线程池提交

线程池提交任务的方式有两种,分别为execute()和 submit()⽅法

execute()⽅法⽤于提交不需要返回值的任务,⽆法判断任务是否被线程池执⾏成功。

submit()⽅法⽤于提交需要返回值的任务。线程池会返回⼀个future类型的对象,可通过future对象判断线程是否执行成功,获取返回值等。

Runnable task = new MyRunnable();
pool.submit(task);
③线程池关闭

ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow()

shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务

shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

pool.shutdown();
2.TCP和UDP区别

3.TCP 1) 创建TCP服务器的基本过程
String serverName="localhost";
int serverPort = 5000;

ServerSocket ss = new ServerSocket(); //创建服务器监听套接字;
SocketAddress serverAddr = new InetSocketAddress(serverName,serverPort);
ss.bind(serverAddr);  //绑定到服务器工作地址
Socket s = ss.accept(); //监听...如果监听到客户端的连接请求,接受并返回对应的客户端套接字Socket,否则阻塞(一直监听)
2) 创建TCP客户端的基本过程
String serverName="localhost";
int serverPort = 5000;

Socket s = new Socket();
SocketAddress serverAddr=new InetSocketAddress(serverName,serverPort);
s.connect(serverAddr);
3) TCP通信基本编程实现,并掌握如何应用多线程技术实现并行通信 TCP单线程通信 服务端
String serverName="localhost";
int serverPort = 5000;

ServerSocket ss ;
SocketAddress serverAddr=new InetSocketAddress(serverName,serverPort);
ss=new ServerSocket(); //创建服务器监听套接字
ss.bind(serverAddr);  //绑定到服务器工作地址

while (true){
    Socket s = ss.accept(); //监听...如果监听到客户端的连接请求,接受并返回对应的客户端套接字Socket,否则阻塞(一直监听)
    BufferedReader in =
        new BufferedReader(new InputStreamReader(s.getInputStream(),"UTF-8"));
    BufferedWriter out =
        new BufferedWriter(new OutputStreamWriter(s.getOutputStream(),"UTF-8"));

    BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));

    String msg=null;
    while ((msg=in.readLine())!=null){//接收客户端发过来的信息(1行)并返回,否则阻塞(等待客户发送信息过来)
        System.out.println(msg);
        if(msg.equals("bye")){
            break;
        }
        String send_msg = sc.readLine();

        out.write(send_msg);//回复信息给客户端,如果客户发过来的是"hi",则回复的是"echo:hi";将此信息写入到输出流
        out.newline();//再将一个换行符写入到输出流
        out.flush();//刷新流,确认将输出流中的数据发送出去给服务器
    }
    sc.close();
    in.close();
    out.close();
    s.close();
}
客户端
String serverName="localhost";
int serverPort = 5000;

Socket s = new Socket();
SocketAddress serverAddr=new InetSocketAddress(serverName,serverPort);
s.connect(serverAddr);

BufferedReader in =
    new BufferedReader(new InputStreamReader(s.getInputStream(),"UTF-8"));
BufferedWriter out =
    new BufferedWriter(new OutputStreamWriter(s.getOutputStream(),"UTF-8"));

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg=br.readLine())!=null){//从键盘读入信息(1行)并返回,否则阻塞(等待用户从键盘敲入一行)
    out.write(msg);//将信息写入到输出流
    out.newline();//再将一个换行符写入到输出流
    out.flush();//刷新流,确认将输出流中的数据发送出去给服务器
    if(msg.equals("bye")){
        break;
    }
    System.out.println(in.readLine());
}
br.close();
in.close();
out.close();
s.close();
TCP多线程通信 服务端
try (ServerSocket listenSocket = new ServerSocket()){

    SocketAddress serverAddr=new InetSocketAddress("localhost",5000);
    listenSocket.bind(serverAddr); 
    System.out.println("服务器启动成功!开始在localhost的5000端口侦听连接...");

    while (true){
        try {
            //2.监听端口并接收客户端的连接请求  
            Socket clientSocket = listenSocket.accept();

            new ClientThread(clientSocket).start();

        }catch (IOException ex) {
            System.out.println("异常信息:"+ex.getMessage());
        }
    }

 } catch (IOException ex) {
     System.out.println("异常信息:"+ex.getMessage());
 }
多线程ClientThread
public class ClientThread extends Thread {
	Socket socket;   
	public ClientThread (Socket socket){
		this.socket = socket;
	}
	public void run(){
		
		try( //带资源的try
			BufferedReader in = new BufferedReader(new InputStreamReader(
			               		 socket.getInputStream()));           
			BufferedWriter out = new BufferedWriter(new OutputStreamWriter(            
			               		  socket.getOutputStream()))
		){
	        System.out.println("客户机连接成功!客户机地址和端口:"+socket.getRemoteSocketAddress());
	             
	        String recv_msg="";  
	        //从客户机接收字符串
	        while((recv_msg=in.readLine())!=null){
	
	            System.out.println("服务器收到字符串:"+recv_msg);
	            //向客户机回送字符串 
	            out.write(recv_msg);         
	            out.newline();
	            out.flush(); 
	            System.out.println("服务器回送字符串成功:"+recv_msg);
	            
	        	if(recv_msg.equals("bye")){
	        		break;
	        	}
	        }
	    } catch (IOException ex) {
	           System.out.println("异常信息"+ex.getMessage());
	    } finally {
	        //关闭套接字和流
	        try {  
	            if (socket != null)   socket.close(); 
	        } catch (IOException ex) {
	           System.out.println("异常信息"+ex.getMessage());
	        }
		}
		
	}
}
客户端
String serverName="localhost";
int serverPort = 5000;

Socket s = new Socket();
SocketAddress serverAddr=new InetSocketAddress(serverName,serverPort);
s.connect(serverAddr);

BufferedReader in =
    new BufferedReader(new InputStreamReader(s.getInputStream(),"UTF-8"));
BufferedWriter out =
    new BufferedWriter(new OutputStreamWriter(s.getOutputStream(),"UTF-8"));

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg=br.readLine())!=null){//从键盘读入信息(1行)并返回,否则阻塞(等待用户从键盘敲入一行)
    out.write(msg);//将信息写入到输出流
    out.newline();//再将一个换行符写入到输出流
    out.flush();//刷新流,确认将输出流中的数据发送出去给服务器
    if(msg.equals("bye")){
        break;
    }
    System.out.println(in.readLine());
}
br.close();
in.close();
out.close();
s.close();
利用线程池去处理多线程 服务端
ExecutorService pool = null;
//1.启动服务器
try (ServerSocket listenSocket = new ServerSocket()){

    SocketAddress serverAddr=new InetSocketAddress("localhost",5000);
    listenSocket.bind(serverAddr); 
    System.out.println("服务器启动成功!开始在localhost的5000端口侦听连接...");

    pool = Executors.newFixedThreadPool(10);

    while (true){
        try {
            //2.监听端口并接收客户端的连接请求  
            Socket clientSocket = listenSocket.accept();

            //new ClientThread(clientSocket).start();
            pool.submit(new ClientThread(clientSocket));

        }catch (IOException ex) {
            System.out.println("异常信息:"+ex.getMessage());
        }
    }

} catch (IOException ex) {
    System.out.println("异常信息:"+ex.getMessage());
} finally{
    if(pool!=null){
        pool.shutdown();
    }
}
多线程ClientThread
public class ClientThread extends Thread {
	Socket socket;   
	public ClientThread (Socket socket){
		this.socket = socket;
	}
	public void run(){
		
		try( //带资源的try
			BufferedReader in = new BufferedReader(new InputStreamReader(
			               		 socket.getInputStream()));           
			BufferedWriter out = new BufferedWriter(new OutputStreamWriter(            
			               		  socket.getOutputStream()))
		){
	        System.out.println("客户机连接成功!客户机地址和端口:"+socket.getRemoteSocketAddress());
	             
	        String recv_msg="";  
	        //从客户机接收字符串
	        while((recv_msg=in.readLine())!=null){
	
	            System.out.println("服务器收到字符串:"+recv_msg);
	            //向客户机回送字符串 
	            out.write(recv_msg);         
	            out.newline();
	            out.flush(); 
	            System.out.println("服务器回送字符串成功:"+recv_msg);
	            
	        	if(recv_msg.equals("bye")){
	        		break;
	        	}
	        }
	    } catch (IOException ex) {
	           System.out.println("异常信息"+ex.getMessage());
	    } finally {
	        //关闭套接字和流
	        try {  
	            if (socket != null)   socket.close(); 
	        } catch (IOException ex) {
	           System.out.println("异常信息"+ex.getMessage());
	        }
		}
		
	}
}
客户端
String serverName="localhost";
int serverPort = 5000;

Socket s = new Socket();
SocketAddress serverAddr=new InetSocketAddress(serverName,serverPort);
s.connect(serverAddr);

BufferedReader in =
    new BufferedReader(new InputStreamReader(s.getInputStream(),"UTF-8"));
BufferedWriter out =
    new BufferedWriter(new OutputStreamWriter(s.getOutputStream(),"UTF-8"));

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg=br.readLine())!=null){//从键盘读入信息(1行)并返回,否则阻塞(等待用户从键盘敲入一行)
    out.write(msg);//将信息写入到输出流
    out.newline();//再将一个换行符写入到输出流
    out.flush();//刷新流,确认将输出流中的数据发送出去给服务器
    if(msg.equals("bye")){
        break;
    }
    System.out.println(in.readLine());
}
br.close();
in.close();
out.close();
s.close();
4)文件传输 客户端-接收端
package file_tcp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class MyFileReceiver {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        ServerSocket ss = new ServerSocket(8000);
        Socket s = ss.accept();

        //构建套接字输入流,接收客户端数据
        DataInputStream in=new DataInputStream(
                new BufferedInputStream(
                        s.getInputStream()));
        //构建套接字输出流,以发送数据给客户端
        BufferedWriter out=new BufferedWriter(
                new OutputStreamWriter(
                        s.getOutputStream()));

        //接收文件名、文件长度
        String filename=in.readUTF(); //文件名
        int fileLen=(int)in.readLong(); //文件长度
        //创建文件输出流
        File f =new File(new Date().getTime() + "." + filename);

        DataOutputStream dos =new DataOutputStream(
                new BufferedOutputStream(
                        new FileOutputStream(f)));

        byte buffer[] = new byte[8096];
        int numRead = 0;
        int numFinished = 0;
        while (numFinished=fileLen) {//文件接收完成?
            out.write("M_DONE"); //回送成功消息
            System.out.println(filename +"  接收成功!");
        }else {
            out.write("M_LOST"); //回送失败消息
            System.out.println(filename +"  接收失败!") ;
        }//end if 
        out.newline();
        out.flush();

        out.close();
        s.close();
        ss.close();
    }

}
服务器-发送端
package file_tcp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyFileSender {

        public static void main(String[] args) throws UnknownHostException, IOException {
                // TODO Auto-generated method stub
                File f = new File("A.txt");

                Socket s =new Socket("localhost",8000);

                //构建套接字输出流,以发送数据给服务器
                DataOutputStream out=new DataOutputStream(
                        new BufferedOutputStream(
                                s.getOutputStream()));
                //构建套接字输入流,接收服务器反馈信息
                BufferedReader br=new BufferedReader(
                        new InputStreamReader(
                                s.getInputStream()));
                //构建文件输入流
                DataInputStream in=new DataInputStream(
                        new BufferedInputStream(
                                new FileInputStream(f)));


                long fileLen=f.length();  //计算文件长度
                //发送文件名称、文件长度
                out.writeUTF(f.getName());
                out.writeLong(fileLen);
                out.flush();
                //传送文件内容
                int numRead=0; //单次读取的字节数
                int numFinished=0; //总完成字节数
                byte[] buffer=new byte[8096];
                while (numFinished 
5)Socket 与 TCP三次握手关系 

客户端connect方法后触发三次握手

握手成功后,在服务器端,对应的连接会加入accept队列.服务器执行accept 方法

只从accept队列取出一个连接进行处理,因此accept 方法与三次握手无关。

4.安全通信 1)SSL的概念,协议的作用,JDK中提供的相关的主要的包及类 概念与作用

SSL:安全套接字层协议 (Secure Sockets Layer,SSL),SSL提供加密,来源认证和数据完整性验证功能,为Web浏览器和TCP C/S提供安全通信。

TLS:传输层安全协议(Transport Layer Security,TLS),SSL升级版

JSSE:Java安全Socket扩展(Java Secure Sockets Extension,JSSE),使用SSL和TLS保护网络通信安全

相关类

安全Socket编程的主要类与接口:

①javax.net.ssl包:

​ SSLServerSocket(ServerSocket的子类)、
​ SSLSocket(Socket的子类)、
​ KeyManagerFactory、TrustManagerFactory、
​ SSLContext、SSLSocketFactory

②Java.security包:

​ KeyStore、MessageDigest、Key等等

2)加密算法

对称加密:加密和解密都使用相同的密钥,如DES、AES;
非对称加密: 加密和解密使用不同的密钥,即,密钥对(公钥、私钥),如RSA;

✳用公钥加密的信息,必须用私钥才能解密;
✳用私钥加密的信息,必须用公钥才能解密;
由于非对称算法加密计算量大,不适合大的数据量, 解决方法是:应用对称密钥加解密数据,应用非对称密钥验证和加解密对称密钥的相关信息。

3) 创建安全客户端与服务器Socket的过程

5.UDP UDP服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

public class Server {

    public static void main(String[] args) {
        //1.启动服务器
        SocketAddress sa = new InetSocketAddress("localhost",8000);
        try (DatagramSocket s = new DatagramSocket(sa)){
            System.out.println("服务器启动成功!开始在localhost的8000端口侦听连接...");

            while (true){
                try {
                    DatagramPacket pin = new DatagramPacket(new byte[512],512);
                    s.receive(pin);
                    String msg = new String(pin.getData(),0, pin.getLength());
                    System.out.println(msg);

                    String replyMsg = "echo: " + msg;
                    DatagramPacket pout  = new DatagramPacket(
                            replyMsg.getBytes(),replyMsg.getBytes().length,pin.getAddress(),pin.getPort());
                    s.send(pout);
                    System.out.println(pin.getAddress()+":"+pin.getPort());

                }catch (IOException ex) {
                    System.out.println("异常信息:"+ex.getMessage());
                }
            }

        } catch (IOException ex) {
            System.out.println("异常信息:"+ex.getMessage());
        }
    }
}
UDP客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

public class Client {
    public static void main(String[] args) {
        DatagramSocket s=null;
        String serverName="localhost";
        int serverPort = 8000;
        BufferedReader br=null;
        try{
            s = new DatagramSocket(0);//系统自动分配一个端口,也可以指定一个端口
            InetSocketAddress ia=new InetSocketAddress(serverName,serverPort);//服务器的地址

            br = new BufferedReader(new InputStreamReader(System.in));//键盘输入
            String msg = null;

            while ((msg = br.readLine())!=null){
                DatagramPacket pout  = new DatagramPacket(msg.getBytes(),msg.getBytes().length,ia);
                s.send(pout);

                DatagramPacket pin = new DatagramPacket(new byte[512],512);
                s.receive(pin);

                //socket收到的数据的长度不一定是pin包定义的数组的大小的,可以通过pin.getLength()取得接收到的数据的长度。
                //将接收到的数据转换成字符串时,可以设定从包里取多少数据,如下:
                System.out.println(new String(pin.getData(),0, pin.getLength()));

                if(msg.equals("bye")){
                    break;
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        } finally{
            try {
                if(s!=null) s.close();
                if(br!=null) br.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

6.组播

组播地址:组播中的一组主机所共享的地址

组播地址是范围在224.0.0.0~239.255.255.255之间的IP地址

224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。 组播通信过程

组播接收端
package multicast;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MultiCastReceiver {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try(MulticastSocket socket = new MulticastSocket(4000)){

            InetAddress ia = InetAddress.getByName("225.0.1.1");
            socket.joinGroup(ia);

            while(true){
                DatagramPacket packet = new DatagramPacket(new byte[512],512);
                socket.receive(packet);
                String msg = new String (packet.getData(), 0, packet.getLength());
                System.out.println(msg);
                if(msg.equals("bye")){
                    break;
                }
            }

            socket.leaveGroup(ia);

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

}

组播发送端
package multicast;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MultiCastSender {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        try(MulticastSocket socket = new MulticastSocket(4000);
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in))){

            InetAddress ia = InetAddress.getByName("225.0.1.1");
            socket.joinGroup(ia);
            //socket.setTimeToLive(0);//如果值为0,则只能在本机传播,而1则只能在本地子网中传播
            socket.setLoopbackMode(true);//参数为disable,当等于false时,组播的信息也发回自己
            new SelfReceiver(socket).start();//接收自己所组播的信息

            String msg = null;
            while ((msg=br.readLine())!=null){
                DatagramPacket p = new DatagramPacket(msg.getBytes(), msg.getBytes().length,
                        ia, 4000);
                socket.send(p);
            }
            socket.leaveGroup(ia);
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

class SelfReceiver extends Thread {
    MulticastSocket socket;
    public SelfReceiver(MulticastSocket socket){
        this.socket = socket;
    }
    public void run(){
        while(true){
            try {
                DatagramPacket packet = new DatagramPacket(new byte[512],512);
                socket.receive(packet);
                String msg = new String (packet.getData(), 0, packet.getLength());
                System.out.println("接收信息:" + msg);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

7.非阻塞I/O 1)服务器和客户端的基本事件

2) 判断哪种事件

key.isAcceptable(): 处理接收连接就绪事件

key.isReadable(): 处理读就绪事件

key.isWritable(): 处理写就绪事件

8. 流

1)分成字节流与字符流,通常图片或音频视频文件用字节流,而文本用字符流,但是如果单纯地复制或传输文本文件,则用字节流
2)基类:字节流的基类是InputStream、OutputStream,字符流是Reader、Writer,为抽象类,不能直接创建对象,
通过重载read、write方法来实现不同的读写 *** 作
3)常用的字节流类和字符流类及其方法的应用,如缓冲流,文件流,数据流
-缓冲流:输入时可以提供readline的方法读取一行字符,输入时可以直接write字符串,需要通过flush()刷新流。
-文件流:当文件不存在时,输入流会异常,而输出流则会创建文件,设置Append参数可以覆盖或追加信息
-数据流:不同数据类型的数据的读写,提供不同的方法,如readUTF, readLong…

9.HTTP:

get常用于向服务器索取信息;post常用于向服务器提交信息

获取网页内容方式 1) 使用openStream方法获取网页html内容

创建URL对象(给定url地址),通过调用openStream()的方法来获取资源

try {
    URL u = new URL("http://xxx/");
    BufferedReader in = null;
    in = new BufferedReader(
        new InputStreamReader(u.openStream(),"UTF-8"));
    String str;
    while ((str= in.readLine())!=null){
        System.out.println(str);
    }
    in.close();
} catch (IOException e) {
    e.printStackTrace();
}

将会输出当前网站的HTML信息

2)利用HttpGet

创建HttpGet对象(给定url地址),通过httpClient.execute(httpGet)获取资源及信息,判断状态码以进入不同的 *** 作处理。

package HTTP;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;


public class httpclient_getHTML {
    public static void main(String[] args) throws IOException {
        String link = "http://xxx";
        String outfile = "test.html";

        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(link);
        

        HttpResponse response = httpClient.execute(httpGet);
        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println(statusCode);
        // 对200状态码进行处理
        if (statusCode == HttpStatus.SC_OK) {
            HttpEntity resEntity = response.getEntity();
            // 获取页面数据并保存到文件
            byte[] pageContent = EntityUtils.toByteArray(resEntity);
            FileOutputStream fout = new FileOutputStream(outfile);
            fout.write(pageContent);
            System.out.println("获取网页成功!");
        } else {
            System.out.println("获取网页失败!");
        }

    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5694335.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存