樹莓派與Android客戶端進行socket通信

    首先,需要對樹莓派進行配置,使其成爲AP熱點,這裏我用的樹莓派3B自帶wifi藍牙模塊,樹莓派3B作AP熱點的方法具體參考https://blog.csdn.net/u014271612/article/details/53766627這篇文章,但配置過程中會遇到一些小問題,比如在輸入git clone https://github.com/oblique/create_ap  這條命令時會提示需要帳號密碼,而我的做法是直接上github將這個項目的zip壓縮文件下載下來拷貝到我的樹莓派中,然後再進行下面操作,後面還有一個問題 ,在sudo create_ap wlan0eth0 熱點名 密碼 這行密碼中wlan0eth0 應該在中間加一個空格,即sudo create_ap wlan0 eth0 熱點名 密碼 至此樹莓派作爲AP熱點配置成功。
    下面進入主題,樹莓派與android客戶端之間的通信我採用socket來實現,樹莓派上我寫了一個python腳本作爲服務器,android作爲客戶端

    首先,上一下效果圖:

    Android客戶端:



樹莓派服務器端:


    樹莓派上的python腳本如下:

import socket
import time
import sys

HOST_IP = "192.168.12.1"    #我的樹莓派作爲AP熱點的ip地址
HOST_PORT = 7654            #端口號

print("Starting socket: TCP...")
socket_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #創建socket

print("TCP server listen @ %s:%d!" %(HOST_IP, HOST_PORT) )
host_addr = (HOST_IP, HOST_PORT)
socket_tcp.bind(host_addr)    #綁定我的樹莓派的ip地址和端口號
socket_tcp.listen(1)	#listen函數的參數是監聽客戶端的個數,這裏只監聽一個,即只允許與一個客戶端創建連接

while True:
	print ('waiting for connection...')
	socket_con, (client_ip, client_port) = socket_tcp.accept()    #接收客戶端的請求
	print("Connection accepted from %s." %client_ip)

	socket_con.send("Welcome to RPi TCP server!")    #發送數據

	while True:
		data=socket_con.recv(1024)    #接收數據
		
		if data:    #如果數據不爲空,則打印數據,並將數據轉發給客戶端
			print(data)
			socket_con.send(data)

socket_tcp.close()

注:上述代碼註釋是後期加上去的,可以將代碼中的註釋去掉再運行。

    接下來是Android客戶端代碼:

    1、XML佈局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="lh.wifidemo.ui.activity.Device_Control_Activity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="設備控制"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/et_send"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送"
        android:id="@+id/bt_send"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="接收到的信息:"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tv_recv"/>
</LinearLayout>

    2、Activity.java

package lh.wifidemo.ui.activity;

import android.os.Handler;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import lh.wifidemo.R;

public class Device_Control_Activity extends ActionBarActivity {

    private EditText et_send;
    private Button bt_send;
    private TextView tv_recv;

    private String send_buff=null;
    private String recv_buff=null;

    private Handler handler = null;

    Socket socket = null;

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

        initView();

        handler = new Handler();

          //單開一個線程來進行socket通信
          new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                        socket = new Socket("192.168.12.1" , 7654);
                        if (socket!=null) {
                            System.out.println("###################");
                            while (true) {      //循環進行收發
                                recv();
                                send();
                            }
                        }
                       else
                            System.out.println("socket is null");
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }).start();
        send();
    }


    private void recv() {

        //單開一個線程循環接收來自服務器端的消息
        InputStream inputStream = null;
        try {
            inputStream = socket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (inputStream!=null){
            try {
                byte[] buffer = new byte[1024];
                int count = inputStream.read(buffer);//count是傳輸的字節數
                recv_buff = new String(buffer);//socket通信傳輸的是byte類型,需要轉爲String類型
                System.out.println(recv_buff);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //將受到的數據顯示在TextView上
        if (recv_buff!=null){
            handler.post(runnableUi);

        }
    }

    //不能在子線程中刷新UI,應爲textView是主線程建立的
    Runnable runnableUi = new Runnable() {
        @Override
        public void run() {
            tv_recv.append("\n"+recv_buff);
        }
    };

    private void send() {
        bt_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        send_buff = et_send.getText().toString();
                        //向服務器端發送消息
                        System.out.println("------------------------");
                        OutputStream outputStream=null;
                        try {
                            outputStream = socket.getOutputStream();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        if(outputStream!=null){
                            try {
                                outputStream.write(send_buff.getBytes());
                                System.out.println("1111111111111111111111");
                                outputStream.flush();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                    }
                }).start();

            }
        });
    }

    private void initView() {
        et_send = (EditText) findViewById(R.id.et_send);
        bt_send = (Button) findViewById(R.id.bt_send);
        tv_recv = (TextView) findViewById(R.id.tv_recv);
    }
}

這裏有幾個地方需要特別注意:

1、創建socket時需要開一個子線程,而不能直接在主線程中完成,否則會報錯

2、當接收來自樹莓派服務器的消息時,需要刷新TextView,而刷新TextView的操作不能直接在子線程中完成,需要用Handler來實現,否則會報錯,提示view只能由源線程來更改

整個項目需要先啓動樹莓派,然後手機連上樹莓派的WIFI熱點,如果想要查看樹莓派的輸出信息,則可以利用window的遠程桌面連接樹莓派,也可以用putty這個軟件進行命令行控制,當然,有條件的完全可以用HDMI接口給你的樹莓派連一個顯示器

        
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章