IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 记录黑历史:Java小项目——多人聊天室(TCP) -> 正文阅读

[网络协议]记录黑历史:Java小项目——多人聊天室(TCP)

实践周的一个小项目:多人聊天室(TCP、多线程、Socket、私聊、广播、多用户)


前言

这个聊天室使用eclipse开发,基于JAVA-SWT开发GUI


提示:以下是本篇文章正文内容,下面案例可供参考

Server

预览

在这里插入图片描述

详细流程图

在这里插入图片描述

代码

代码如下(示例):

package TCP.Server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class MiniServer {

	protected Shell shell;
	private Text text_Port;
	private Text text_message;
	private List list;
	private Button btnStart ;
	private Label lblNewLabel_2;
	private String ip="127.0.0.1";
	static Map<String ,Socket> map=new HashMap<String ,Socket>();
	static ArrayList<Users > UsersList=new ArrayList<>();
	public int ServerPort=4321;
	private boolean ButtonMark=false;
	//true: DisConnect
	//false:Connect
	private final String MessageSplit = "###";
	public static void main(String[] args) {
		try {
			MiniServer window = new MiniServer();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void open() {
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}

	protected void createContents() {
		shell = new Shell();
		shell.setSize(486, 300);
		shell.setText("MiniChatServer");
		shell.addShellListener(new ShellAdapter() {
            public void shellClosed(ShellEvent e) {
            	DisConnect();
                System.exit(0);
            }});
		text_Port = new Text(shell, SWT.BORDER);
		text_Port.setText(ServerPort+"");
		text_Port.setBounds(215, 19, 94, 23);
		
		btnStart = new Button(shell, SWT.NONE);
		btnStart.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				ServerStart start=null;
				if(!ButtonMark)
				{
					btnStart.setText("Stop");
					ButtonMark=!ButtonMark;
					start=new  ServerStart();
					start.start();
				}
				else {
					btnStart.setText("Start!!!");
					ButtonMark=!ButtonMark;
					DisConnect();
//					start.stop();
					text_message.append("服务已关闭\n");
				}
			}
		});
		btnStart.setBounds(351, 10, 109, 41);
		btnStart.setText("Start!!!");
		
		text_message = new Text(shell, SWT.BORDER | SWT.V_SCROLL);
		text_message.setBounds(10, 74, 299, 176);
		
		
		list = new List(shell, SWT.MULTI | SWT.BORDER);
		list.setItems(new String[] {"Server"});
		list.setBounds(351, 73, 109, 176);
		
		
		Label lblNewLabel = new Label(shell, SWT.NONE);
		lblNewLabel.setBounds(171, 22, 30, 17);
		lblNewLabel.setText("\u7AEF\u53E3");
		
		Label lblNewLabel_1 = new Label(shell, SWT.NONE);
		lblNewLabel_1.setBounds(10, 22, 40, 17);
		lblNewLabel_1.setText("\u672C\u673Aip");
		
		lblNewLabel_2 = new Label(shell, SWT.BORDER | SWT.WRAP);
		lblNewLabel_2.setBounds(56, 21, 117, 21);
		lblNewLabel_2.setText(getHostAddress());
	
	}
	//方法区-----------------------
	public void BroadSendMessage(String message)//广播消息
	{
		DataOutputStream out;
		for (Map.Entry<String , Socket> entry : map.entrySet()) {
			try {
				out=new DataOutputStream(entry.getValue().getOutputStream());
				out.writeUTF("Broad"+MessageSplit+message);
				AppendMessage("已发送"+entry.getKey());
			} catch (IOException e) {
			}
        }
		
	}
	public void SendMessage(String name,String message) {//私聊消息
		DataOutputStream out;
		for (Map.Entry<String , Socket> entry : map.entrySet()) {
			if(entry.getKey().equals(name))
			{
				try {
					out=new DataOutputStream(entry.getValue().getOutputStream());
					out.writeUTF("PrivateChat"+MessageSplit+message);
					AppendMessage("已发送私聊"+entry.getKey());
				} catch (IOException e) {
				}
				return;
			}
			
			
        }
	}
	public void SendList()//分发List更新申请
	{
		
			
			AppendMessage("准备发送list");
			String message=new String("Server"+MessageSplit);
			for (Map.Entry<String , Socket> entry : map.entrySet()) {
				message+=entry.getKey()+MessageSplit;
	        }	
			AppendMessage("发送"+message);
			for (Map.Entry<String , Socket> entry : map.entrySet()) {
				try {
				
					DataOutputStream out=new DataOutputStream(entry.getValue().getOutputStream());
					out.writeUTF(message);
					
				} catch (IOException e) {
			
				}	
	        }
			
		
		
	}
	public void AppendListItems(String Items) {//List添加
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				list.add(Items);
			}
			
		});
		
	}
	public void AppendMessage(String Message) {// 往消息框添加消息
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				text_message.append(Message + '\n');
			}
		});
	}
	
	public void SetListItems(String [] List) {// List消息
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				list.setItems(List);
				
			}
		});
	}
	public void SetListItems() {// 更新List消息
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				list.setItems(new String[] {"Server"});	
				for (Map.Entry<String , Socket> entry : map.entrySet()) {
					list.add(entry.getKey());
		        }
				
			}
		});
	}
	public boolean AgreeConnect(String name) {//是否允许连接
		if (map.containsKey(name)) {
			return false;
		}
		return true;
	}
	public void DisConnect(String name)//客户端申请断线
	{
		try {
			map.get(name).close();
			
		} catch (IOException e) {
		}finally {
			for (int i = 0; i < UsersList.size(); i++) {
				if(UsersList.get(i).UserName.equals(name))
				{
					UsersList.remove(i);
					break;
				}
			}
			map.remove(name);
			//列表更新
			SetListItems();
			//分发更新
			SendList();
		}
		
	}
	public void DisConnect() {//服务器主动离线
		SetListItems(new String [] {"Server"});
		for (Map.Entry<String , Socket> entry : map.entrySet()) {
			try {
				DataOutputStream out=new DataOutputStream(entry.getValue().getOutputStream());
				out.writeUTF("DisConnect"+MessageSplit);
			} catch (IOException e) {
			}
			
        }
		for (Users users : UsersList) {
			try {
				users.socket.close();
			} catch (IOException e) {
			}
			users.stop();
		}
		map.clear();
		UsersList.clear();
	}
	String getHostAddress()//获取本机ip地址
	{
		 InetAddress addr;
		try {
			addr = InetAddress.getLocalHost();
			return addr.getHostAddress();
		} catch (UnknownHostException e) {
			return "127.0.0.1";
		}
	}
	//线程区-----------------------
	class ServerStart extends Thread{
		@Override
		public void run() {
			while (true) {
				
				
				try {
//					if(!ButtonMark)
//						this.stop();
					ServerSocket server=new ServerSocket(ServerPort);
					AppendMessage("已开启server,等待连接??");
					Socket socket=server.accept();
					new Users(socket).start();
					
				} catch (IOException e) {
				}
				
			}
			
		}
	}
	
	class Users extends Thread{
		DataInputStream in;
		DataOutputStream out;
		String UserName=null;
		Socket socket;
		Users(Socket socket){
			if(!ButtonMark)
				return;
			try {
				this.socket=socket;
				in=new DataInputStream(socket.getInputStream());
				out=new DataOutputStream(socket.getOutputStream());
				UserName=in.readUTF();
				if(UserName!=null&&AgreeConnect(UserName))
				{
					out.writeUTF("agree");
					map.put(this.UserName, this.socket);
					UsersList.add(this);
					SendList();
					AppendListItems(UserName);
					AppendMessage(UserName+"已连接");
				}
				else {
					out.writeUTF("Disagree");
					out.writeUTF(UserName+"已登录");
				}
			} catch (IOException e) {
			}
		}
		
		@Override
		public void run() {
			
			while (true) {
				try {
					if(!ButtonMark)
					{
						in.close();
						this.socket.close();
						this.stop();
					}
					String ClientMessage=in.readUTF();
					AppendMessage("已接收"+ClientMessage);
					String[] message=ClientMessage.split(MessageSplit);
					InformationCenter(message);
					
				} catch (IOException e) {
				}
				
			}
			
			
		}
		public void InformationCenter(String[] message)//接受消息的信息处理中心
		{
			
				switch (message[0]) {
				case "Connect":
					//收到请求连接后:
					try {
						out.writeUTF("agree");
						AppendMessage("已同意"+UserName+"连接请求");
					} catch (IOException e) {
					}
					break;
				case "UpdateList":
					//请求更新列表
					SendList();
					AppendMessage("已发送"+UserName+"的List");
					break;
				case "Broad":
					//广播
					BroadSendMessage(message[1]);
					AppendMessage("广播"+message[1]);
					break;
				case "DisConnect":{
					//断开连接
					DisConnect(message[1]);
					AppendMessage(UserName+"已断线");
					//干掉自己这个线程
					this.stop();
					break;
					
				}
				default://私聊
					SendMessage(message[0], message[1]);
					break;
				}
			
		}
	}
}





