2017/02/06

[Android] UDP Socket Send&Receive with Thread

Android要寫UDP傳輸有點麻煩,因為它對於有關網路的行為有特別的規範,不能直接在MainActivity做任何網路行為,所以必須要另外建立方法來做。
有兩種方法可以寫,一種是透過Thread,另一種是透過AsyncTask(比較推薦),在這邊先用Thread的方法,建立一個可以傳送跟接收UDP Socket的Android App。


首先Android要寫網路有關的功能的話,App要先開權限!(開發的時候常常會忘記)
在AndroidManifest.xml裡面加入這兩行:

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

接著分別加入兩個class,分別來負責做傳送跟接收的工作
chatsender.java
  1. package com.example.chris.udp_sample;
  2. import java.io.IOException;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import java.net.InetAddress;
  6. import java.net.SocketException;
  7. import java.net.UnknownHostException;
  8. import android.util.Log;
  9.  
  10. class ChatSender extends Thread{
  11.  
  12. public void run(String strIP, String str, int port){
  13. DatagramSocket socket = null;
  14. try
  15. {
  16. socket = new DatagramSocket();
  17. InetAddress serverAddress = InetAddress.getByName(strIP);
  18. Log.d("IP Address", serverAddress.toString());
  19.  
  20. DatagramPacket packet;
  21.  
  22. //send socket
  23. packet=new DatagramPacket(str.getBytes(),str.length(),serverAddress,port);
  24. socket.send(packet);
  25.  
  26. }
  27. catch(SocketException e)
  28. {
  29. e.printStackTrace();
  30. String error = e.toString();
  31. Log.e("Error by Sender", error);
  32. }
  33. catch(UnknownHostException e)
  34. {
  35. e.printStackTrace();
  36. String error = e.toString();
  37. Log.e("Error by Sender", error);
  38. }
  39. catch(IOException e)
  40. {
  41. e.printStackTrace();
  42. String error = e.toString();
  43. Log.e("Error by Sender", error);
  44. }
  45. catch(Exception e)
  46. {
  47. e.printStackTrace();
  48. String error = e.toString();
  49. Log.e("Error by Sender", error);
  50. }
  51. finally{
  52. if(socket != null){
  53. socket.close();
  54. }
  55. }
  56. }
  57. }
chatserver.java
  1. package com.example.chris.udp_sample;
  2. import java.io.IOException;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import android.os.Bundle;
  6. import android.os.Message;
  7. import android.util.Log;
  8.  
  9. class ChatServer extends Thread{
  10.  
  11. private DatagramSocket server = null;
  12. private static final int PORT = 8008;
  13.  
  14. public ChatServer() throws IOException {
  15. server = new DatagramSocket(PORT);
  16. Log.d("User","new server socket");
  17. }
  18. public void run(){
  19.  
  20. byte[] byte1024 = new byte[1024];
  21. //Message msg = new Message();
  22. //Bundle data = new Bundle();
  23. DatagramPacket dPacket = new DatagramPacket(byte1024, 100);
  24. String txt;
  25. try{
  26. Log.d("User","runing run()");
  27. while(true){
  28. server.receive(dPacket);
  29. while(true)
  30. {
  31. txt = new String(byte1024, 0, dPacket.getLength());
  32. MainActivity.exHandler.sendMessage(MainActivity.exHandler.obtainMessage(1,txt));
  33. Log.d("User","Handler send Message");
  34. if(true) break;
  35. }
  36. //CloseSocket(client);
  37. }
  38. }
  39. catch(IOException e)
  40. {}
  41. }
  42.  
  43. private void CloseSocket(DatagramSocket socket) throws IOException{
  44. socket.close();
  45. }
  46. }

準備好這兩個class之後就可以在MainActivity裡面call來用了,如果要建立server,為了方便檢查自己的IP位址,在一開始有先加入顯示本身IP位址的程式碼。
在這個App中可以自己輸入要傳送的IP位址以及Socket字串內容。以下是MainActivity程式碼:

MainActivity.java
  1. package com.example.chris.udp_sample;
  2.  
  3. import java.io.IOException;
  4. import java.net.DatagramPacket;
  5. import java.net.DatagramSocket;
  6. import java.net.InetAddress;
  7. import java.net.NetworkInterface;
  8. import java.net.SocketException;
  9. import java.net.UnknownHostException;
  10. import java.util.Enumeration;
  11. import java.util.concurrent.atomic.AtomicBoolean;
  12.  
  13. import android.net.wifi.WifiInfo;
  14. import android.os.StrictMode;
  15. import android.support.v7.app.AppCompatActivity;
  16. import android.os.Bundle;
  17. import android.os.Handler;
  18. import android.os.Message;
  19. import android.text.format.Time;
  20. import android.util.Log;
  21. import android.view.View;
  22. import android.widget.Button;
  23. import android.widget.EditText;
  24. import android.widget.TextView;
  25.  
  26. public class MainActivity extends AppCompatActivity {
  27.  
  28. public static Handler exHandler;
  29. private ChatServer chatserver;
  30. private ChatSender chatsender;
  31. private TextView texv_ip, texv_recv, texv_send;
  32. private EditText edt_ip, edt_socket;
  33. private Button button01;
  34.  
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState) {
  37. super.onCreate(savedInstanceState);
  38. setContentView(R.layout.activity_main);
  39.  
  40. if (android.os.Build.VERSION.SDK_INT > 9) {
  41. StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
  42. StrictMode.setThreadPolicy(policy);
  43. }
  44.  
  45. texv_ip = (TextView) findViewById(R.id.textView);
  46. texv_recv = (TextView) findViewById(R.id.textView2);
  47. texv_send = (TextView) findViewById(R.id.textView5);
  48.  
  49. edt_ip = (EditText)findViewById(R.id.editText);
  50. edt_socket = (EditText)findViewById(R.id.editText2);
  51.  
  52. button01 = (Button)findViewById(R.id.btn_send1);
  53.  
  54. //receive text
  55. exHandler=new Handler() {
  56. @Override
  57. public void handleMessage(Message msg) {
  58. super.handleMessage(msg);
  59. String msgString = (String)msg.obj;
  60. Log.d("Handler","Now in Handler");
  61. texv_recv.setText(null);
  62. texv_recv.setText("Receive: " + msgString);
  63. }
  64. };
  65.  
  66. //get wifi IP
  67. StringBuilder IFCONFIG=new StringBuilder();
  68. try {
  69. for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
  70. NetworkInterface intf = en.nextElement();
  71. for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
  72. InetAddress inetAddress = enumIpAddr.nextElement();
  73. if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) {
  74. IFCONFIG.append(inetAddress.getHostAddress().toString()+"\n");
  75. }
  76. }
  77. }
  78. texv_ip.setText("My IP address is: " + IFCONFIG.toString());
  79. Log.d("LOG_Text", IFCONFIG.toString());
  80. } catch (SocketException ex) {
  81. Log.e("LOG_TAG", ex.toString());
  82. }
  83. try{
  84. chatsender = new ChatSender();
  85. chatserver = new ChatServer();
  86. chatserver.start();
  87. Log.d("User","Thread start...");
  88. }catch(Exception e)
  89. {
  90. String str = e.toString();
  91. Log.e("Error by User", str);
  92. }
  93. }
  94. protected void onStart(){
  95. super.onStart();
  96. button01.setOnClickListener(new Button.OnClickListener(){
  97. @Override
  98. public void onClick(View v) {
  99. chatsender.run(edt_ip.getText().toString(), edt_socket.getText().toString(), 8008);
  100. texv_send.setText("Send " + edt_socket.getText().toString() + " to " + edt_ip.getText().toString());
  101. }
  102. });
  103. }
  104. protected void onPause(){
  105. super.onPause();
  106. if(chatserver != null)
  107. {
  108. if(!chatserver.isInterrupted())
  109. {
  110. chatserver.interrupt();
  111. }
  112. }
  113. if(chatsender != null)
  114. {
  115. if(!chatsender.isInterrupted())
  116. {
  117. chatsender.interrupt();
  118. }
  119. }
  120. }
  121. protected void onResume() {
  122. super.onResume();
  123. }
  124. }

以下是layout的部分:
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.chris.udp_sample.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.33"
        android:text="My IP address"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="20dp" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.33"
        android:text="Receive: null"
        android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="20dp"/>

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.33"
        android:text="Send IP: "
        android:layout_below="@+id/textView2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="20dp" />

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.33"
        android:text="Send Socket: "
        android:layout_below="@+id/textView3"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="20dp"/>

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:text="172.20.10.2"
        android:ems="10"
        android:layout_alignBaseline="@+id/textView3"
        android:layout_alignBottom="@+id/textView3"
        android:layout_toRightOf="@+id/textView3"
        android:layout_toEndOf="@+id/textView3" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:text="test"
        android:ems="10"
        android:layout_alignBaseline="@+id/textView4"
        android:layout_alignBottom="@+id/textView4"
        android:layout_toRightOf="@+id/textView4"
        android:layout_toEndOf="@+id/textView4" />

    <Button
        android:id="@+id/btn_send1"
        android:text="Send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView4"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="20dp" />

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.33"
        android:text="show send socket"
        android:layout_below="@+id/btn_send1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="20dp"/>

</RelativeLayout>

這是用Thread的方法寫的,之後會再補一篇用AsyncTask的方法來做UDP傳輸的功能。




2 則留言:

  1. ChatServer.java中并没有start() function
    try{
    chatsender = new ChatSender();
    chatserver = new ChatServer();
    chatserver.start(); //------>>>>>>>>>>ChatServer.java中并没有start() function
    Log.d("User","Thread start...");
    }catch(Exception e)
    {
    String str = e.toString();
    Log.e("Error by User", str);
    }

    回覆刪除
    回覆
    1. 因為ChatServer是繼承Thread來用,start()是Thread裡面的function

      刪除