socket学习之电脑手机通信

来源:互联网 发布:tensorflow可视化 编辑:程序博客网 时间:2024/04/30 12:06

工作好几个月了,在公司才开始接触Java,android.一切从零开始。
为了尽快与公司项目同步,胡乱啃了基本android书,就开始上手一些项目,记得最开始是老大让做一个练手项目:基于android TV的远程电子监控,做的差不多了就没有继续往下进行。后来就一直是做一些修复bug,添加模块的工作。
近段在学习socket,在网上也找了一些示例,比如android手机QQ示例。觉得挺有意思,想玩一玩,瞎弄了很久,终于有一点心得了,在此列出来跟大家分享一下。
由于初学,很多地方捉襟见肘,有不足之处,还望大侠们多多指教。关
于socket的一些基本的常识就不在这里瞎扯了。做了一个简单的由android手机控制pc的一些功能的小实例,跟大家分享一下:
例子的功能有:在手机上控制自己的电脑关机,重启,获取电脑的截屏。先看PC端的server部分:

在服务器端开启一个serversocket:

code:

package com.xluo.Server;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

import com.xluo.common.UserData;
import com.xluo.common.UserMessage;

public class StartServer {
public StartServer(){
ServerSocket ss;
try{
ss = new ServerSocket(5001);//服务端口号,可自行定义,但要和客户端的端口请求号保持一致,而且最好为大于1024的整数,
System.out.println("服务器已启动 at "+new Date());
while(true){
Socket s = ss.accept();
String ClientAddr = s.getInetAddress().toString();
System.out.println(ClientAddr);
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());//获取socket中的对象
UserData ud = (UserData)ois.readObject();//获取从客户端发过来的对象,UserData为自定义的Java文件
boolean shutdown = ud.getShutdown();//是否为关机请求
boolean restart = ud.getRestart();//重启请求
boolean PShow = ud.getGetPicture();//截屏请求
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());//获取socket输出对象
UserMessage um = new UserMessage();//预返回客户端对象
if(shutdown){
um.setType(1);
oos.writeObject(um);//传送消息给客户端
Runtime.getRuntime().exec("cmd.exe /c shutdown -s -t 00");//在PC上执行关机命令
}else if(restart){
um.setType(1);
oos.writeObject(um);
Runtime.getRuntime().exec("cmd.exe /c shutdown -r -t 00");//重启命令
}else if(PShow){
um.setType(1);
um.setB(CopyScreen.getByteFile());//获取截屏并转换成byte[]类型赋值给um对象
oos.writeObject(um);
}
s.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}

这里纠结了我很久的的一个问题是从客户端发送数据到服务端,以获取对象的方式去读取socket中的数据(还有一些其他的方式),失败了很多次,后来才发现客户端发送的对象类必须与服务端接受的类在同一个包名下而且类名也要相同,现在想觉得是一个好弱智的问题,但当时就是不知道,真是瞎子走路一样。。。

在服务端可能大家对截屏CopyScreen这个类比较感兴趣,下面贴一下代码:

package com.xluo.Server;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;

import javax.imageio.ImageIO;

public class CopyScreen {
 private static Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
 public CopyScreen(){
  
 }
 public static byte[] getByteFile() throws Exception{ //得到截屏图片的二进制流
  BufferedImage bi = null;
     bi = (new Robot()).createScreenCapture(new Rectangle(0,0,(int) d.getWidth(),(int) d.getHeight()));//截屏图片的BufferedImage对象
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  ImageIO.write(bi,"png",baos);
  byte[] b = null;
  b = baos.toByteArray();
  return b;
 }
}

其实很简单,首先利用Robot的createScreenCapture做出图片,然后转换成byte流就ok了

到这里,其实server端的工作已经做完了,但是可能有些跟我一样的小白还想知道作为在socket中传递的数据对象文件。

从上面可以看出我定义了两个作为传递数据的对象:1.UserData.java 服务端用于接收,2.UserMessage.java服务端用于发送,这两个文件在客户端就恰好想法,即UserData用于发送,而UserMessage用于接收

下面看一下两个文件:

UserData.java:

package com.xluo.common;

import java.io.Serializable;

public class UserData implements Serializable {
 /**
  *
  */
 private static final long serialVersionUID = 1L;
 private boolean Shutdown;
 private boolean Restart;
 private boolean GetPicture;
 
 public void setShutdown(boolean bl){
  this.Shutdown = bl;
 }
 public boolean getShutdown(){
  return this.Shutdown;
 }
 public void setRestart(boolean bl){
  this.Restart = bl;
 }
 public boolean getRestart(){
  return this.Restart;
 }
 public void setGetPicture(boolean bl){
  this.GetPicture = bl;
 }
 public boolean getGetPicture(){
  return this.GetPicture;
 }
}

 

我习惯把它理解成为流动数据库(纯属个人理解)

UserMessage.java:

package com.xluo.common;

import java.io.Serializable;

public class UserMessage implements Serializable {
 /**
  *
  */
 private static final long serialVersionUID = 1L;
 private int Type ;
 private byte[] b; //用于传递图片的byte数组
 
