标签:
本来是一个问题贴,最终因为太长了,只能搞成博客了。算是做个记录吧
</pre><pre name="code" class="java">我的这个Socket是想保持一个长连接,文件可以循环传输 但是我的问题在于,不能抓住文件传输完成的这个时机,导致异常 我的文件发送代码 [code=java] public void sendFile(String filePath) { try { File file = new File(filePath); //发送文件 this.fileInputStream = new FileInputStream(file); byte[] buffer = new byte[this.bufferSize]; int total = 0; _Log.e(_Log.msg() + "我们正在发送文件先生,"); while (true) { int length = this.fileInputStream.read(buffer); this.outputStream.write(buffer); this.outputStream.flush(); if (length == -1) { _Log.e(_Log.msg() + "这就是当-1的时候我们读文件结束了"); break; } total += length; } _Log.e(_Log.msg() + "我们按传输所计算的文件大小为:" + total + "bit"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } _Log.e(_Log.msg() + "文件发送结束我们应该等待客户端的确认,然后关闭等待对话框"); } [/code] 我的文件接收代码 [code=java] //region 正在接受文件 this.fileOutputStream = new FileOutputStream(this.receiveFilePath); boolean first = true; _Log.e(_Log.msg() + "文件正在被接受"); while (true) { if (!first) { _Log.e(_Log.msg() + "正在等待接收吓一跳"); total = this.inputStream.read(buffer); _Log.e(_Log.msg() + "接收到的数组长度为+" + total); if (new File(this.receiveFilePath).length() >= this.receiveFileSize) { _Log.e(_Log.msg() + "读取文件时因为到末尾时返回-1而结束的 = " + this .receiveFileSize); break; } } else { first = false; } //我们暂且认为这里FileOutputStream(path)会自动创建这个不存在的路径,如果 this.fileOutputStream.write(buffer, 0, total); } this.fileOutputStream.close(); _Log.e(_Log.msg() + "传输完成了"); //endregion传输文件完成 this.receiveFileState = false; _Log.e(_Log.msg() + "我们在1秒钟之后告知客户我们已经接受文件完成的这件事"); Thread.sleep(1000); this.sendString(this.transferFileOver);//发送文件接收完成标志 _Log.e(_Log.msg() + "我们已经说完了,就是不知道其受到没有"); [/code] 关键点:(我想在文件传输完成后从这个点跳出但是总不能把握文件什么时候传输完成) [code=java] //这个没有成功 if (new File(this.receiveFilePath).length() >= this.receiveFileSize) { _Log.e(_Log.msg() + "读取文件时因为到末尾时返回-1而结束的 = " + this .receiveFileSize); break; } //这个也没有成功 this.receiveFileSize -= total; if (this.receiveFileSize <= 0) { _Log.e(_Log.msg() + "读取文件时因为到末尾时返回-1而结束的 = " + this .receiveFileSize); break; } //还试过让发送方Socket发送完成文件后发送一个字符串命令, //我让发送文件后,使其sleep(1000),在发送命令,依然会和文件数据 混在一起 [/code] 分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割分割 没想到的是问题还没有问,不过是写了些思路问题就有了解,真棒 代码如下: SocketTool.java [code=java] package a114.yangming.Work.M_Socket; import android.os.Message; import android.preference.PreferenceManager; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.List; import a114.yangming.Util.Global; import a114.yangming.Util.Tool_GetStorage; import a114.yangming.Util._Log; import a114.yangming.myapplication.R; /** 杨铭 Created by kys_8 on 16/6/11,0011. <p>Email:771365380@qq.com</p> <p>Mobile phone:15133350726</p> <p/> Client 和 Server同时需呀的是 <p/> 收发文字——包括命令,和通信语句 <p/> 收发文件 */ public class SocketTool { //region 流 包括 简单流 和 文件流 private OutputStream outputStream; private InputStream inputStream; /** 设置简单流 @param outputStream @param inputStream */ public void setSimpleStream(OutputStream outputStream, InputStream inputStream) { this.inputStream = inputStream; this.outputStream = outputStream; } private FileOutputStream fileOutputStream; private FileInputStream fileInputStream; //endregion //region 通信语句 /** 客户端向服务器打招呼的短语 */ public final String hello = "信工学院"; /** 服务器的标准回复 */ public final String hi = "北方学院"; /** 成功得到了文件描述 */ public final String getFileIntroduceOK = "getIntroduceOk"; /** 传输文件完成 */ public final String transferFileOver = "transferFileOver"; /** 通过Json传来的文件名字——Key */ public final String jsonFileName = "fileName"; /** 通过Json出来的文件大小——Key */ public final String jsonFileSize = "fileSize"; /** 默认缓冲区大小 */ private final int bufferSize = 10240; //endregion //region 标识 /** 用来判断当前是否正处于发送文件阶段,true,正在发送文件,false,当前没有发送文件 */ private boolean sendFileState = false; /** 用来标记是否应该接受文件流了,如果是应该接受文件流。这里置为true ——如果为False说明应该传输文件名字 */ private boolean receiveFileState = false; //endregion //region 传输过程中需要的一些文件介绍信息——我们的程序,收发不能同时进行,所以这里公用相同的变量是没有问题的 /** 正在传输的文件大小——接收过程 */ private long receiveFileSize = -1; /** 正在传输的文件路径——接收过程 */ private String receiveFilePath = null; public List<String> transFilePaths = null; //endregion /** 仅用于处理客户发来的打招呼消息——这是服务端需要用的 <p/> 如果给的暗号是正确的,我们就回复他对应的暗号,如果是错误的就和他断开连接等待下一个用户 @return true代表,可以建立通信,false,代表应该断开这个Socket等待下一个连接的到来 */ public boolean receiveHello() { try { byte[] buffer = new byte[20]; _Log.e(_Log.msg() + "我是服务端我正在等待客户的打招呼信息"); int total = this.inputStream.read(buffer); if (total == 12) { String string = this.getStrFormBytes(buffer, total); if (string.equals(this.hello)) { _Log.e(_Log.msg() + "客户端说:" + string); Thread.sleep(1000);//睡一小会再发就能发了 _Log.e(_Log.msg() + "看看这句话和上一句是否相聚1000mm"); this.sendString(this.hi); return true; } else { return false; } } else { return false; } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } _Log.e(_Log.msg() + "能运行到这一步本身就是异常"); return false; } public boolean receiveHi() { try { byte[] buffer = new byte[20]; _Log.e(_Log.msg() + "我是客户端,我已经和服务端打过招呼了,等待他的回应"); int total = this.inputStream.read(buffer); if (total == 12) { String string = this.getStrFormBytes(buffer, total); if (string.equals(this.hi)) { _Log.e(_Log.msg() + "服务端回复正常——应该关闭界面进行文件传输了"); this.initSendUI(); return true; } else { return false; } } else { return false; } } catch (IOException e) { e.printStackTrace(); } _Log.e(_Log.msg() + "能运行到这一步本身就是异常"); return false; } /** 说白了就是关闭当前页面启动初始页面准备进行文件传输 */ public void initSendUI() { if (Global.handlerLoading != null) { Message message = Message.obtain(); message.what = Global.CloseActivity; Global.handlerLoading.sendMessage(message); } else { _Log.e(_Log.msg() + Global.mainActivity.getResources().getString(R.string.myExecption)); } } /** 接受信息的逻辑 ——代码运行到这里说明已经打过招呼了,我们可以进行后续判断了 */ public void receive() { //我们在这里等待接收,我们会等到什么呢?有可能是文件描述 //如果我们已经发送了一个文件描述,那么我们回得到一个文件描述已经处理完成的确认信息 //有可能是打招呼信息 //有可能是文件 try { byte[] buffer = new byte[1024]; while (true) { _Log.e(_Log.msg() + "这里是正在等待一些什么"); int total = this.inputStream.read(buffer); if (total == -1)//说明客户端主动断开连接了 { _Log.e(_Log.msg() + "正在等待处理的异常"); } if (this.receiveFileState)//正在处于文件接收状态——就是说已经接受过文件介绍会运行这里的 { _Log.e(_Log.msg() + "进入到接受文件的地方了"); //region 正在接受文件 this.fileOutputStream = new FileOutputStream(this.receiveFilePath); boolean first = true; _Log.e(_Log.msg() + "文件正在被接受"); while (true) { if (!first) { _Log.e(_Log.msg() + "正在等待接收吓一跳"); total = this.inputStream.read(buffer); _Log.e(_Log.msg() + "接收到的数组长度为+" + total); if (this.receiveFileSize <= 0) { String string = this.getStrFormBytes(buffer, total).trim(); if (string.equals(this.transferFileOver)) { break; } } else { this.receiveFileSize -= total; } } else { first = false; } //我们暂且认为这里FileOutputStream(path)会自动创建这个不存在的路径,如果 this.fileOutputStream.write(buffer, 0, total); } this.fileOutputStream.close(); _Log.e(_Log.msg() + "传输完成了"); //endregion传输文件完成 this.receiveFileState = false; _Log.e(_Log.msg() + "我们在1秒钟之后告知客户我们已经接受文件完成的这件事"); Thread.sleep(1000); this.sendString(this.transferFileOver);//发送文件接收完成标志 _Log.e(_Log.msg() + "我们已经说完了,就是不知道其受到没有"); } else//没有接受过文件介绍的会运行这里 { if (this.sendFileState)//这是正处于文件发送状态的 { _Log.e(_Log.msg() + "当前本机是发送状态,到达接受用户机器,对接受本机文件后的反馈 的地方了"); //region 发送文件阶段客户接受文件介绍成功 String string = this.getStrFormBytes(buffer, total); if (string.equals(this.getFileIntroduceOK))//客户端说接受文件介绍成功了 { _Log.e(_Log.msg() + "对方接受文件信息成功了,可以发送文件了——已经写好了看看结果怎么样吧"); this.sendFile(this.receiveFilePath); } else if (string.equals(this.transferFileOver)) { this.sendFileState = false; _Log.e(_Log.msg() + "我们已经得到了transferFileOver的反馈,从而设置了this" + ".sendFileState = false;"); } else { _Log.e(_Log.msg() + "对方发送的暗号不太对,重新等待暗号"); } continue; //endregion } else//这种情况下接受文件介绍信息——这个阶段没有检错,不能出错 { //region 正在接受文件信息 _Log.e(_Log.msg() + "这是正在等待文件介绍的位置呢"); //region 得到文件路径和文件大小 String jsonStr = this.getStrFormBytes(buffer, total); _Log.e(_Log.msg() + "运行到这里了吗?111111111111111莫名奇妙这里怎么会得到内容呢?+" + jsonStr); //得到文件名和文件大小. this.getFileIntroduceFormJson(jsonStr); _Log.e(_Log.msg() + "运行到这里了吗?22222222222222222222222"); //endregion String testFilePath = this.receiveFilePath; //region 为了防止文件重名,造成对文件的覆盖,我们要确保这个文件名在这个目录下独一无二 ——完成了这一步才算真正得到了文件路径 int i = 1; while (true) { if (new File(testFilePath).exists()) { String suffix = this.getFileSuffix(receiveFilePath);//得到后缀 if (suffix == null) { testFilePath = receiveFilePath + "(" + i + ")"; } else { testFilePath = receiveFilePath.substring(0, receiveFilePath .length() - suffix.length() - 1); testFilePath = testFilePath + "(" + i + ")." + suffix; } i++; } else { this.receiveFilePath = testFilePath; break; } } //endregion //文件名字处理完成——搞好标致位 this.receiveFileState = true; //告知客户我们得到了文件介绍——也就是说可以发送文件了 this.sendString(this.getFileIntroduceOK); //endregion } } } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** 从JSONStr中得到对文件的描述,包括文件名和文件大小 <p/> 这个方法在适当的时候执行完成后,成员变量 fileSize 和 filePath 将得到各自的值 <p/> 但是每次用完后最好是能释放掉 @param jsonStr 得到的Json */ private void getFileIntroduceFormJson(String jsonStr) { try { JSONTokener jsonParser = new JSONTokener(jsonStr); JSONObject connect = (JSONObject) jsonParser.nextValue(); this.receiveFileSize = connect.getLong(this.jsonFileSize); String fileName = connect.getString(this.jsonFileName); fileName = fileName == null ? "未命名" : fileName;//防止传过来的文件名为空 //从XML文件中得到文件默认存储路径。如果用户还没有设置,就用它手机中首个存储卡的根目录——注意是在根目录创建“速传文件夹” String fileDic = PreferenceManager.getDefaultSharedPreferences(Global.mainActivity) .getString(Global.keySaveStorage, new Tool_GetStorage(Global.mainActivity) .getStorageLocationStr()[0]); this.receiveFilePath = fileDic + "/" + Global.folderName; //我们会首先创建这个文件夹 File file = new File(this.receiveFilePath); if (!file.exists() && !new File(receiveFilePath).mkdirs()) {//先判断文件夹是否存在,如果存在就没事 //如果不存在就会创建,如果创建成功那么也没事, //如果创建失败了就会报错的 _Log.e(_Log.msg() + Global.mainActivity.getResources() .getString(R.string.myExecption)); } this.receiveFilePath += "/" + fileName;//最终得到文件路径 } catch (JSONException e) { e.printStackTrace(); } } //region 发送文件的相关工作 /** 发送文件介绍 */ public void sendFileIntraduce(String filePath) { try { this.receiveFilePath = filePath; File file = new File(filePath); JSONObject jsonObject = new JSONObject(); jsonObject.put(this.jsonFileName, file.getName()); jsonObject.put(this.jsonFileSize, this.receiveFileSize = file.length()); this.sendString(jsonObject.toString()); _Log.e(_Log.msg() + "我们发送出去的JSON为:" + jsonObject.toString()); this.sendFileState = true; } catch (JSONException e) { e.printStackTrace(); } } /** 真正的发送文件 */ public void sendFile(String filePath) { try { File file = new File(filePath); //发送文件 this.fileInputStream = new FileInputStream(file); byte[] buffer = new byte[this.bufferSize]; int total = 0; _Log.e(_Log.msg() + "我们正在发送文件先生,"); while (true) { int length = this.fileInputStream.read(buffer); this.outputStream.write(buffer); this.outputStream.flush(); if (length == -1) { _Log.e(_Log.msg() + "这就是当-1的时候我们读文件结束了"); break; } total += length; } _Log.e(_Log.msg() + "我们按传输所计算的文件大小为:" + total + "bit"); Thread.sleep(2000); this.sendString(this.transferFileOver); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } _Log.e(_Log.msg() + "文件发送结束我们应该等待客户端的确认,然后关闭等待对话框"); } //endregion //===========================来自publicMethod /** 把 byte 数组中的有用的部分转换为String也就是说如果 bytes 肯定是不满的,而 total < bytes 长度 @param bytes 包含数据的byte数组 @param total 所包含的数据占的数组的长度 @return 返回所转换的字符串 */ public String getStrFormBytes(byte[] bytes, int total) { byte[] helloFromClient = new byte[total]; System.arraycopy(bytes, 0, helloFromClient, 0, total); String string = new String(helloFromClient);//把这12位byte转化为字符串 return string; } //region 发送字符串 /** 这里把一个字符串转换为byte数组 @param msg 这里要注意msg的默认编码也就是我们的 .java 文件的默认编码格式。getByte() 如果我们不指定编码方式,就会按默认编码来得到数组。 我们这里指定用`UTF-8`来搞。 <p/> String长度为4 UTF_8 得到的数组长度为 12——GBK 得到的数组长度为 8 */ public void sendString(String msg) { try { byte[] bytes = msg.getBytes("UTF-8"); this.sendBytes(bytes); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } /** 专一发送Bytes数组的 ,处理好的Byte */ private void sendBytes(byte[] bytes) { try { outputStream.write(bytes); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } //endregion /** 得到文件后缀名 @param fileName 参数为String—是FileName @return 返回后缀名 */ public final String getFileSuffix(String fileName) { if (fileName == null) { Log.e(_Log.TAG, _Log.msg() + "当前功能不接受null"); return null; } //要分个那就要考虑情况 //点在开始 得到 “”+点后的内容 //点在中间 得到 以点为分割点的数组 //======================== //点在末尾 得到 得到除分隔符外的串,类似于没有点,但又不太相同 //没有点 得到 原来串 if (!fileName.contains(".")) {//如果不包含"."或者最后一个是"."——那么肯定是未知类型 return null; } if ((fileName.lastIndexOf(".") == fileName.length() - 1)) { return ""; } else { String[] splitDot = fileName.split("\\.");//被“.”分割 String strSuffix = splitDot[splitDot.length - 1];//得到后缀 return strSuffix; } } } [/code] SocketServer.java [code=java] package a114.yangming.Work.M_Socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import a114.yangming.Util.Global; import a114.yangming.Util.OurIP; import a114.yangming.Util._Log; /** 杨铭 Created by kys_8 on 16/6/3,0003. <p>Email:771365380@qq.com</p> <p>Mobile phone:15133350726</p> <p/> 服务端判断客户端时候断线的标志就是接收到的信息是否为空,如果不为空,说明没有断开啊,如果为空说明断线了,那就要做一些操作了 — Client也一样的断开就会接收到null <p/> 在这里我们必须解决端口占用问题“java.net.BindException: bind failed: EADDRINUSE (Address already in use)”如果用户短时间内用软件重复开启热点就会出现端口占用问题。 */ public class SocketServer extends Thread { //region 自己的中断标志设置 /** 直接用官方提供的方法,没能解决在线程睡眠时候进行中断操作,所以自己定义一个中断标志进行尝试——没想到成功了,虽然好了,但还是要因对中断的不了解做出检讨 */ private boolean myInterrupt = false; public boolean isMyInterrupt() { return myInterrupt; } public void setMyInterrupt(boolean myInterrupt) { this.myInterrupt = myInterrupt; }//endregion //region 其余标志们 /** 这个表示是用来标记当前数据信道是否处于连接状态 */ public boolean connect = false; //endregion //region 基础成员 /** 默认端口号 */ private final int port = 9999; /** 默认客户端连接数 */ private final int backlog = 1; /** 通信socket */ private Socket socket; private ServerSocket serverSocket; private OutputStream outputStream; private InputStream inputStream; //endregion public SocketTool socketTool; public SocketServer() { this.socketTool = new SocketTool(); } @Override public void run() { _Log.e(_Log.msg() + "服务端启动了"); //获取IP和初始化必要数据 this.getIpCreateServer(); //接下来应该循环等待用户接入 while (true) { if (this.connect)//判断当前是否处于有客户连接的状态—true代表有用户连接 { this.socketTool.receive(); } else//如果处于未连接状态 { this.waitClient(); //等待客户端打招呼 if (this.socketTool.receiveHello())//和客户端连接成功 { _Log.e(_Log.msg() + "连接成功了***********************************"); this.socketTool.initSendUI(); //现在应该关闭当前页,打开初始界面,就是说可以是开始文件传输了 this.connect = true;//设置好连接标志 continue;//等待真正的数据传来 } else//这个客户端不是自己人,应该断开 { _Log.e(_Log.msg() + "暗号异常连接失败#############################"); this.connect = false; //断开当前Socket try { this.socket.close(); } catch (IOException e) { _Log.e(_Log.msg() + "this.socket.close();%%%%%%%%%%%%%%%%%% catch"); e.printStackTrace(); } } } } } /** 获取IP,创建服务端。这里是一次性操作的 <p/> 它的结果是ServerSocket准备好了 */ private void getIpCreateServer() { try { //region 获取IP InetAddress inetAddress;//获取IP地址 OurIP ourIP = new OurIP(); while (!Global.wifiReceiver.isGetApIp() || (inetAddress = ourIP.getApIp()) == null) { if (this.isMyInterrupt()) { return; } this.sleep(1000); } //endregion _Log.e(_Log.msg() + "服务端的地址为:" + inetAddress); //建立服务端 this.serverSocket = new ServerSocket(this.port, this.backlog, inetAddress); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** 阻塞等待用户接入 <p/> 简单流在这里初始化 */ private void waitClient() { try { _Log.e(_Log.msg() + "正在等待客户端的接入···········"); this.socket = serverSocket.accept();//阻塞线程等待客户端的接入 this.outputStream = this.socket.getOutputStream(); this.inputStream = this.socket.getInputStream(); this.socketTool.setSimpleStream(outputStream, inputStream); } catch (IOException e) { e.printStackTrace(); } } public void stopMySelf() { try { this.setMyInterrupt(true);//设置中断 if (this.socket != null) { this.socket.close(); } if (this.serverSocket != null) { this.serverSocket.close(); } } catch (IOException e) { e.printStackTrace(); } _Log.e(_Log.msg() + "SocketServer结束了"); } } [/code] SocketClient.java [code=java] package a114.yangming.Work.M_Socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import a114.yangming.Util._Log; /** 杨铭 Created by kys_8 on 16/6/3,0003. <p>Email:771365380@qq.com</p> <p>Mobile phone:15133350726</p> <p/> 使用这个类一定是要首先设置好启动它的动作是什么,然后在真正启动的时候设置好DHCP然后才能安全启动 */ public class SocketClient extends Thread { //region 自己的中断标志设置 /** 直接用官方提供的方法,没能解决在线程睡眠时候进行中断操作,所以自己定义一个中断标志进行尝试——没想到成功了,虽然好了,但还是要因对中断的不了解做出检讨 */ private boolean myInterrupt = false; public boolean isMyInterrupt() { return myInterrupt; } public void setMyInterrupt(boolean myInterrupt) { this.myInterrupt = myInterrupt; }//endregion //region 其余标志们 public enum Mark { /** 从FragmentRadar中通过点击启动本线程 */ /** 从FragmnetScanCoce中通过扫描启动线程 */ markClickScan, /** 秋其他启动本线程的动作 */ markNon } private Mark mark; public boolean connect = false; //endregion private String dhcp; private int port = 9999; private Socket socket; private InputStream inputStream; private OutputStream outputStream; public SocketTool socketTool; public SocketClient() { this.socketTool = new SocketTool(); } /** 启动线程 */ @Override public synchronized void start() { switch (mark) { case markClickScan: _Log.e(_Log.msg() + "客户端启动成功"); super.start(); break; case markNon: _Log.e(_Log.msg() + "标示异常,客户端未正常启动"); } } @Override public void run() { try { this.sleep(1000); _Log.e(_Log.msg() + "当前得到的ip为:" + dhcp); InetAddress inetAddress = InetAddress.getByName(dhcp); this.socket = new Socket(inetAddress, port); this.outputStream = this.socket.getOutputStream(); this.inputStream = this.socket.getInputStream(); this.socketTool.setSimpleStream(outputStream, inputStream); }//region 一堆Catch catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); }//endregion this.socketTool.sendString(this.socketTool.hello); if (!this.socketTool.receiveHi())//如果接受到错误的信息就结束本线程 { this.stopMySelf(); return; } else { this.connect = true; } this.socketTool.receive(); } /** 首先标记这个动作,在启动的时候只有有效的动作,才会启动本线程 @param mark 启动本线程的动作是什么 */ public void setMark(Mark mark) { this.mark = mark; } /** 设置DHCP @param dhcp */ public void setDHCP(String dhcp) { if (dhcp == null) { _Log.e(_Log.msg() + "当前DHCP是null"); } this.dhcp = dhcp; } public void stopMySelf() { try { if (this.socket != null) { this.socket.close(); } } catch (IOException e) { e.printStackTrace(); } } } [/code]
android WIFI Socket 文件传输——问题已经解决了,发帖只因高兴--嘿嘿--并不彻底欢迎点评
标签:
原文地址:http://blog.csdn.net/u014587769/article/details/51643148