socket學習之電腦手機通訊

來源:互聯網
上載者:User

工作好幾個月了,在公司才開始接觸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。現在正跟中國的社會主義一樣,處於初級階段,很多的東西需要學習。我是一隻糞鬥小菜鳥,望大家多批評指教!

 

(附:如轉載請註明出處!)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.