 public void setType(int t){
  this.Type = t;
 }
 public int getType(){
  return this.Type;
 }
 public void setB(byte[] b){
  this.b = b;
 }
 public byte[] getB(){
  return this.b;
 }
}

我们再来看看客户端:

客户端其实也很简单在一个apk的主界面上画三个按钮,然后分别做关机,重启,获取图片的请求。

 

具体代码如下:

package com.xluo.pcphone;

import com.xluo.common.UserData;
import com.xluo.common.UserMessage;
import com.xluo.socket.UserSocket;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;
import android.support.v4.app.NavUtils;

public class MainActivity extends Activity implements android.view.View.OnClickListener {

 
 private Button bt1,bt2,bt3;
 private UserData ud ;
 
 private Handler mHandler = new Handler(){    //使用handler来处理线程中发送过来的数据
  public void handleMessage(Message msg){
   switch(msg.what){
   case 1:
    UserMessage um = (UserMessage)msg.obj;
    if(um.getType() == 1 && um.getB() == null){
     Toast.makeText(MainActivity.this, "操作成功", Toast.LENGTH_SHORT).show();
    }
    break;
   case 0:
    Toast.makeText(MainActivity.this, "连接服务器失败!", Toast.LENGTH_SHORT).show();
    break;
   default:
    break;
   }
   
  }
 };
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
       
        bt1 = (Button)findViewById(R.id.shutdown);
        bt2 = (Button)findViewById(R.id.restart);
        bt3 = (Button)findViewById(R.id.spicture);
       
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

 public void onClick(View v) {
  // TODO Auto-generated method stub
  switch(v.getId()){   
  case R.id.shutdown: //关机按钮事件
   ud = new UserData();
   ud.setShutdown(true);
   new UserSocket(mHandler,ud).start(); //启动线程,发送请求
   break;
  case R.id.restart: //重启按钮事件
   ud = new UserData();
   ud.setRestart(true);
   new UserSocket(mHandler,ud).start();
   break;
  case R.id.spicture://获取图片按钮事件
   Intent i = new Intent();
   i.setClass(this, PictureShow.class);
   startActivity(i); //跳转到新的activity
   break;
  }
 }

   
}

 

这个文件没什么可讲的,下面我们来看这里启动的线程,

UserSocket.java:

 

package com.xluo.socket;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import com.xluo.common.UserData;
import com.xluo.common.UserMessage;

import android.os.Handler;
import android.os.Message;
public class UserSocket extends Thread{
 private Handler mHandler;
 private UserData ud;
 public UserSocket(Handler h,Object ob){
  this.mHandler = h;   //需要handler处理收到服务器发送过来的数据
  this.ud = (UserData) ob; //将要发送给服务器的数据对象
 }
 public void run(){
  Socket s = new Socket();
  try{
   s.connect(new InetSocketAddress("10.16.6.163",5001),3000);//10.16.6.163为pc的ip,5001为端口号(与服务端监听的端口号一致),3000为连接超时时间(3秒)
   Message msg = new Message(); //实例化要返回给handler的对象
   UserMessage um = new UserMessage(); //作为数据接收对象
   if(!s.isConnected()){ //如果连接不成功
    msg.what = 0;
   }else{
    msg.what = 1;
    ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
    oos.writeObject(ud);  //发送请求
    ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); 
    um = (UserMessage) ois.readObject(); //得到数据包
   }
   msg.obj = um;赋值给msg的对象
   mHandler.removeCallbacksAndMessages(msg.obj);
   mHandler.sendMessage(msg);//返回给handler
  }catch(Exception e){
   e.printStackTrace();
  }
  
 }
}

这个线程的工作就是保持跟服务端通信,得到消息和发送消息

下面我们看一下获取图片的activity

PictureShow.java:

package com.xluo.pcphone;

import com.xluo.common.UserData;
import com.xluo.common.UserMessage;
import com.xluo.socket.UserSocket;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Window;
import android.widget.ImageView;
import android.widget.Toast;

public class PictureShow extends Activity {
 private ImageView iv = null;
 
 private UserData ud = new UserData();

 private Handler mHandler = new Handler(){
  public void handleMessage(Message msg){
   switch(msg.what){
   case 1:
    UserMessage um = (UserMessage)msg.obj;  //得到数据包
    byte[] b = um.getB();  //把数据包中的图片流拿出来
    if(b.length != 0){
     Bitmap bm = getBitmap(b); //转换成bitmap
     iv.setImageBitmap(bm); //显示图片
    }
    break;
   case 0:
    Toast.makeText(PictureShow.this, "连接服务器失败!", Toast.LENGTH_SHORT);
   }
   
  }
  
 };
 
 @Override
 public void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.screen_picture);
  
  iv = (ImageView)findViewById(R.id.screen_picture_img);
  
  ud.setGetPicture(true);
  new UserSocket(mHandler,ud).start();
  Toast.makeText(PictureShow.this,"按返回键后退",Toast.LENGTH_SHORT);
 }
 
 private Bitmap getBitmap(byte[] b){ //把二进制图片转换成bitmap
  if(b.length != 0)
  {
   return BitmapFactory.decodeByteArray(b, 0, b.length);
  }else{
   return null;
  }
 }
}

 


 

实例基本完成了,功能很简单,而且而无美观科研,仅供和我一样菜鸟的童鞋共勉,如有大侠莅临,还望指点江山。小生在学校是学php的,现在工作需要,只得埋头啃Java,玩android。现在正跟中国的社会主义一样,处于初级阶段,很多的东西需要学习。我是一只粪斗小菜鸟,望大家多批评指教!

 

(附:如转载请注明出处!)

原创粉丝点击