设为首页 收藏本站
查看: 989|回复: 0

[经验分享] [转载]Android网络:由手机与手机WIFI互传照片谈Android TCP Socket开发要点

[复制链接]

尚未签到

发表于 2015-10-1 14:43:01 | 显示全部楼层 |阅读模式
  本文背景:局 域网内手机与手机利用wifi建立tcp连接,通过socket互传照片。即一个手机当作服务器,另一个手机是客户端,客户端可以看到服务器指定文件夹内 的图片缩略图,并选择下载到本机。另外,客户端会显示本地某个文件夹内的图片缩略图,并选择上传到服务器。总而言之本例中图片的发送和接收都是双向的。除 发送图片外,还含有字符串(图片的名字)、文件夹内图片的个数(刷新适配器)的发送。
   众 所周知,android常用的网络开发无外乎http和socket,其中http是应用层的协议,tcp是传输层。所以,http也是用socket封 装的,用起来更方便。由于是封装过的,它提供了更强大的功能。socket又分为TCP和UDP,局域网内TCP速度就很快了,鉴于局域网内传送东西不需 要考虑流量,所以此种场合多用socket。首先看下本例的运行效果:
   下面是客户端初始界面:
   DSC0000.jpg

  点击下载后进入下载界面,服务器就开始给客户端传缩略图和图片的名字了,如下:
   DSC0001.jpg

  选择需要下载的图片:
   DSC0002.jpg

  点击确定按钮后,服务器就开始给客户端传大图了,就是原始图片,传输完毕后,弹出提示框:
   DSC0003.jpg

  点击上面的确定后,就自动结束当前activity,返回到初始界面,就不附图了。
  下面是客户端的上传界面,将图片上传到服务器上,这个没啥难的,就是本地图片生成缩略图填充到listview上,不涉及到网络部分。其传输,跟服务器往客户端传东西是一样的。:
   DSC0004.jpg

  最后来看下服务器,由于服务器没任何UI上的要求,所以就是些简单的log打印:
   DSC0005.jpg

  本以为很简单的一个功能,但网上大多数是手机是客户端,PC是服务端。虽然都是流传输,但服务器在PC和手机上解析成图片还是不大一样的,中间走了很多弯路才搞定,另外,就是网上大多是简单的收发,本例中既要传输图片个数、图片名字等,客户端牵涉到切换到activity,要多个socket链接,还是有些麻烦的。下面把开发中遇到的问题、开发要点和步骤记录下来。
  1、权限问题
  这里包括WIFI权限,Internet权限和文件读写权限。我第一次的时候权限没添加完,来来回回折腾了好几次。服务器和客户端的权限是一样的.



[html] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
  •     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />  
  •     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  •     <uses-permission android:name="android.permission.INTERNET" />  
  •     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />  
  •     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /></span>  
  
