java socket传多个文件的疑问?望解答

zg6002 2010-07-02 02:42:34
服务端:ServerSocket server = new ServerSocket(9527);

客房端:Socket socket = new Socket(IP,9527);

问题:传输一个,十个,百个文件都没事,可是现在要传一个文件夹里面有上百万个文件,每次传完一个文件就要重新执行
new Socket(IP,9527);一变,次数多了会有一异常出现,

现在想有没有办法只实现一次new Socket(IP,9527);(把客户端与服务端连接建立后)就利用这一个连接传多个文件?不在for循环里写连接代码?
...全文
737 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
_真真 2012-07-30
  • 打赏
  • 举报
回复
非常感谢16楼的亲情奉献,, 我研究了好久都没弄出来.
阿贝啊啊 2012-04-30
  • 打赏
  • 举报
回复
多谢上面的回答。
tanrenzong1986 2011-07-21
  • 打赏
  • 举报
回复
传输这么多文件,为什么不用FTP啊。
dog12ccc 2011-07-21
  • 打赏
  • 举报
回复
我们的教科书上面也弄错了 客户端是用输出流吧? 服务器是输入流 吧? 是不是哦? 忘记了
guoao_xxL 2011-07-21
  • 打赏
  • 举报
回复
楼上写的 正好需要了!
感谢
kala197 2010-07-05
  • 打赏
  • 举报
回复
嘿嘿ie 顶个 虽然搞Java 但是还没用的过socket
「已注销」 2010-07-05
  • 打赏
  • 举报
回复

发送方
dos.writeInt(files.size());//文件个数
for(File f:files){
dos.writeUTF(f.getName());//文件名
dos.writeLong(f.length());//文件长度
dos.write(...);//文件内容
}

接收方
int n = dis.readInt();//文件个数
for(int i=0;i<n;i++){
String filename = dis.readUTF();//文件名
long length = dis.readLong();//文件长度
dis.read(...);//读取length长度的字节
...//保存文件
}
zg6002 2010-07-05
  • 打赏
  • 举报
回复
发送端用 out.writeUTF("end");表示一份文件完成,开发送下一份文件
接收端:
while((ch = in.read(buf))!=-1) {
len += ch;
DiaInfo.pb1.setValue(len);
DiaInfo.pb1.setString(len+"/"+size);
fos.write(buf,0,ch);
fos.flush();
if("end".equals( in.readUTF())) {
System.out.println("关闭");
break;
}
}
这样写不行了,只要运行到"end".equals( in.readUTF())这句就报错malformed input around byte
应该怎么做标记?
xtuxucj 2010-07-05
  • 打赏
  • 举报
回复
在来一个BteBuffer的,但没实现NIO这个功能。NIO不太会,呵呵。代码没注释,写的很粗糙。就写了基本功能。
客户端

package nioserver;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class ChannelClient {
private SocketChannel sc;
private String hostIp;
private int hostPort;

public ChannelClient(String hostIp, int port) {
this.hostIp = hostIp;
this.hostPort = port;
}

private void setUpConnection() {
try {
SocketAddress remote = new InetSocketAddress(hostIp, hostPort);
sc = SocketChannel.open();
sc.connect(remote);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private void sendFiles(File[] files) {
FileChannel fileChannel = null;
try {
for (int i = 0;i < files.length; i++) {
byte[] namebyte = files[i].getName().getBytes("UTF-8");
long size = files[i].length();
int nameLength = namebyte.length;
fileChannel = new FileInputStream(files[i]).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
buffer.putInt(4+8+nameLength);
buffer.putInt(nameLength);
buffer.put(namebyte);
buffer.putLong(size);
buffer.flip();
while(buffer.hasRemaining()){
sc.write(buffer);
}
long count = 1024*1024;
long read = 0L;
while(read < size){
if(size - read < count)
count = size - read;
read += fileChannel.transferTo(0+read, count, sc);
System.out.println("read:"+read);
}
fileChannel.close();
if(i < files.length -1){
sc.write(ByteBuffer.wrap(new byte[]{1}));
System.out.println(1);
}
else
sc.write(ByteBuffer.wrap(new byte[]{0}));
}

sc.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
ChannelClient client = new ChannelClient("127.0.0.1", 3000);
client.setUpConnection();
File[] files = new File("D:\\send").listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isFile();
}
});
client.sendFiles(files);
}

}


服务器端

