1. 程式人生 > >android socket通訊demo (本篇服務於android訊息推送)

android socket通訊demo (本篇服務於android訊息推送)

本文系作者原創,轉載請附原文地址,謝謝。

文章末尾提供本文中的原始碼下載連結,需資源積分1分,人艱不拆,下載後評論資源可獲系統返回積分=無損!

前言:

關於什麼是socket通訊,本篇文件中不進行解釋,不甚清楚的可以去百科查詢,日後得空我也會整理相關的內容。

本文是對上一篇關於訊息推送的文章的補充,此處給出快速連結:http://blog.csdn.net/a774057695/article/details/47024887

本篇書寫的目的:

1.       讓android客戶端和伺服器端實現socket通訊,且demo需實現雙向

2.       為實現伺服器向客戶端推送訊息奠定基礎

特別說明:

1.       伺服器端利用pc機執行apach伺服器模擬

2.       Pc整合xampp環境

3.       伺服器端eclipse編譯,java實現

4.       客戶端使用真機除錯(使用模擬器需要對原始碼進行修改,因為沒有嘗試,所以沒有給出示例程式碼,使用模擬器調試出錯的請留意該點)

5.       手機和pc加入同一域,至少連入同一臺路由器,使用wifi共享的方法本文不提供技術支援、不保證其可行性。

功能概述:

l  伺服器端:

主函式實現:阻塞的程式碼段等待socket連線、啟動執行緒為socket連線提供應答;

互動執行緒(暫且這樣稱呼):實現收到客戶端訊息後進行反饋

單向執行緒(暫且這樣稱呼):實現接受控制檯輸入並向客戶端傳送

連線斷開刪去該條socket

l  客戶端:

實現向伺服器端建立socket連線

啟動執行緒監聽伺服器傳送的訊息、並反饋UI顯示

將本地訊息發往伺服器

程式碼結構:

伺服器端:(主要)

       |-- MyServer

              |--src

                     |--[package]

                            |--MyServer.java

                            |--ServerThread.java

                            |--Thread2.java

客戶端:(主要)

       |--MultiThreadClient

              |--src

                     |--[package]

                            |--ClientThread.java

                            |--MainActivity.java

              |--res

                     |--layout

                            |--activity_main.xml

              |--AndroidManifest.xml

程式碼示例及關鍵點:

l  伺服器端:

MyServer.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class MyServer {
   //定義儲存所有Socket的ArrayList
   public static ArrayList<Socket> socketList = new ArrayList<Socket>();
   /**
    * @param args
    * @throws IOException
    */
   public static void main(String[] args) throws IOException {
      // param 埠號
      ServerSocket ss = new ServerSocket(30000);
      while(true){
         //此程式碼會阻塞,將一直等待別人連線
         Socket s = ss.accept();
         socketList.add(s);
         /*每當客戶端連線後啟動一條ServerThread執行緒為該客戶端服務*/
         new Thread(new ServerThread(s)).start();
         new Thread(new Thread2(s)).start();
      }
   }
 
}

此處我們需要留意的是埠號,第一不要去使用系統關鍵服務的埠號,第二我們要注意客戶端中的埠號需要匹配,在後面我們還會提及。

ServerThread.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
 
public class ServerThread implements Runnable {
 
   //定義當前執行緒所處理的Socket
   Socket s = null;
   //該執行緒所處理的Socket所對應的輸入流
   BufferedReader br = null;
   public ServerThread(Socket s) throws IOException {
      this.s = s;
      //初始化該Socket對應的輸入流
      br = new BufferedReader(new InputStreamReader(s.getInputStream(),
            "utf-8"));
   }
 
   @Override
   public void run() {
      try
      {
         String content = null;
         //採用迴圈不斷從Socket中讀取客戶端傳送過來的資料
         while((content = readFromClient())!=null){
            //遍歷socketList中的每個Socket
            //將讀到的內容向每個Socket傳送一次
            for(Socket s : MyServer.socketList){
                OutputStream os = s.getOutputStream();
                //簡單處理模擬應答
                os.write((content + "——返回自伺服器\n").getBytes("utf-8"));
            }
         }
      }catch (IOException e) {
         e.printStackTrace();
      }
   }
   /**
    * 讀取客戶端資料
    * @return
    */
   public String readFromClient()
   {
      try{
         return br.readLine();
      }catch(IOException e){   //如果捕捉到異常,表明該Socket對應的客戶端已經被關閉
         //刪除該Socket
         MyServer.socketList.remove(s);
      }
      return null;
   }
 
}

這裡也沒有太多值得注意的,這條執行緒是實現對客戶端應答,當客戶端向伺服器發出訊息,伺服器收到後進行反饋,這裡我們只是對原來的內容進行了簡單的處理,可以按照伺服器-客戶端之間的約定,實現更有意義的事情。

當連線斷開時,刪除該socket

Thread2.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
 
 
public class Thread2 implements Runnable {
 
   //定義當前執行緒所處理的Socket
   Socket s = null;
   //該執行緒所處理的Socket所對應的輸入流
   BufferedReader br = null;
 
   public Thread2(Socket s) throws IOException {
      this.s = s;
      //初始化該Socket對應的輸入流
      br = new BufferedReader(new InputStreamReader(s.getInputStream(),
            "utf-8"));
   }
  