2、服务器再开启服务前,首先要判断WIFI是否连接,如果没有WIFI连接应该弹出个提示框。如果有WIFI连接上了,则进一步提取出IP地址。我将其封装到NetworkUtil类里弄成静态方法,如下:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.util;  

  • import android.content.Context;  
  • import android.net.ConnectivityManager;  
  • import android.net.NetworkInfo;  
  • import android.net.wifi.WifiInfo;  
  • import android.net.wifi.WifiManager;  

  • public class NetworkUtil {  

  •     /**
  •      *判断wifi是否连接  
  •      * @param context
  •      * @return  
  •      */  
  •     public static boolean isWiFiConnected(Context context){  
  •         ConnectivityManager connectManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
  •         NetworkInfo networkInfo = connectManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
  •         if(networkInfo.isConnected()){  
  •             return true;  
  •         }
  •         else{  
  •             return false;  
  •         }

  •     }

  •     /**
  •      * 得到wifi连接的IP地址
  •      * @param context
  •      * @return
  •      */  
  •     public static String getWifiIP(Context context){  
  •         WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
  •         WifiInfo wifiInfo = wifiManager.getConnectionInfo();
  •         int ipAddr = wifiInfo.getIpAddress();  
  •         String ipStr =  int2string(ipAddr);
  •         return ipStr;  
  •     }

  •     /**
  •      * 输入int 得到String类型的ip地址
  •      * @param i
  •      * @return
  •      */  
  •     private static String int2string(int i){  

  •         return (i & 0xFF)+ "." + ((i >> 8 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) +"."+((i >> 24 ) & 0xFF );  

  •     }
  • }
  • </span>
  
在wifi地址这块,当时还参考了网上一个,但是转出来的string类型的IP地址是错误的,上面贴的代码是正确的。如果wifi没有连接,则弹出提示框报错:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    public void showAlterDialog(){  
  •         Dialog alterDialog = new AlertDialog.Builder(this).  
  •                 setTitle("警告").  
  •                 setMessage("当前WiFi没有正常连接,请连接后再操作.").  
  •                 setIcon(android.R.drawable.ic_dialog_alert).
  •                 setPositiveButton("确定", new DialogInterface.OnClickListener() {  

  •                     @Override  
  •                     public void onClick(DialogInterface dialog, int which) {  
  •                         // TODO Auto-generated method stub  
  •                         dialog.dismiss();
  •                     }
  •                 }).
  •                 create();
  •         alterDialog.show();

  •     }</span>
  3、服务器点击开启服务后,如果WIFI连接好了。service没有运行就要开启Service了。当前Activity名字为ServerActivity,服务是TCPService。



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">if(!isServiceRun){  
  •         startService(new Intent(ServerActivity.this,TCPService.class));  
  •         noticeTextView.append("\n" + "服务已开启," + "正在服务..."+"\n");  
  •         manageButton.setText("销毁共享服务");  
  •         isServiceRun = true;  
  •          }else{  
  •         stopService(new Intent(ServerActivity.this, TCPService.class));  
  •         noticeTextView.append("\n" + "服务已销毁"+"\n");  
  •         isServiceRun = false;  
  •         manageButton.setText("开启共享服务");  
  •         }</span>
  
  4、服务器端因为用了service,所以牵涉到Activity和Service的通信问题,最常见的莫过于用广播了。在ServerActivity里定义一个继承BroadcastReceiver的类。



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    /**
  •      * @author Administrator
  •      *Activity接收Service消息,更新UI
  •      */  
  •     public class MsgReceiver extends BroadcastReceiver{  

  •         @Override  
  •         public void onReceive(Context context, Intent intent) {  
  •             // TODO Auto-generated method stub  
  •             String s = intent.getStringExtra("INFO");  
  •             noticeTextView.append(s);
  •         }

  •     }</span>
  
在onCreate()里实例化并注册:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">msgReceiver = new MsgReceiver();  
  •         IntentFilter intentFilter = new IntentFilter();  
  •         intentFilter.addAction("org.yanzi.shareserver.receiver");  
  •         registerReceiver(msgReceiver, intentFilter);</span>
  其中的addAction是为了区别哪个广播。这个标签和Service里保持同步。如果是有多个Service的话,Activity利用它来区分是哪个Service发来的广播。
  然后再Service里利用下面代码给Activity发广播:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">private Intent mIntent = new Intent("org.yanzi.shareserver.receiver");</span>  
  