package nioserver;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class FilesChannelServer {
private String fileName = null;
private long fileSize = 0L;
private int hostPort;
private ServerSocketChannel ssl = null;
private SocketChannel clientChannel = null;
private ByteBuffer buffer = null;

public FilesChannelServer(int port) {
this.hostPort = port;
}

private void setUpConnection() {
try {
ssl = ServerSocketChannel.open();
SocketAddress address = new InetSocketAddress("127.0.0.1", hostPort);
ssl.socket().bind(address);
System.out.println("bind.....");
} catch (IOException e) {
e.printStackTrace();
}
}

private void parseHead(int headlength) {
buffer = ByteBuffer.allocate(headlength);
try {
while(buffer.position() < buffer.capacity())
clientChannel.read(buffer);
buffer.flip();
byte[] filenamebyte = new byte[buffer.getInt()];
buffer.get(filenamebyte);
fileName = new String(filenamebyte,"UTF-8");
fileSize = buffer.getLong();
} catch (IOException e) {
e.printStackTrace();
}
}

private void parseBody(long size) {
long read = 0L;
long count = 8192;
FileChannel fileChannel = null;
try {
fileChannel = new FileOutputStream("D:\\receive\\" + fileName)
.getChannel();
System.out.println(fileName);
while (read < size) {
if (size - read < count)
count = size - read;
read += fileChannel
.transferFrom(clientChannel, 0 + read, count);
}

} catch (IOException e) {
e.printStackTrace();
}finally{
try {
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

private int parse() {
int i = 0;
try {
buffer = ByteBuffer.allocate(4);
while(buffer.position() < buffer.capacity())
clientChannel.read(buffer);
buffer.flip();
parseHead(buffer.getInt());
parseBody(fileSize);
buffer = ByteBuffer.allocate(1);
while(buffer.position() < buffer.capacity())
clientChannel.read(buffer);
buffer.flip();
i = buffer.get();
} catch (IOException e) {
e.printStackTrace();
}

return i;
}

private void acceptConnection() {
while (true) {
try {
clientChannel = ssl.accept();
int j = 1;
while(j != 0){
j = parse();
System.out.println(j);
}
} catch (IOException e) {
e.printStackTrace();
} finally {

try {
if (clientChannel != null)
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}

}

}
}

public static void main(String[] args) {
FilesChannelServer cs = new FilesChannelServer(3000);
cs.setUpConnection();
cs.acceptConnection();
}
}

这里说明下。可以说是我自定义的协议吧。
一共分成6段
在bytebuffer中第一个4个byte,存放第二段到地四段的byte长度
第二段,四个byte长度是文件名转换成byte数组的长度(file.getName().getBytes("UTF-8").length)
第三段,存放文件名转换的byte(file.getName().getBytes("UTF-8"))
第四段,8个bte存放文件的大小(file.getSize())
第五段,存放文件的byte形式,
第六段,1个byte长度,如果还有后续的文件存放1,如果文件传送完了就存放0
ChDw 2010-07-05
  • 打赏
  • 举报
回复
只要双方都关闭了Socket,那么不应该出现问题的
xtuxucj 2010-07-05
  • 打赏
  • 举报
回复
实在不行就搞个ZipInoutStream。按顺序把文件写到ZipInoutStream中,在另一端解压缩就行。或者自己定义一个解析规定。
贴上我的代码。这个还没整理过。作用是从客服端往服务器端发送文件。
客服端代码

package net;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
* @author Life
*发送文件的客户端
*/
public class FileSendClient {
protected String hostIP;
protected int hostPort;
protected OutputStream socketInS;
private Socket client;

public FileSendClient(String ip, int portNumber) {
this.hostIP = ip;
this.hostPort = portNumber;
}

/**
* 建立连接
*/
public void setUpConnection() {
try {
client = new Socket(hostIP, hostPort);
socketInS = client.getOutputStream();
} catch (UnknownHostException e) {
throw new RuntimeException(e.toString());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}

}

/**
* @param files
* 用zip流发送多个文件
*
*/
public void sendFile(File[] files) {
BufferedInputStream fileReader = null;
ZipOutputStream zos = new ZipOutputStream(socketInS);
BufferedOutputStream socketWriter = new BufferedOutputStream(zos);
byte[] buff = new byte[8192];
int c = 0;
try {
for (File file : files) {
fileReader = new BufferedInputStream(new FileInputStream(file));
zos.putNextEntry(new ZipEntry(file.getName()));
while ((c = fileReader.read(buff)) != -1) {
socketWriter.write(buff, 0, c);
}
socketWriter.flush();
try {
if (fileReader != null)
fileReader.close();
} catch (IOException e) {
System.out.println("Error closing fileReader" + e);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socketWriter != null )
socketWriter.close();
} catch (IOException e) {
System.out.println("Error closing socketWriter" + e);
}finally{
if(client != null && client.isConnected()){
try {
client.close();
} catch (IOException e) {
System.out.println("Error closing socket" + e);
}
}
}

}

}

public static void main(String[] args) {
FileSendClient fileSendClient = new FileSendClient("127.0.0.1", 3000);
fileSendClient.setUpConnection();
File[] files = new File("D:\\send").listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isFile();
}
});
fileSendClient.sendFile(files);

}
}

