基于 Socket 的 Android与树莓派通信

个人博客地址:http://icharles.top/2018/04/29/Socket/

一、Socket定义

  • 即套接字,是一个对 TCP / IP协议进行封装 的编程调用接口(API)

    1.即通过Socket,我们才能在Andorid平台上通过 TCP/IP协议进行开发
    2.Socket不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)
    
  • 成对出现,一对套接字:
    Socket ={(IP地址1:PORT端口号),(IP地址2:PORT端口号)}

二、Socket基本通信模型

Socket的使用类型主要有两种:

流套接字(streamsocket) :基于 TCP协议,采用 流的方式 提供可靠的字节流服务
数据报套接字(datagramsocket):基于 UDP协议,采用 数据报文 提供数据打包发送的服务

具体原理图如下:

三、使用基于TCP协议的Socket

    Socket通信主要分为服务端和客服端   

一个客户端要发起一次通信,首先必须知道运行服务器端的主机IP地址。然后由网络基础设施利用目标地址,将客服端发送的信息传递到正确的主机上,在Java中,地址可以由一个字符串来定义,这个字符串可以使用数字型的地址(比如192.168.1.1),也可以是主机名(example.com)。在Java当中InetAddress类代表了一个网络目标地址,包括主机名和数字类型的地址信息。
基于TCP协议操作Socket的API

服务端

1)、创建ServerSocket的方法

ServerSocket(int localPort);
ServerSocket(int localPort, int queueLimit);
ServerSocket(int localPort, int queueLimit, InetAddress localAddr);

创建一个ServerSocket必须指定一个端口,以便客户端能够向端口号发送连接请求。端口的有效范围是0~65535
2)、ServerSocket操作
Socket accept()——此方法为下一个传入的连接请求创建Socket实例,并将已经成功连接的Socket实例返回给服务器套接字,如果没有连接请求,accept()方法将阻塞等待
void close()——此方法用于关闭套接字

客户端

1)、创建Socket的方法
Socket(InetAddress remoteAddress, int remotePort);
利用Socket的构造函数,可以创建一个TCP套接字后,先连接到指定的远程地址和端口号
2)、操作Socket的方法
InputStream getInputStream();
OutputStream getOutputStream();
void close();

四、使用UDP的Socket

1)、创建DatagramPacket
DatagramSocket(byte[] data, int offset, int length, InetAddress remoteAddr, int remotePort);
该构造函数创建一个数据报文对象,数据包含在第一个参数中
2)、创建DatagramSocket
DatagramSocket(int localPort);
该构造函数将创建一个UDP套接字
3)、DatagramSocket:发送和接收
void send(DatagramPacket packet);——此方法用来发送DatagramPacket实例,一旦创建连接,数据报将发送到该套接字所连接的地址
void receive(DatagramPacket packet);——此方法将阻塞等待,直到接收到数据报文,并将报文中的数据复制到指定的DatagramPacket实例中

五、操作总结

服务器端编程步骤:
1、创建服务器端套接字并绑定到一个端口上
2、套接字设置监听模式等待连接请求
3、接收连接请求后进行通信
4、返回,等待另一个连接请求

客户端编程步骤:
1、创建客户端套接字(指定服务器端的IP地址和端口号)
2、连接(Android创建Socket时会自动连接)
3、与服务器端进行通信
4、关闭套接字

Android Socket通信原理,注意地方:
1、中间的管道连接是通过InputStream/OutputStream流实现的
2、一旦管理建立起来可以进行通信
3、关闭管道的同时意味着关闭Socket
4、当对同一个Socket创建重复管道时会异常
5、通信过程中顺序很重要:服务器端首先得到输入流,然后将输入流信息输出到其各个 客户端;客户端先建立连接后先写入输出流,然后再获得输入流,不然会有EOFException的异常。

六、具体实例

步骤1:添加网络权限

AndroidManifest.xml


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.robot.charles.hexapod">

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

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

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

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

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>
    application>

manifest>

步骤2:布局

activity_main.xml