[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    /**
  •      * 通知Activity更新UI
  •      * @param s
  •      * @param len
  •      */  
  •     public void sendBroadcastString(String s, int len){  
  •         mIntent.putExtra("INFO", s +"长度  = "+ len+ "\n");  
  •         sendBroadcast(mIntent);
  •         mIntent.removeExtra("INFO");  
  •     }</span>
  mIntent发送完消息后就remove一下。
  
  5、接着就轮到Service上场了。想对于一般的Socket DEMO服务器功能都比较单一,本例的Service任务和逻辑如下:
  a、首先有个全局的socket,负责监听客户端的请求,是 下载 还是上传;
  b、如果监听到是下载,就把这个socket关闭。然后再开个socket和客户端连接,此时对应的客户端页面也进行了切换,客户端也开了一个socket负责接收图片的。在传输图片时一定要先传递图片的个数,否则的话客户端的listview没法实例化,不知道view有多少个。
  【备注:Socket的对等性
  Socket的对等性一定要时刻清晰,socket就像一条线的两个端,连接服务器和客户端。服务器的socket对等于客户端的socket,是一一对应的。其实用对应还不大准确,就如同手心和手背一样,一体两面。因此服务器和客户端,两端的socket如果连接好了,任何一端关闭socket都会造成对方的socket关闭。不仅如此,socket、输入流、输出流,三者有任何一个关闭都会造成整个socket连接的关闭。但输出流的flush()函数不会造成这个问题,在发送东西时flush也是必须的。因此,大量网上给出的socket示例,说发送完毕后都要把输出流close()掉其实是不对的。如果有多个图片,一定要等图片发送完毕后,再去close。
  6、服务器的Service里可以弄个死循环一直监听,也可以弄个线程一直监听。这里是弄的线程ServiceThread,如果要考虑多客户端,这块要弄成线程池,即来一个客户端连接上,就专门开个线程来处理。
  在Service的onCreate函数里new一个ServertSocket,将监听的线程实例化:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    /**
  •      * 初始化ServerSocket,buffer分配内存
  •      */  
  •     public void initTCPService(){  
  •         try {  
  •             mServerSocket = new ServerSocket(PORT);  
  •             Log.i(tag, "Service启动监听...");  
  •         } catch (IOException e) {  
  •             // TODO Auto-generated catch block  
  •             e.printStackTrace();
  •         }
  •         if(mServiceThread == null){  
  •             mServiceThread = new ServiceThread();  
  •         }

  •     }</span>
  此后mServerSocket就会一直监听PORT端口。下面是我的ServiceThread的完整实现:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    class ServiceThread extends Thread{  

  •         @Override  
  •         public void run() {  
  •             // TODO Auto-generated method stub  
  •             while(allowRun){  
  •                 try {  
  •                     client = mServerSocket.accept();
  •                     Log.i(tag, "Service监听到消息...");  
  •                     InputStream inputStream = client.getInputStream();
  •                     int temp = 0;  
  •                     while((temp = inputStream.read(mByteBuffer)) != -1){  
  •                         if(temp < 10){  
  •                             String s = new String(mByteBuffer, 0, temp);  
  •                             inputStream.close();
  •                             client.close();
  •                             if(s.trim().equals(new String("DOWNLOAD"))){  
  •                                 sendBroadcastString("客户端下载开始:" + s, temp);  
  •                                 sendThumnail(SRC_PAH);
  •                             }
  •                             else if(s.trim().equals(new String("UPLOAD"))){  
  •                                 sendBroadcastString("客户端上传开始:" + s, temp);  
  •                                 getUploadImages();
  •                             }
  •                         }
  •                         else{  
  •                             String s = new String(mByteBuffer, 0, temp);  
  •                             Log.i(tag, "temp = " + temp+" = " + s);  
  •                             inputStream.close();
  •                             if(client.isConnected()){  
  •                                 client.close();
  •                             }
  •                             sendBroadcastString("服务器:收到客户端的下载列表 = " + s,temp);  
  •                             sendImage(s.trim());
  •                         }
  •                     }
  •                 } catch (IOException e) {  
  •                     // TODO Auto-generated catch block  
  •                     e.printStackTrace();
  •                 }
  •             }
  •         }
  •     }</span>
  这个Thread一旦运行就会进入死循环,如果服务端监听到有客户端连接,client = mServerSocket.accept(); client的类型是Socket。之后程序会阻塞在InputStream inputStream = client.getInputStream();这一句,而不是上面的accept()这里。
  简单介绍下InputStream和OutputStream,当socket是接收方时,就用Socket.getInputStream(),得到的是输入流。如果是socket想发送东西,就用Socket.getOutputStream。无论是输入流还是输出流,都是为了和基本数据类型进行转换。何为基础数据类型?比如byte[]这些。其实InputStream是利用read函数,将输入流的东西read到基础数据类型里。OutputStream是利用write函数,将基本数据类型的东西write到流里面。
上面的代码中,首先判断客户端发来的是字符串有多长,如果小于10,则认为他是命令级的,即要么是上传或下载。根据上传或下载再跳到具体的函数里开一个新的socket去发送和接收图片。如果string的长度大于10,那就认为是客户端勾选的下载列表的名字。其实这么搞不大精确,可以客户端和服务端约定好,流的第一个字节写进入一个int,用来区分判断是下载和上传,及其他功能。
  7、手机与手机TCP Socket发送字符串:
  最简单的就是writeUTF然后接收方readUTF就ok来了。我这里弄的还是有点复杂了,见客户端里的



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">sendMsgToServer</span>  
  函数。
  8、手机与手机TCP Socket发送图片,这里是以服务端上的发送图片和接收图片来示例的:
  发送图片个数:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">Socket socket = null;  

  •         Bitmap bitmap = null;  
  •         try {  
  •             socket = mServerSocket.accept();
  •         } catch (IOException e1) {  
  •             // TODO Auto-generated catch block  
  •             e1.printStackTrace();
  •         }
  •         //第一次发送文件夹内有多少个图片  
  •         int num = listFile.length;  
  •         try {  
  •             DataOutputStream dos1 = new DataOutputStream(socket.getOutputStream());  
  •             dos1.writeInt(num);
  •             dos1.flush();
  •             Log.i(tag, "服务端:文件夹下图片数 = " + num);  
  •         } catch (IOException e1) {  
  •             // TODO Auto-generated catch block  
  •             e1.printStackTrace();
  •         }</span>
  先往流里写入有多少个图片,将来客户端readInt读出来传给适配器。
  下面是发送图片:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">FileInputStream fileStream = new FileInputStream(listFile);  
  •     bitmap = BitmapFactory.decodeStream(fileStream);
  •     Bitmap thumBitmap = ThumbnailUtils.extractThumbnail(bitmap, THUM_WIDTH, THUM_HEIGHT);
  •     ImageUtil.saveJpeg(thumBitmap);
  •     //发送给客户端  
  • DataOutputStream dos = new DataOutputStream(socket.getOutputStream());  
  • ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  • thumBitmap.compress(Bitmap.CompressFormat.JPEG, 85, baos);  
  • Log.i(tag, "baos.size() " + baos.size());  
  • dos.writeInt(baos.size());

  • byte[] bytes = baos.toByteArray();  
  • dos.write(bytes);
  • dos.writeUTF(name);//写进去名字  

  • dos.flush();</span>
  
在已有Bitmap的情况,Bitmap---compress到ByteArrayOutPutStream baos-----baos转到byte[] bytes里,然后将基础数据byte[] bytes write到dos里取,dos的类型如下:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">DataOutputStream dos = new DataOutputStream(socket.getOutputStream());</span>  
  
接收图片,还是以服务器为例,我就把接收客户端传来的照片函数getUploadImages()完整贴出来:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">public void getUploadImages(){  
  •         Socket socket = null;  
  •         int numNeedDownload = 0;  
  •         try {  
  •             socket = mServerSocket.accept();
  •             DataInputStream is = new DataInputStream(socket.getInputStream());  
  •             numNeedDownload = is.readInt(); //获得需要下载的个数  
  •             sendBroadcastString("服务器:客户端将要上传文件个数 = " +numNeedDownload, 000);  
  •         } catch (IOException e) {  
  •             // TODO Auto-generated catch block  
  •             e.printStackTrace();
  •         }

  •         for(int i=0; i<numNeedDownload; i++){  
  •             try {  
  •                 DataInputStream is = new DataInputStream(socket.getInputStream());  
  •                 int size = is.readInt(); //得到byte的长度  
  •                 byte[] buffer = new byte[size];  
  •                 int len = 0;  
  •                 while(len < size){  
  •                     len += is.read(buffer, len, size-len);
  •                 }
  •                 Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);  
  •                 if(bitmap == null){  

  •                     Log.i(tag, "服务器:bitmap为空");  
  •                 }
  •                 String name = is.readUTF();
  •                 ImageUtil.saveJpeg(bitmap, name);
  •                 sendBroadcastString("服务器:"+name+"接收成功", 000);  
  •             } catch (IOException e) {  
  •                 // TODO Auto-generated catch block  
  •                 e.printStackTrace();
  •             }
  •         }
  •         //接收完毕后,关闭socket  
  •         try {  
  •             socket.close();
  •         } catch (IOException e) {  
  •             // TODO Auto-generated catch block  
  •             e.printStackTrace();
  •         }
  •     }</span>
  
