2013年5月6日 星期一

[Android][JAVA] Android 與 PC JAVA 間的 Socket 連線(Android Client Java Server)

要讓 Android 與一般的 Java 程式透過網路溝通,我們最習慣的是使用 Socket 來做連線,而使用一般的 java 寫法可能會發現無法建立連線。

因為兩端皆須透過 Thread 使用額外的執行緒來處理建立連線這塊,可以參考以下的做法:

(本範例僅供連線通訊端的程式碼,並無做加密的控制)





Android Client

package com.example.androidclient;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import android.os.Bundle;
import android.app.Activity;
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 {

 static EditText ed;
 static TextView tv1,tv2;
 static Button btn ;
 static String str1="0",str2="0";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //先建立好與XML的元件連結
        ed = (EditText) findViewById(R.id.edt);
        tv1 = (TextView) findViewById(R.id.show01);
        tv2 = (TextView) findViewById(R.id.show02);
        btn = (Button) findViewById(R.id.btn);
        //撰寫一個按鈕事件,當按下按鈕時,才啟動一些功能
        btn.setOnClickListener(new OnClickListener() 
        {public void onClick(View arg0) 
  {
         //建立 thread 的物件
         Thread t = new thread();
            //啟動thread
         t.start();
            try {
             //先讓他等待個 3 秒
       t.sleep(3000);
      } catch (InterruptedException e) {}
            //之後在讓它顯示在銀幕上,有時網路不夠穩時,傳輸會較慢,而導致傳誦與接收都尚未完成,便顯示到銀幕上了
            tv1.setText(str1);
            tv2.setText(str2);
  }});    
    }
    class thread extends Thread{
     public void run() {
      try{
      System.out.println("Waitting to connect......");
      String server=ed.getText().toString();
      int servPort=1025;
      Socket socket=new Socket(server,servPort);
      InputStream in=socket.getInputStream();
      OutputStream out=socket.getOutputStream();
      System.out.println("Connected!!");
      byte[] rebyte = new byte[18];
      in.read(rebyte);
      str2 ="(Client端)接收的文字:"+ new String(new String(rebyte));
      String str = "android client string";
      str1 = "(Client端)傳送的文字:"+str;
      byte[] sendstr = new byte[21];
      System.arraycopy(str.getBytes(), 0, sendstr, 0, str.length());
      out.write(sendstr);
      }catch(Exception e)
      {
       System.out.println("Error: "+e.getMessage());
      }
      }
    }
}




Java Server

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class jSer extends Thread {

    public static void main(String[] argv) {
        Thread t = new thread(); 
        t.start(); 
        }
    }

class thread extends Thread{
 public void run() {
  try{
  System.out.println("Waitting to connect......");
  ServerSocket servSock=new ServerSocket(1025);
  Socket clntSock=servSock.accept();
  InputStream in=clntSock.getInputStream();
  OutputStream out=clntSock.getOutputStream();
  System.out.println("Connected!!");
  String str = "java server string";
  System.out.println("(Server端)傳送的字串:"+str);
  byte[] sendstr = new byte[18];
  System.arraycopy(str.getBytes(), 0, sendstr, 0, str.length());
  out.write(sendstr);
  
  byte[] re = new byte[21];
  in.read(re);
  String s = new String(re);
  System.out.println("(Server端)接收的字串:"+s);
  }catch(Exception e)
  {
   System.out.println("Error: "+e.getMessage());
  }
  }
}



專案檔下載:file