<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"
    tools:context=".MainActivity"
    android:background="@color/colorBackground"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/tv_hexapod"
            style="@style/Text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:gravity="center_horizontal"
            android:text="Hexapod Controler" />
    LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1">
            <EditText
                android:id="@+id/et_input"
                android:layout_width="180dp"
                android:layout_height="40dp"
                android:layout_marginLeft="18dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorBlack"
                android:hint="Input IP"
                android:text="172.26.34.58:6666"/>
        LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginTop="4dp">
            <Button
                android:id="@+id/btn_start"
                android:layout_width="66dp"
                android:layout_height="33dp"
                android:layout_marginLeft="80dp"
                android:background="@android:color/holo_purple"
                android:text="START" />

            <Button
                android:id="@+id/btn_end"
                android:layout_width="66dp"
                android:layout_height="33dp"
                android:layout_marginLeft="20dp"
                android:background="@android:color/holo_purple"
                android:text="END" />
        LinearLayout>
    LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:orientation="horizontal"
        android:layout_marginTop="10dp">
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="center_horizontal">
                <Button
                    android:id="@+id/btn_up"
                    style="@style/BtnStyleLeft"
                    android:background="@color/colorBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:text="↑" />
            LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="center_horizontal">
                <Button
                    android:id="@+id/btn_left"
                    style="@style/BtnStyleLeft"
                    android:background="@color/colorBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:text="←" />
                <Button
                    android:id="@+id/btn_right"
                    style="@style/BtnStyleLeft"
                    android:background="@color/colorBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="90dp"
                    android:layout_gravity="center_vertical"
                    android:text="→" />
            LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="center_horizontal">
                <Button
                    android:id="@+id/btn_down"
                    style="@style/BtnStyleLeft"
                    android:background="@color/colorBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:text="↓" />
            LinearLayout>
        LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="center_horizontal">
                <Button
                    android:id="@+id/btn_y"
                    style="@style/BtnStyleRight"
                    android:layout_marginTop="18dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@android:color/holo_orange_dark"
                    android:text="Y" />
                <Button
                    android:id="@+id/btn_x"
                    style="@style/BtnStyleRight"
                    android:layout_marginTop="18dp"
                    android:layout_marginLeft="32dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@android:color/holo_blue_dark"
                    android:text="X" />
            LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="center_horizontal">
                <Button
                    android:id="@+id/btn_b"
                    style="@style/BtnStyleRight"
                    android:layout_marginTop="4dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@android:color/holo_red_light"
                    android:text="B" />
                <Button
                    android:id="@+id/btn_a"
                    style="@style/BtnStyleRight"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="32dp"
                    android:layout_marginTop="4dp"
                    android:background="@android:color/holo_green_dark"
                    android:text="A" />
            LinearLayout>
        LinearLayout>
    LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/tv_server"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="By Charles"
            android:textSize="15dp"
            android:layout_marginTop="0dp"
            android:gravity="center_horizontal"/>
    LinearLayout>

LinearLayout>

style.xml

<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        -- #region /设置全屏属性-->
        <item name="android:windowFullscreen">trueitem>

        <item name="colorPrimary">@color/colorPrimary
        "colorPrimaryDark">@color/colorPrimaryDark
        "colorAccent">@color/colorAccent
    style>
    <style name="BtnStyleLeft">
        <item name="android:textColor">@color/colorWhite
        "android:textSize">12pt
        "android:height">65dp
        "android:width">35dp
    style>
    <style name="BtnStyleRight">
        <item name="android:textColor">@color/colorBlack
        "android:textSize">12pt
        "android:height">65dp
        "android:width">55dp
    style>
    <style name="BtnStyle">
        <item name="android:textColor">@color/colorBlack
        "android:textSize">18pt
        "android:height">66dp
        "android:width">56dp
    style>
    <style name="SmallBtn">
        <item name="android:textColor">@color/colorBlack
        "android:textSize">18pt
        "android:height">15dp
        "android:width">15dp
    style>
    <style name="Text">
        <item name="android:textSize">9ptitem>
        <item name="android:width">200dpitem>
        <item name="android:height">30dpitem>
    style>

resources>

colors.xml


<resources>
    <color name="colorPrimary">#3F51B5color>
    <color name="colorPrimaryDark">#303F9Fcolor>
    <color name="colorAccent">#FF4081color>
    <color name="colorBtn">#757575color>
    <color name="colorBackground1">#A4A4A4color>
    <color name="colorBackground">#D8D8D8color>
    <color name="colorBlack">#000000color>
    <color name="colorWhite">#FFFFFFcolor>
resources>

步骤3:Java源代码

MainActivity.java

package com.robot.charles.hexapod;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;
import android.os.Message;
import android.view.View;
import android.os.Handler;
import android.widget.Button;
import android.widget.Toast;
import android.widget.TextView;
import java.util.StringTokenizer;

public class MainActivity extends AppCompatActivity {

    //控件变量
    TextView tvServer;
    EditText etInput;
    Button   btnStart;
    Button   btnEnd;
    Button   btnUp;
    Button   btnDown;
    Button   btnLeft;
    Button   btnRight;
    Button   btnX;
    Button   btnY;
    Button   btnA;
    Button   btnB;
    ClientThread clientThread;

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