首先获得需要下载的个数,然后进入一个循环。将得到的输入流读到byte里,这里通过
  int size = is.readInt(); //得到byte的长度
byte[] buffer = new byte[size];
int len = 0;
while(len < size){
len += is.read(buffer, len, size-len);
}

  先获得输入流里存的照片的byte长度,然后进入while循环,读到buffer里。之后就是BitmapFactory解析了。再从流里读出来图片的名字。
客户端部分:
  1、客户端跟服务器端差不多,唯一不同的是服务器是ServertSocket负责监听,监听到连接后调用accept()函数得到socket。而客户端是直接new一个socket,连接服务器。需要注意客户端的点击下载和上传的执行要写在单独的线程里,自从android4.0后,关于网络的操作都不能放在主线程里。下面的代码是客户端向服务器发送选择的下载列表名字:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    /**
  •      * @author Administrator
  •      *发送给服务器,需要下载的图片的名字
  •      */  
  •     class SendDownName implements Runnable{  

  •         @Override  
  •         public void run() {  
  •             // TODO Auto-generated method stub  
  •             String sendName = getNeedDownNames();
  •             try {  
  •                 Socket socket = new Socket(Ip, Port);  
  •                 sendMsgToServer(socket, sendName);
  •                 socket.close();
  •                 mainHanlder.sendEmptyMessage(START_DOWNLOAD_ASYNCTASK);
  •             } catch (UnknownHostException e) {  
  •                 // TODO Auto-generated catch block  
  •                 e.printStackTrace();
  •             } catch (IOException e) {  
  •                 // TODO Auto-generated catch block  
  •                 e.printStackTrace();
  •             }
  •         }

  •     }
  •     public void sendMsgToServer(Socket socket, String s){  
  •         try {  
  •             OutputStreamWriter outputStrWriter = new OutputStreamWriter(socket.getOutputStream());  
  •             BufferedWriter buffWriter = new BufferedWriter(outputStrWriter);  
  •             PrintWriter printWriter = new PrintWriter(buffWriter);  
  •             printWriter.println(s);
  •             printWriter.flush();
  •             printWriter.close();

  •         } catch (IOException e) {  
  •             // TODO Auto-generated catch block  
  •             e.printStackTrace();
  •         }
  •     }</span>
  