56 則留言:

  1. 請問 我在電腦 android 模擬器上 可以成功連上去

    但是 我匯到手機後 就連不到了 請問是哪裡出了問題?

    回覆刪除
    回覆
    1. 1.首先確定電腦的防火牆是否將它的連線拒絕。(如果不知如何設定,直接把windows防火牆關閉)
      (模擬器跟伺服器端都在電腦,所以不會接觸道防火牆)
      2.Android端的AndroidManifest.xml要記得給予網路權限。

      刪除
  2. 大大!!我也是 用模擬器都可以,但用實機WIFI,3G,都連不到。
    電腦的防火牆也關了,PORT也設定了!!都連不到
    請求指點!!!!

    回覆刪除
    回覆
    1. Android端的AndroidManifest.xml要記得給予網路權限~
      我明天在用實機在跑一次試試看好了

      刪除
    2. 剛剛測試的結果是可以正常運作的,請先檢查該區段的網路是否有類似防火牆的設定,如校園防火牆之類的。

      刪除
    3. Rang Wang感謝您。成功了。
      原來是我自己有裝一個防火牆路由器。
      我把線全部接回電信機上盒,就可以了!!!
      只是說也奇怪,電腦對電腦沒問題!!
      為什麼手機對電腦這樣就會被那個防火路由給擋下來。
      但還是謝謝您,感恩。

      刪除
    4. 恭喜你啦!!跟你分享一個之前開發上的經驗,像這個專案的連線裡面有用到Thread,其實相同的運作模式,如果是Java 對 Java,是無需要寫成Thread的,但是把他移植到Android後,因為如果不寫成Thread(所謂的多工,也可以說是副程序),他會影響本身的主程序而無法運作。所以在Android和Java開發上仍有許多不同之處,加油!

      刪除
  3. 請問一下 ~ 不用設定IP的位址嗎 ?

    回覆刪除
    回覆
    1. Client 需要設定遠端Server IP,Server的IP則是以當下環境的IP

      刪除
  4. 請問 可以Android Client傳給C Server嗎?

    回覆刪除
    回覆
    1. 可以的,基本上只要雙方有做好協定就可傳送與收法,不過我這沒有寫C的server喔sorry

      刪除
  5. 您好,我想請問您如何將程式改成client可與server互傳文字呢?謝謝!

    回覆刪除
  6. 請參考我小改的程式範例:http://ppt.cc/SaHC

    回覆刪除
  7. 可以請問一下!! 我都照您的程式去寫
    然後port 改成3200 (因為您用的1025 他說要有什麼權限的)
    用模擬器模擬的結過 str1 跟 str2 依然是0(我server沒寫 所以str2是0沒錯)
    但是str1 應該是 "android client string" 吧!!
    我用wireshark 去監測3200的port 也沒偵測到有東西,可以麻煩解答一下嗎?謝謝

    回覆刪除
    回覆
    1. 因為該程式使用到Socket,所以必須兩端(client and server)建立好連線了,才會進行傳輸,您提到「server沒寫」,那連線尚未建立起來則無法進行傳輸的。
      此外,別忘了要給client的Android網路的權限喔!

      刪除
    2. 您好:
      我傳送端已經寫好 目前用模擬器做測試, 然後接收端有用c#寫了一個監聽本機的port
      可是也沒收到東西,所以才去網路上面找到一個程式叫wireshark
      可以用它來監聽本機的PORT
      我用android模擬器來做傳送,也沒收到封包,
      然後我想是不是因為沒收到從server端來的資料所以將client端的接收隱蔽掉
      可是還是對PORT進行監聽也沒有封包耶!!

      刪除
    3. 我目前想到的可能在於,您的server端沒有與這個client端的socket建立連線。
      在Android Client裡面的這行「Socket socket=new Socket(server,servPort);」
      其實,如果他沒有與Server建立連線成功,他會直接丟出Exception。

      看看要不要在「Socket socket=new Socket(server,servPort);」下面一行加入程式碼「System.out.println("已連線完成");」,並進入logcat中查看,是否有這行顯示出來如果有表示兩端有建立連線了,如果沒有,表示連線是失敗的。

      刪除
    4. 把server端架好才能連線!!
      謝謝了~~

      刪除
  8. 您好~
    請問可否達到只按一次就可以保持連線不中斷
    在哪邊一直等待Server傳遞多次資訊?

    回覆刪除
    回覆
    1. 其實可以這樣處理:
      先在其它地方建立好 socket連線
      然後client端重複使用這段:
      byte[] sendstr = new byte[21];
      System.arraycopy(str.getBytes(), 0, sendstr, 0, str.length());
      out.write(sendstr);
      而server端寫一個Thread監聽即可。
      有關每次傳送文字長度不同,可參考:
      http://study2fun.blogspot.tw/2013/03/java_8.html

      刪除
    2. 我想請問 一下大大喔~~
      要讓他在CLIENT端重覆這段該怎麼重覆阿?!是要加上WHILE迴圈嗎還是該怎麼做~~

      刪除
  9. Range Wang您好,請問一下,是不是要先開著serve端再開clienct才能正確工作啊

    回覆刪除
    回覆
    1. 是的,不然client端會發現無法連線而產生Exception

      刪除
    2. 謝謝! 我現在可以連線了,不過電腦可以傳值給手機,但是手機傳值給電腦,電腦卻沒有收到,手機上的傳值字串可以正常顯示,這樣是不是我的電腦在收值方面有問題呢?


      刪除
    3. 備註一下:防火牆有關,手機網路權現也有給了

      刪除
    4. 問題解決了! 後來發現只要將傳送字串改短就可以正常的接收了,因該是因為電腦收的速度太慢,手機發送以經結束,電腦卻還在收值的關係吧? 在試看看延長等待時間會不會解決這個問題。

      這裡再次謝謝你的分享!

      刪除
    5. 請問一下Range Wang如何在CLINE端加入網路權限
      我查了一下望路權限有多種要加入哪一種才能用真正的手機接收到訊息

      刪除
    6. 將 android.permission.INTERNET 加入即可!!
      除非你是將訊息存在SDcard,則才有可能需要存取SDcard的權限~

      刪除
    7. 再請教 Range Wang 大大

      我已經在client加入了 android.permission.INTERNET

      防火牆也關閉了

      可執行到手機還是不能動作

      p.s 我的aever有先開啟才執行client 可是一執行client就閃退

      刪除
    8. 如果client執行時,且尚未按下傳送時就閃退,可以先檢查layout本身是否正確,和連接java的findviewbyid是否都連正確的,如button的layout,卻連到Textview的java宣告,這是會直接閃退的~

      刪除
  10. 請問一下大大,
    要怎麼讓Server接收來自不同手機IP的訊息呢?要怎麼打?
    以及讓Server接收多次訊息?
    P.S.而不是手機每傳一次訊息,Server就要執行一次才收的到
    最後是如何讓兩端像聊天室互傳呢?雖然你上面有個小改,不過開起卻都是錯的

    回覆刪除
    回覆
    1. 上方的專案並不符合你的要求,你要是應該是在Server寫好Thread,開不同的 port來接收不同的來源。
      import專案時,每一台電腦的環境不同,需要做調整才行~

      刪除
  11. 請問Server和Client要互傳文字+英數,要如何顯示中英文呢?

    回覆刪除
    回覆
    1. 中文字要使用另外的編碼才可達成喔~

      刪除
  12. 請問server端的ip是否要固定ip才可以?

    回覆刪除
    回覆
    1. 目前是的,或者可用no-IP之類的服務來達成。

      刪除
  13. 請問Range Wang
    要如何在以上的server與client中互相傳送文字,但不限字數呢?
    目前client可以傳英文與中文給server,但是字數卻有限,英文約20個字左右,中文只能2個字
    要修改或加上哪裡才有辦法,每次傳送的文字長度不一,兩端卻又不會被截斷呢?

    回覆刪除
    回覆
    1. 可以參考這篇,做法是一樣的,都是屬於動態的長度在變動
      http://study2fun.blogspot.ca/2013/03/java_8.html

      刪除
  14. 你好 想請問一下~
    我的手機與電腦連接另外一支手機的wifi 是可以連線的
    如果連接wifi分享器的wifi 手機與電腦卻無法連線
    請問有甚麼原因會造成這樣的問題?!

    回覆刪除
  15. 請問大大是否有遇到這樣的問題

    我目前使用Java 與 Android行動裝置進行 Socket 通信
    剛開始運行前幾小時Wifi和3G移動網路都正常
    但幾小時候 如果使用3G傳輸網路Java,Android端就會出現底下的錯誤
    但是使用Wifi(同一個網域)傳輸卻沒問題

    後來我發現只需要把ServerSocket Port換掉就不會有錯誤
    但是過沒幾個小時也是一樣3G就會出現底下的錯誤
    Wifi同一個區網都沒問題

    防火牆關了、防毒軟體砍了 也都沒用

    很想知道答案困擾我很久了

    Java Serve端錯誤

    回覆刪除
    回覆
    1. 透過wifi時,請注意AP或Router會不會擋掉一些封包。而3G網路可能是因為會因為傳輸上的delay或掉封包而導致失敗。主要是這程式我沒有檢查是否有順利傳給對方而需不需要做重傳的動作。

      刪除
  16. 請問一下 JAVA 對C SERVER溝通 雙方的封包都訂好 可以直接用你JAVA的程式直接丟給C SERVER嗎?

    還有一個問題是 我想傳字串 跟 整數的資料 可以用什麼方法 和範例嗎?

    回覆刪除
    回覆
    1. 基本上是可以的,主要傳遞的是byte[]陣列

      刪除
  17. 請問大大 這篇的專案檔是否還有??

    回覆刪除

  18. 使用C# TcpClient 寫的Server可正常連線及接收資料,但是當C# Server 回拋資料卻無法正常接收(C# 的Client 全都正常),奇怪的是跑到 int FileSize = dIn.readInt(); 就會跳 Exception,不知道各位有沒有遇過類似的問題。程式是在模擬器上運作。

    mSocket = new Socket(SERVERIP, SERVERPORT);
    dOut = new DataOutputStream(mSocket.getOutputStream());
    dIn = new DataInputStream(mSocket.getInputStream());
    -----------------------------------------------------------------------------------------
    while (true) {
    int FileSize = dIn.readInt();
    ret = new byte[FileSize];
    if(FileSize>0) {
    dIn.read(ret, 0, FileSize);
    break;
    }
    }

    回覆刪除
    回覆
    1. 備註: 出現Exception的是 Android模擬環境。

      刪除
    2. 找到問題了 因為我把收資料寫在UI那層Thread,另外再加個Thread 就好 或在
      setContentView(R.layout.xxxx_main)下面加上

      if (android.os.Build.VERSION.SDK_INT > 9) {
      StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
      StrictMode.setThreadPolicy(policy);
      }

      刪除
  19. 請問一下 , 1.我打好了以上的Code 如果要放進手機裡要放哪一個?
    2.我打完用模擬機測試沒反應耶,可以把運作流程跟我說一下嗎?(初學者..抱歉

    回覆刪除
    回覆
    1. 1.client會產生apk檔,這個可以放到手機內
      2.要注意模擬器內部IP和外部IP是否有對到

      刪除
  20. 請問一下,client 跟 server 在不同的網域下 也可以傳送嗎?
    server有實體IP的情況下

    回覆刪除
    回覆
    1. 可以,只要中間經過的APor電腦的防火牆沒有擋就可以~

      刪除
  21. Error: Address already in use: JVM_Bind
    我的JAVA 顯示這樣,問題出在哪??

    回覆刪除
    回覆
    1. 每一次執行後server會不斷監聽port,若要再次執行時必須把上次的執行終止,釋放正在使用中的port,才能再次執行~

      刪除
  22. 請問大大,我用android 模擬器跑會出現Skipped 184 frames! The application may be doing too much work on its main thread.請問這要怎麼解決呢

    回覆刪除
  23. 請問要怎麼讓client一直保持連線呢?

    回覆刪除