Client

预览

在这里插入图片描述

详细流程图

在这里插入图片描述

代码

package TCP.Client;

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

import javax.swing.JOptionPane;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.wb.swt.SWTResourceManager;

public class MiniClient {

	protected Shell shell;
	private Text text_Port;
	private Text text_IP;
	private Text text_Input;
	private Text text_Message;
	private List UserList;
	Button btnConnect;
	private boolean ButtonMark = false;
	//true: DisConnect
	//false:Connect
	private String IP = "127.0.0.1";
	private String UserName = null;
	private int Port = 4321;
	private Text text_Name;
	private final String MessageSplit = "###";
	private String PrivateObject = "Broad";
	private Socket socket=null;
	private Read read;
	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			MiniClient window = new MiniClient();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Open the window.
	 */
	public void open() {
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents() {
		shell = new Shell();
		shell.setSize(560, 448);
		shell.setText("MiniChat");
		shell.addShellListener(new ShellAdapter() {
            public void shellClosed(ShellEvent e) {
            	DisConnect(socket, read);
                System.exit(0);
            }});
		
		text_Name = new Text(shell, SWT.BORDER);
		text_Name.setBounds(337, 22, 72, 23);

		 btnConnect = new Button(shell, SWT.NONE);
		 btnConnect.setFont(SWTResourceManager.getFont("微软雅黑", 10, SWT.NORMAL));
		btnConnect.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (text_Name.getText().equals("")) {
					JOptionPane.showMessageDialog(null, "必须设置用户名", "提示", JOptionPane.INFORMATION_MESSAGE);
				} else {
					
					if (!ButtonMark) {
						SwitchButton();
//						 开始连接socket
						Initialization();
					} else {
						SwitchButton();
//						 请求断连
						DisConnect(socket,read);
					}
					
				}

			}
		});
		btnConnect.setBounds(434, 10, 100, 47);
		btnConnect.setText("Connect!!!");

		text_Port = new Text(shell, SWT.BORDER);
		text_Port.setText(this.Port + "");
		text_Port.setBounds(194, 22, 72, 23);

		text_IP = new Text(shell, SWT.BORDER);
		text_IP.setText(this.IP);
		text_IP.setBounds(33, 22, 115, 23);

		Label lblIp = new Label(shell, SWT.NONE);
		lblIp.setFont(SWTResourceManager.getFont("微软雅黑", 10, SWT.NORMAL));
		lblIp.setBounds(10, 25, 17, 17);
		lblIp.setText("IP");

		Label lblPort = new Label(shell, SWT.NONE);
		lblPort.setFont(SWTResourceManager.getFont("微软雅黑", 10, SWT.NORMAL));
		lblPort.setBounds(154, 25, 34, 17);
		lblPort.setText("Port");

		text_Input = new Text(shell, SWT.BORDER);
		text_Input.setFont(SWTResourceManager.getFont("微软雅黑", 13, SWT.NORMAL));
		text_Input.setBounds(10, 332, 399, 61);
		text_Input.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.keyCode == 13) // 当按键为回车键时触发
				{
					SendMessage();
					text_Input.setText("");
				}
			
			}
		});
	
		Button btnSend = new Button(shell, SWT.NONE);
		btnSend.setFont(SWTResourceManager.getFont("微软雅黑", 13, SWT.NORMAL));
		btnSend.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				// 发送!!
				SendMessage();
			}
		});
		btnSend.setBounds(434, 332, 100, 61);
		btnSend.setText("Send");

		text_Message = new Text(shell, SWT.BORDER | SWT.V_SCROLL);
		text_Message.setFont(SWTResourceManager.getFont("微软雅黑", 13, SWT.NORMAL));
		text_Message.setBounds(10, 60, 399, 266);

		UserList = new List(shell, SWT.BORDER);
		UserList.setFont(SWTResourceManager.getFont("微软雅黑", 13, SWT.NORMAL));
		UserList.setBounds(434, 63, 100, 263);
		UserList.addMouseListener(new MouseListener() {
			public void mouseUp(MouseEvent arg0) {
				PrivateObject=UserList.getSelection()[0];
			}
			public void mouseDown(MouseEvent arg0) {}
			public void mouseDoubleClick(MouseEvent arg0) {}
		});

		Label lblName = new Label(shell, SWT.NONE);
		lblName.setFont(SWTResourceManager.getFont("微软雅黑", 10, SWT.NORMAL));
		lblName.setAlignment(SWT.CENTER);
		lblName.setText("Name");
		lblName.setBounds(272, 25, 59, 17);

	}

	// 方法区:-----------------------
	void SwitchButton()
	{
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
					if (!ButtonMark) {
					btnConnect.setText("Disconnect");
					ButtonMark = !ButtonMark;
					// 开始连接socket
	//				Initialization();
				} else {
					btnConnect.setText("Connect!!!");
					ButtonMark = !ButtonMark;
	//				DisConnect(socket);
	//				// 请求断连
				}
				
			}
		});
		
	}
	void Initialization() {// 连接初始化
		this.IP = this.text_IP.getText();
		this.Port = Integer.parseInt(this.text_Port.getText());
		this.UserName = this.text_Name.getText();
		new Connect().start();
		

	}
	void ApplyUpdateList(Socket socket)//向服务器发送更新名单的请求
	{
		try {
			DataInputStream in =new DataInputStream(socket.getInputStream());
			
			UpdateList(in.readUTF());
		} catch (IOException e) {
		}
	}
	void AppendMessage(String Message) {// 往消息框添加消息
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				text_Message.append(Message + '\n');
			}
		});
	}
	void SetListItems(String [] message)//设置UserList 
	{
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				UserList.setItems(message);;
			}
		});
	}
	void UpdateList(String message) {// 更新列表
		PrivateObject = "Broad";
		try {
			String[] Items = message.split(MessageSplit);
			Items[0]="Broad";
			SetListItems(Items);
		} catch (Exception e) {
			AppendMessage("更新列表失败");
		}
	}
	
	void InformationCenter(String message,Socket socket) {//信息分类处理中心
		//广播消息:Server###+message
		//私聊消息:PrivateChat###+message
		//更新List:UpdateList###,后续会发出长度和items
		//离线消息:DisConnect###
		String[] split=message.split(MessageSplit);
		switch (split[0]) {
			case "Server": {
				//更新list
				UpdateList(message);
				break;
			}
			case "PrivateChat":{
				//来自私聊的信息
				AppendMessage("<私聊>\t"+split[1]);
				break;
			}
			case "Broad":{
				//普通信息
				AppendMessage("<广播>\t"+split[1]);
				break;
			}
			case "DisConnect":{
				//服务器要求离线
				DisConnect(read);
				break;
			}
		
		}	
	}

	Boolean ApplyConnect(Socket socket) {// 向服务器请求连接并取得同意
		try {
			DataOutputStream out = new DataOutputStream(socket.getOutputStream());
			DataInputStream in = new DataInputStream(socket.getInputStream());
			out.writeUTF( UserName);
			String ServerMessage = in.readUTF();
			AppendMessage("向服务器发送连接请求");
			if (ServerMessage.equals("agree")) {
				AppendMessage("服务器同意连接请求");// 服务器同意连接了
				return true;
			} else {
//				SwitchButton();
				AppendMessage("服务器不同意连接请求");
				AppendMessage("原因:"+in.readUTF());
				
				return false;
			}

		} catch (IOException e) {
			SwitchButton();
			AppendMessage("向服务器发送连接请求失败");
			return false;
		}

	}
	void DisConnect(Read read) {//服务器要求断开
		AppendMessage("服务器要求断开连接");
		SwitchButton();
		SetListItems(new String[] {""});
		PrivateObject = "Broad";
		try {
			read.socket.close();
		} catch (IOException e) {
		}
		read.stop();
	}
	void DisConnect(Socket socket,Read read)// 向服务器请求断开连接
	{
		
		try {
			SetListItems(new String[] {""});
			DataOutputStream out = new DataOutputStream(socket.getOutputStream());
			// 向服务器发送离线消息
			String message = "DisConnect" + MessageSplit + UserName;
			out.writeUTF(message);
			AppendMessage("向服务器申请断开连接");
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
		}

	}

	void SendMessage() {// 广播消息+私聊
		// Border###username: message
		// 私聊: XXX###+UserName:	message
		
		if(socket!=null)
		{
			if(!text_Input.getText().equals(""))
			{
				if(!PrivateObject.equals("Broad"))
					AppendMessage("<私聊>\t"+UserName + ":\t" + text_Input.getText());
				String message = PrivateObject + MessageSplit + UserName + ":\t" + text_Input.getText();
				DataOutputStream out;
				try {
					out = new DataOutputStream(socket.getOutputStream());
					out.writeUTF(message);
				} catch (IOException e) {
					AppendMessage("发送消息失败");
					
				}
			}
			else {
				JOptionPane.showMessageDialog(null, "输入框为空", "提示", JOptionPane.INFORMATION_MESSAGE);
			}
			
		}
		else {
			JOptionPane.showMessageDialog(null, "未连接到服务器", "提示", JOptionPane.INFORMATION_MESSAGE);
		}
		
	}
	// 接收的线程-----------------------
	class Read extends Thread {
		DataInputStream in;
		Socket socket;

		public Read(Socket socket) {
			AppendMessage("已开启read线程");
			try {
				this.socket = socket;
				this.in = new DataInputStream(socket.getInputStream());
				AppendMessage("已初始化Read\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			} catch (IOException e) {

			}
		}

		@Override
		public void run() {
			while (true) {
				try {
					if(!ButtonMark)
						this.stop();
					String message = in.readUTF();
//					AppendMessage("已收到来自服务器的信息"+message);
					InformationCenter(message, socket);
				} catch (IOException e) {

				}
			}
		}
	}
	//Socket的线程,为了不影响主面板
	class Connect extends Thread{
		@Override
		public void run() {
			try {
				AppendMessage("打开服务??");
				 socket = new Socket(IP, Port);
				// 向服务器申请连接 UserName
				 if(ApplyConnect(socket))//会发送自己用户名给服务器进行验证
				 {
					 ApplyUpdateList(socket);
					 read=new Read(socket);
					 read.start();
				 }
				 else {
					throw new IOException();
				}
				 
			} catch (Exception e) {
				AppendMessage("连接服务器失败");
				SwitchButton();	
				try {
					socket.close();
				} catch (IOException e1) {
				}
				this.stop();
			}
		}
	}
}

后续

后续会写出基于UDP的多人聊天室(入数据库)本项目写得稀烂。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-17 12:17:20  更:2021-07-17 12:18:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:31:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码