当发送完毕后,给mainandler传一个消息,开一个异步线程类来接收图片。在下载缩略图过程中,每次接受到一个图片都应该通知listview的适配器更新一下,mainHanlder.sendEmptyMessage(UPDATE_LIST_ADAPTER);也是通过mainHandler来做的。



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">    case UPDATE_LIST_ADAPTER:  
  •                 listViewAdapter.notifyDataSetChanged();
  •                 break;</span>  
  
2、客户端里用了AsyncTask,博主<span>singwhatiwanna</span>关于它的用法讲的十分透彻,参见:http://blog.iyunv.com/singwhatiwanna/article/details/17596225  http://blog.iyunv.com/singwhatiwanna/article/details/9272195 补充一点,在AsyncTask的onPreExecute() 、onPostExecute()都只能做主线程UI的事情,而不能操作网络这些主线程不允许做的事情。 AsyncTask<Void, Integer, Void>的第一个参数输入、第二个参数是中间值、第三个参数是输出。本例为了能接收到一个图片,就更新下progressbar,需要以下语句:
  在doInBackground()里publishProgress(mCnt); 然后是下面的:



[java] view plaincopyprint?

  • <span style="font-family:Comic Sans MS;font-size:18px;">        @Override  
  •         protected void onProgressUpdate(Integer... values) {  
  •             // TODO Auto-generated method stub  
  •             super.onProgressUpdate(values);  
  •             Log.i(tag, "进度条 = " + values[0]);  
  •             progressBar.setProgress(values[0]);  
  •         }</span>
  
本例遗留的问题:
  现在是客户端接收到一个图片然后进度条走一下,但是我们用手机蓝牙传照片时,是一个图片在传输过程中就能显示进度的变化。这就需要在传输时,分好几份来传,然后合并。等完善了进度条的UI再贡献源码下载。
  ------------------------本文系原创,转载请注明作者yanzi1225627
  欢迎Android爱好者加群,群号:192413115 备注: yanzi

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-121431-1-1.html 上篇帖子: Mesh Wifi 下篇帖子: Android 控制WIFI相关操作
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表