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
package com.example.chris.udp_sample;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import android.util.Log;

class ChatSender extends Thread{

    public void run(String strIP, String str, int port){
        DatagramSocket socket = null;
        try
        {
            socket = new DatagramSocket();
            InetAddress serverAddress = InetAddress.getByName(strIP);
            Log.d("IP Address", serverAddress.toString());

            DatagramPacket packet;

            //send socket
            packet=new DatagramPacket(str.getBytes(),str.length(),serverAddress,port);
            socket.send(packet);

        }
        catch(SocketException e)
        {
            e.printStackTrace();
            String error = e.toString();
            Log.e("Error by Sender", error);
        }
        catch(UnknownHostException e)
        {
            e.printStackTrace();
            String error = e.toString();
            Log.e("Error by Sender", error);
        }
        catch(IOException e)
        {
            e.printStackTrace();
            String error = e.toString();
            Log.e("Error by Sender", error);
        }
        catch(Exception e)
        {
            e.printStackTrace();
            String error = e.toString();
            Log.e("Error by Sender", error);
        }
        finally{
            if(socket != null){
                socket.close();
            }
        }
    }
}
chatserver.java
package com.example.chris.udp_sample;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;

class ChatServer extends Thread{

    private DatagramSocket server = null;
    private static final int PORT = 8008;

    public ChatServer() throws IOException {
        server = new DatagramSocket(PORT);
        Log.d("User","new server socket");
    }
    public void run(){

        byte[] byte1024 = new byte[1024];
        //Message msg = new Message();
        //Bundle data = new Bundle();
        DatagramPacket dPacket = new DatagramPacket(byte1024, 100);
        String txt;
        try{
            Log.d("User","runing run()");
            while(true){
                server.receive(dPacket);
                while(true)
                {
                    txt = new String(byte1024, 0, dPacket.getLength());
                    MainActivity.exHandler.sendMessage(MainActivity.exHandler.obtainMessage(1,txt));
                    Log.d("User","Handler send Message");
                    if(true) break;
                }
                //CloseSocket(client);
            }
        }
        catch(IOException e)
        {}
    }

    private void CloseSocket(DatagramSocket socket) throws IOException{
        socket.close();
    }
}

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

MainActivity.java
package com.example.chris.udp_sample;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicBoolean;

import android.net.wifi.WifiInfo;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Time;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    public static Handler exHandler;
    private ChatServer chatserver;
    private ChatSender chatsender;
    private TextView texv_ip, texv_recv, texv_send;
    private EditText edt_ip, edt_socket;
    private Button button01;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

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

        texv_ip = (TextView) findViewById(R.id.textView);
        texv_recv = (TextView) findViewById(R.id.textView2);
        texv_send = (TextView) findViewById(R.id.textView5);

        edt_ip = (EditText)findViewById(R.id.editText);
        edt_socket = (EditText)findViewById(R.id.editText2);

        button01 = (Button)findViewById(R.id.btn_send1);

        //receive text
        exHandler=new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String msgString = (String)msg.obj;
                Log.d("Handler","Now in Handler");
                texv_recv.setText(null);
                texv_recv.setText("Receive: " + msgString);
            }
        };

        //get wifi IP
        StringBuilder IFCONFIG=new StringBuilder();
        try {
            for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) {
                        IFCONFIG.append(inetAddress.getHostAddress().toString()+"\n");
                    }
                }
            }
            texv_ip.setText("My IP address is: " + IFCONFIG.toString());
            Log.d("LOG_Text", IFCONFIG.toString());
        } catch (SocketException ex) {
            Log.e("LOG_TAG", ex.toString());
        }

        try{
            chatsender = new ChatSender();
            chatserver = new ChatServer();
            chatserver.start();
            Log.d("User","Thread start...");

        }catch(Exception e)
        {
            String str = e.toString();
            Log.e("Error by User", str);
        }

    }

    protected void onStart(){
        super.onStart();

        button01.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View v) {
                chatsender.run(edt_ip.getText().toString(), edt_socket.getText().toString(), 8008);
                texv_send.setText("Send " + edt_socket.getText().toString() + " to " + edt_ip.getText().toString());
            }
        });

    }

    protected void onPause(){
        super.onPause();
        if(chatserver != null)
        {
            if(!chatserver.isInterrupted())
            {
                chatserver.interrupt();
            }
        }

        if(chatsender != null)
        {
            if(!chatsender.isInterrupted())
            {
                chatsender.interrupt();
            }
        }
    }

    protected void onResume() {
        super.onResume();
    }
}

以下是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

      刪除