   /**
    * 讀取控制檯輸入
    * */
   private void readConsole() {
        //陣列緩衝
        byte[] b = new byte[1024];
        //有效資料個數
        int n = 0;
        try{
         while(true){
              //提示資訊
              System.out.println("請輸入:");
              //讀取資料
              n = System.in.read(b);
              //轉換為字串
              String str = new String(b,0,n - 2);
            //傳送到客戶端
             for(Socket s : MyServer.socketList){
                OutputStream os = s.getOutputStream();
                os.write(("伺服器傳送"+str + "\n").getBytes("utf-8"));
            }
              //判斷是否是quit
              if(str.equalsIgnoreCase("quit")){
                        break; //結束迴圈
              }
              //回顯內容
              System.out.println("輸入內容為:" + str);
             }
        }catch(Exception e){}
   }
  
 
   @Override
   public void run() {
      readConsole();
     
   }
}

這條執行緒實現讀取控制檯輸入內容並向客戶端傳送,這裡我們沒有關心連線斷開,這與demo的目的不衝突,但是投入生產時還是應當注意。

l  客戶端:

AndroidManifest.xml

建立一個應用,首先做得事情不是在預設開啟的佈局檔案中進行佈局,而是規劃一下需要哪些許可權,並在配置檔案中新增!

本處我們需要申請Internet許可權:

<uses-permission android:name="android.permission.INTERNET"/>

手打的朋友請注意:是uses,不是user!

activity_main.xml

預覽:(真的太大,求調大小功能)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="bottom"
    android:orientation="vertical" >
 
<!--個並不美觀的佈局 -->
    <EditText
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/linearLayout1"
        android:layout_alignParentLeft="true"
        android:cursorVisible="false"
        android:gravity="top" />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/linearLayout1"
        android:orientation="horizontal"
        android:weightSum="5"
        android:layout_alignParentBottom="true" >
 
        <EditText
            android:id="@+id/input"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
             >
            <requestFocus />
        </EditText>
 
        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="send" />
    </LinearLayout>
 
</RelativeLayout>

MainActivity.java

package com.example.multithreadclient;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
 
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends Activity {
 
       EditText input, show;
       TextView mode;
       Button send,smode,cmode;
       OutputStream os;
       Handler handler;
 
       @Override
       protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              input = (EditText) findViewById(R.id.input);
              send = (Button) findViewById(R.id.send);
              show = (EditText) findViewById(R.id.show);
             
              handler = new Handler() {
                     public void handleMessage(android.os.Message msg) {
                            if (msg.what == 0x123) {
                                   show.append("\n" + msg.obj.toString());
                            }
                     };
              };
              new AsyncTask<String, String, String>(){
 
                     @Override
                     protected String doInBackground(String... params) {
                            // TODO Auto-generated method stub
                            Socket s;
                            try {
                                   /**@ param param1 param2
                                    * param1: 伺服器ip地址,本例中,即pc機的ip地址 cmd輸入ipconfig -all可查詢
                                    * param2:埠號 注意和伺服器原始碼中的匹配本
                                    *例中30000
                                    * */
                                   s = new Socket(param1,param2);
                                   //客戶端啟動ClientThread執行緒,不斷讀取來自伺服器的資料
                                   new Thread(new ClientThread(s, handler)).start();
                                   os = s.getOutputStream();
                            } catch (Exception e) {
                                   e.printStackTrace();
                            }
                            return null;
                     }
                    
              }.execute("");
             
              send.setOnClickListener(new OnClickListener() {
 
                     @Override
                     public void onClick(View v) {
                            // TODO Auto-generated method stub
                            try {
                                   //將使用者在文字框中輸入的內容寫入網路
                                   os.write((input.getText().toString() + "\r\n")
                                                 .getBytes("utf-8"));
                                   input.setText("");
                            } catch (IOException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                            }
                     }
              });
       }
      
       @Override
       protected void onDestroy() {
              super.onDestroy();
              try {
                     os.write("".getBytes("utf-8"));
              } catch (UnsupportedEncodingException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
       }
}

值得特別注意的就是兩個引數!在註釋中已經說得很明白了,第一個是pc機的ip,第二個是伺服器約定的埠號。使用模擬器進行除錯需要修改ip,我印象中是10.0.2.2,而127.0.0.1會被認為是android模擬器本身。

ClientThread.java

package com.example.multithreadclient;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
 
import android.os.Handler;
import android.os.Message;
 
public class ClientThread implements Runnable {
 
       private Socket s;  //該執行緒負責處理的Socket
       private Handler handler;
       //該執行緒所處理的Socket對應的輸入流
       BufferedReader br = null;
 
       public ClientThread(Socket s, Handler handler) throws IOException {
              this.s = s;
              this.handler = handler;
              br = new BufferedReader(new InputStreamReader(s.getInputStream()));
       }
 
       @Override
       public void run() {
              // TODO Auto-generated method stub
              try {
                     String content = null;
                     //不斷讀取Socket輸入流中的內容
                     while ((content = br.readLine()) != null) {
                            //每當讀到來自伺服器的資料後,傳送訊息通知程式介面顯示資料
                            Message msg = new Message();
                            msg.what = 0x123;
                            msg.obj = content;
                            handler.sendMessage(msg);
                     }
              } catch (Exception e) {
                     e.printStackTrace();
              }
 
       }
}

此處沒有特別關注的地方

執行測試:

除錯android應用,將其安裝到手機,只是想先裝上去而已

執行xampp panel 啟動apach服務

Eclipse中執行伺服器端程式

重新啟動手機端的客戶端程式——只是需要伺服器先啟動,客戶端再連線而已。

你可以在eclipse的console中輸入想要傳送的訊息,回車會被去掉,輸入quit回車可以停止之,然並卵

你也可以在手機端傳送訊息等待回傳訊息

截圖就不附了

本文系作者原創,轉載請附原文地址,謝謝。

本文中的原始碼下載連結:http://download.csdn.net/detail/a774057695/8929341 需資源積分1分,人艱不拆,下載後評論資源可獲系統返回積分=無損!