服务器端代码:

package net;

import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;


/**
* @author Life
*文件接收服务器
*/
public class FileReceiveServer {
protected int listenPort;
private int maxConnection;

public FileReceiveServer(int listenPort,int maxConnection) {
this.listenPort = listenPort;
this.maxConnection = maxConnection;
}

/**
* 建立监听端口
*/
public void setUpConnection(){
for(int i = 0; i < maxConnection; i++){
new Thread(new FileReceiverHandle(),"handle"+i).start();
}


}

/**
* 接收文件客户端的连接
*/
public void acceptConnection() {
try {
ServerSocket server = new ServerSocket(listenPort);
Socket incomingSocket = null;
while (true) {
incomingSocket = server.accept();
handlerConnection(incomingSocket);
}
} catch (BindException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

/**
* @param incomingSocket
* 处理接收到的连接(socket)
*/
private void handlerConnection(Socket incomingSocket) {
FileReceiverHandle.processConnection(incomingSocket);

}

public static void main(String[] args) {
FileReceiveServer fileReceiveServer = new FileReceiveServer(3000,5);
fileReceiveServer.setUpConnection();
fileReceiveServer.acceptConnection();
}

}



package net;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
* @author Life
*文件接收处理程序
*/
public class FileReceiverHandle implements Runnable {
private static List<Socket> socketsPool = new LinkedList<Socket>();
private Socket connection = null;

/**
* @param incomingSocket
* 降接收到的连接放到一个池中
*
*/
public static void processConnection(Socket incomingSocket) {
synchronized (socketsPool) {
socketsPool.add(socketsPool.size(), incomingSocket);
socketsPool.notifyAll();
}
}

/**
* 按zip流解码获取接收到的流
*/
public void handlerConnection() {
InputStream inputFromSocket = null;
BufferedOutputStream fileWriter = null;
BufferedInputStream socketReader = null;
byte[] buff = new byte[8192];
int c = 0;
try {
inputFromSocket = connection.getInputStream();
ZipInputStream zis = new ZipInputStream(inputFromSocket);
socketReader = new BufferedInputStream(zis);
ZipEntry e = null;
while ((e = zis.getNextEntry()) != null) {
System.out.println(e.getName());
fileWriter = new BufferedOutputStream(new FileOutputStream(
new File("D:\\receive\\" + e.getName())));
while ((c = socketReader.read(buff)) != -1) {
fileWriter.write(buff, 0, c);
}
try {
fileWriter.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socketReader != null) {
socketReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try{
if(connection != null){
connection.close();
}
}catch(IOException e){
e.printStackTrace();
}
}

}
}

/*
* 处理池中的连接
*/
public void run() {
while (true) {
synchronized (socketsPool) {
while (socketsPool.isEmpty()) {
try {
socketsPool.wait();
} catch (InterruptedException e) {
return;
}
}
connection = socketsPool.remove(socketsPool.size() - 1);
}
handlerConnection();
}
}

}
guobaoaskformore 2010-07-02
  • 打赏
  • 举报
回复
自己设置一个文件分隔标识吧,搞复杂一些,当发送一个文件结束后,将该分隔标识发出去,客户端读到这一行就表示一个文件结束,则开始读取下个文件
如果觉得土,可以看看,一些FTP的源码如何处理的。
zg6002 2010-07-02
  • 打赏
  • 举报
回复
现在的问题就是怎么判断什么时候发送完一个文件,什么时候开始接收下一个文件
zuoguodang 2010-07-02
  • 打赏
  • 举报
回复
连一次啊,发文件就是遍历文件一个一个的发
俊哥123 2010-07-02
  • 打赏
  • 举报
回复
无论是发送还是接受方都不要重复的new socket,
第一次使用时判断是否为空,为空才new,所有的任务做完了,才能关闭。
你把握这一点,程序就OK了

用while(true){
Thread.sleep(1000l);
if(inputStream.available()>0){
//判断有数据过来,接收

}
}
zg6002 2010-07-02
  • 打赏
  • 举报
回复
发送方:int t = 0;
while((ch = fis.read(buf))!=-1) {
t += ch;
out.write(buf,0,ch);
if(t == (int)f.length()) {
break;
}
}


接收方:while((ch = in.read(buf))!=-1) {
len += ch;
DiaInfo.pb1.setValue(len);
DiaInfo.pb1.setString(len+"/"+size);
fos.write(buf, 0, ch);
if(len == size) {
break;
}
}
zg6002 2010-07-02
  • 打赏
  • 举报
回复
发送端和接收端都有一个while循环 通过in.read(buf))!=-1退出
如果不重新连一下服务,用一个连接的话,发送端和接收端怎么判断什么时候接收新的文件?
prince_java 2010-07-02
  • 打赏
  • 举报
回复
你不会把Socket写在for循环里了吧- -!
客户端就写一个Socket连接,用BufferedReader从键盘接收你要传的文件或文件夹,然后用if进行判断,如果你要传的类型是文件,那好,直接用输入输出流进行复制,如果是文件夹,那就用for循环对文件夹进行遍历,是文件就复制文件,是文件夹的就按照文件夹的名字新建文件夹
俊哥123 2010-07-02
  • 打赏
  • 举报
回复
你这样老new socket,再牛逼的电脑,连接缓存都要搞没了
你在外面定义好一个socket,和输入输出流
new 一次,以后重用
所有文件发完了再关闭。
magicbu 2010-07-02
  • 打赏
  • 举报
回复
为什么要重新执行new Socket(IP,9527)
好像没必要吧~
加载更多回复(1)
【完整课程列表】 https://download.csdn.net/download/qq_27595745/55555830 完整版精品java课件 Java基础入门教程 Java程序设计 第1章 Java语言概述(共38页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第2章 java语言基础(共31页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第3章 控制结构(共23页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第4章 类和对象(共57页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第5章 继承和接口(共47页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第6章 数组和集合(共44页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第7章 字符串处理(共38页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第8章 异常处理(共27页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第9章 输入输出流(共49页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第10章 JDBC数据库编程(共21页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第11章 图形用户界面1(共27页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第11章 图形用户界面2(共31页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第12章 applet(共16页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第13章 多线程(共24页).ppt 完整版精品java课件 Java基础入门教程 Java程序设计 第14章 socket网络编程(共24页).ppt
Java版精华区
java jsp及js等精华帖子合集
适合收藏 查询

序号 精华区主题
--------------------------------
1. [目录]Java教程
1. [目录]Java语言教程
2. [目录]来自java
3. [目录]咖啡备忘录
4. [目录]Java介绍
5. [目录]Java学习笔记(推荐)
6. [目录]JDBC文档
7. [目录]RMI 文档
2. [目录]Java资源(文档-书籍-下载-注册码)
1. [目录]License 和注册码
2. [目录]好书推荐
3. [目录]关于Java的一些Mail List
4. [目录]CORBA资源
5. [目录]Linux下的Java
24. [目录]以前介绍的资源
25. [目录]Java编程工具
30. [目录]更多下载相关
3. [目录]JavaScript
1. [目录]javascript 书籍
2. [目录]javascript 书籍(2)
4. [目录]术语字典
5. [目录]Java编程
1. [目录]Java简单问题
2. [目录]为什么Applet修改后在浏览器中不发生变化
3. [目录]Java中的类型转换
4. [目录]怎样找到编译时缺少的类
2. [目录]Java疑难解答
1. [目录]Java原理问题
1. [目录]Java中是指针还是引用?
2. [目录]关于getMethod方法
3. [目录]怎样建立Package
4. [目录]关于classloader
2. [目录]Java汉字问题
3. [目录]Java中的界面处理
1. [目录]Java中的鼠标操作
2. [目录]如何使画面不闪烁?
4. [目录]Java与Internet
1. [目录]Socket方面的疑问
2. [目录]用plag-in运行Applet with JDK 1.2
5. [目录]Java访问数据库
1. [目录]一个JDBC问题
6. [目录]Java安全性问题
1. [目录]数字化签名
1. [目录]JAVAKEY问题
7. [目录]Java与Linux
8. [目录]Java其他问题
1. [目录]播放.au文件的问题
2. [目录]RMI
11. [目录]线程问题
12. [目录]Java打印
13. [目录]本地相关问题(JNI,串口等)
3. [目录]Java编程实例
1. [目录]Java 实 例
2. [目录]Java applet中的动画
17. [目录]Java串口实例
4. [目录]Java3D专题介绍
5. [目录]Java与开发工具
1. [目录]关于JBuilder的问题
2. [目录]Jbuilder安装与使用中的常见问题
7. [目录]Java编程工具
8. [目录]Visual Age for Java
9. [目录]Websphere
7. [目录]Applet专题(安全,通信)
1. [目录]Applet的安全限制及措施
2. [目录]与Servlet通信
3. [目录]与其他Cgi程序通信
4. [目录]与JavaScript通信
5. [目录]与其他Applet通信
6. [目录]找不到类的问题
6. [目录]Java动态与讨论
1. [目录]Java最新动态
9. [目录]面向对象的骡子
13. [目录]JSP+Bean?
14. [目录]牢骚
15. [目录]Java之争 - 谁最吃亏
17. [目录]真成JAVA大牛还是很有前途的
18. [目录]Jbuilder及Swing,多线程问题讨论
7. [目录]快乐Java大家谈
1. [目录]活动简介
2. [目录]庆祝专区
3. [目录]dW站点简介
4. [目录]待审稿件
5. [目录]已审稿件
6. [目录]整理精华区
7. [目录]建议和投票
8. [目录]临时目录
9. [目录]活动征文
10. [目录]人物趣事
8. [目录]java server技术
1. [目录]中文问题
2. [目录]JSP 和Servlet
1. [目录]JSP语法
2. [目录]JSP的安全问题
3. [目录]查询结果的分页显示
4. [目录]CGI,mod_perl,PHP,JSP比较
3. [目录]线程池的讨论
4. [目录]JDBC
5. [目录]EJB技术
1. [目录]EJB 的设计模式
6. [目录]Servlet Container 和 应用服务器
3. [目录]TOMCAT
9. [目录]纪念光盘精华区精选目录
1. [目录]Java编程
1. [目录]Java简单问题
2. [目录]为什么Applet修改后在浏览器中不发生变化
3. [目录]Java中的类型转换
4. [目录]怎样找到编译时缺少的类
2. [目录]Java疑难解答
1. [目录]Java原理问题
1. [目录]Java中是指针还是引用?
2. [目录]关于getMethod方法
3. [目录]怎样建立Package
4. [目录]关于classloader
2. [目录]Java汉字问题
3. [目录]Java中的界面处理
1. [目录]Java中的鼠标操作
2. [目录]如何使画面不闪烁?
4. [目录]Java与Internet
1. [目录]Socket方面的疑问
2. [目录]用plag-in运行Applet with JDK 1.2
5. [目录]Java访问数据库
1. [目录]一个JDBC问题
6. [目录]Java安全性问题
1. [目录]数字化签名
1. [目录]JAVAKEY问题
7. [目录]Java与Linux
8. [目录]Java其他问题
1. [目录]播放.au文件的问题
2. [目录]RMI
11. [目录]线程问题
12. [目录]Java打印
3. [目录]Java编程实例
1. [目录]Java 实 例
2. [目录]Java applet中的动画
4. [目录]Java3D专题介绍
5. [目录]Java与开发工具
1. [目录]关于JBuilder的问题
6. [目录]Java编程工具
7. [目录]Visual Age for Java
8. [目录]Websphere
7. [目录]Applet
2. [目录]Java动态与讨论
1. [目录]Java最新动态
9. [目录]面向对象的骡子
13. [目录]JSP+Bean?
3. [目录]java server技术
1. [目录]中文问题
29. [目录]线程池的讨论
30. [目录]JDBC
31. [目录]JSP语法
32. [目录]EJB
33. [目录]TOMCAT
34. [目录]JSP的安全问题
35. [目录]查询结果的分页显示
10. [目录]老精华区文章
1. [目录]Active X编程
2. [目录]Active X介绍

62,636

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