        //隐藏虚拟键
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav
                        // bar
                        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | View.SYSTEM_UI_FLAG_IMMERSIVE);
    }

    //初始化控件
    private void findView() {
        tvServer  = (TextView) this.findViewById(R. id. tv_server);
        etInput   = (EditText) this.findViewById(R. id. et_input);
        btnStart  = (Button) this.findViewById(R. id. btn_start);
        btnEnd    = (Button) this.findViewById(R. id. btn_end);
        btnUp     = (Button) this.findViewById(R. id. btn_up);
        btnDown   = (Button) this.findViewById(R. id. btn_down);
        btnLeft   = (Button) this.findViewById(R. id. btn_left);
        btnRight  = (Button) this.findViewById(R. id. btn_right);
        btnX      = (Button) this.findViewById(R. id. btn_x);
        btnY      = (Button) this.findViewById(R. id. btn_y);
        btnA      = (Button) this.findViewById(R. id. btn_a);
        btnB      = (Button) this.findViewById(R. id. btn_b);
    }

    //连接server
    Handler handler = new Handler() //①
    {
        public void handleMessage(Message msg)
        {
            // 如果消息来自于子线程
            // 将读取的内容追加显示在文本框中
            //tvServer.append(msg.obj.toString()+'\n');
        }
    };

    //监听器
    private void setListener() {
        btnStart.setOnClickListener(new View.OnClickListener()
        {
            public void onClick(View v)
            {

                if(etInput.getText().toString().equals(""))
                {
                    Toast.makeText(MainActivity.this,"请先设置IP地址以及端口号 格式为  IP:PORT",Toast.LENGTH_SHORT).show();
                }
                else
                {
                    String msg_all=null;
                    String msg_ip=null;
                    String msg_port_string=null;
                    int msg_port_int=0;
                    try {
                        msg_all=etInput.getText().toString();
                        StringTokenizer msg_socket = new StringTokenizer(msg_all,":");
                        msg_ip=msg_socket.nextToken();
                        msg_port_string=msg_socket.nextToken();
                        msg_port_int=Integer.parseInt(msg_port_string);
                        Toast.makeText(MainActivity.this,"IP:"+msg_ip+"  端口:"+msg_port_int,Toast.LENGTH_SHORT).show();
                    } catch (Exception e1) {}
                    clientThread = new ClientThread(handler,msg_ip,msg_port_int);
                    // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
                    new Thread(clientThread).start();
                }//①
            }
        });
        btnEnd.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "exit";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                    Toast.makeText(MainActivity.this,"与机器人连接断开", Toast.LENGTH_SHORT).show();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnUp.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "up";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnDown.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "down";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnLeft.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "left";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnRight.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "right";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnA.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "A";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnB.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "B";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnX.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "X";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        btnY.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try
                {
                    Message msg = new Message();
                    msg.obj = "Y";
                    clientThread.revHandler.sendMessage(msg);
                    //input_msg.setText("");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
    }
}

ClientThread.java

package com.robot.charles.hexapod;

import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class ClientThread implements Runnable
{
    private Socket s;
    // 定义向UI线程发送消息的Handler对象
    private Handler handler;
    // 定义接收UI线程的消息的Handler对象
    public Handler revHandler;
    // 该线程所处理的Socket所对应的输入流
    BufferedReader br = null;
    OutputStream os = null;
    private String ip = null;
    private int port = 0;

    public ClientThread(Handler handler,String ip,int port)
    {
        this.handler = handler;
        this.ip=ip;
        this.port=port;
    }
    public void run()
    {
        try
        {
            s = new Socket(ip,port);
            br = new BufferedReader(new InputStreamReader(
                    s.getInputStream()));
            os = s.getOutputStream();
            new Thread()
            {
                @Override
                public void run()
                {
                    String content = null;
                    // 不断读取Socket输入流中的内容。
                    try
                    {
                        while ((content = br.readLine()) != null)
                        {
                            // 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
                            Message msg = new Message();
                            msg.obj = content;
                            handler.sendMessage(msg);
                            //System.out.println(msg.obj.toString());
                        }
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }.start();
            // 为当前线程初始化Looper
            Looper.prepare();
            // 创建revHandler对象
            revHandler = new Handler()
            {
                @SuppressLint("HandlerLeak")
                @Override
                public void handleMessage(Message msg)
                {
                    // 接收到UI线程中用户输入的数据
                    // 将用户在文本框内输入的内容写入网络
                    try
                    {
                        System.out.println("Hello "+msg.obj.toString());
                        os.write((msg.obj.toString())
                                .getBytes("utf-8"));
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            };
            // 启动Looper
            Looper.loop();
        }
        catch (SocketTimeoutException e1)
        {
            System.out.println("网络连接超时!!");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

七、测试

基于 Socket 的 Android与树莓派通信_第1张图片

项目源码:https://github.com/hhuaibo/socket

推荐:
https://blog.csdn.net/carson_ho/article/details/53366856
https://blog.csdn.net/thanksgining/article/details/43561053

你可能感兴趣的:(基于 Socket 的 Android与树莓派通信)