基于Android语言的通信调试助手实现(TCP协议+Socket编程)

在Android开发中经常会遇到Android通信的问题,比如物联网开发,实现Android上位机和下位机的通信,但是在没有硬件设备的配合之下,又不知道此次开发是否正确,发送数据是否达到要求。于是,此篇文章就是讲述Android通信调试助手的开发,方便Android通信的验证和数据对比,就是利用Android开发一个数据端和Windows数据端进行通信。效果如下图。

 

Windows端是NetAssist助手,后面我会给出下载地址。Android端就是今天的重点。

接下来就是讲述Android端的开发原理和过程:

1. 原理

TCP协议,即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP旨在适应支持多网络应用的分层协议层次结构。连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。因此,可以理解TCP提供一种面向连接的、可靠的字节流服务,面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。

Socket编程,即套接字编程,就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。通过IP+端口号的形式可以唯一确定一个进程,利用套接字编程就可以访问目的主机的某一个特定进程,在本实验中,就是利用Android语言实现套接字网络编程,实现手机客户端对电脑客户端的访问,然后电脑客户端和手机客户端实现双向通信,进而实现简单的通信调试程序。

Android客户端,手机客户端是基于Android语言进行编写的。首先,程序的编译环境是在Android studio上进行的,先创建几个活动,用以承载主要的通信程序的运行,然后创建几个XML文件,用以承载客户端界面的运行,通过创建发送线程类来实现套接字的运行,进而实现C/S之间的数据通信。

2. 开发内容分析

(1)首先,明确软件内容和结构,整个实验主要分为Android客户端和windows客户端。其中windows客户端是现成的调试助手,本不是本次实验的重点,所以本次实验主要介绍Android客户端的实现和编写。本次实验的Android客户端主要分为三个模块,即连接登录模块、数据收发模块、主界面模块。其中连接登录模块主要包括IP和端口号,通过用户手动输入IP和端口号对特定的服务端进行连接;数据收发模块主要是实现Android客户端和Windows客户端的通信过程,即通过socket编程利用IP和端口建立Android客户端和Windows客户端的无线连接,并对收发数据信息通过WiFi进行发送和接收;主界面模块主要是Android客户端的数据接收和数据发送的显示和输入界面,主要包括数据输入框和发送按钮,以及数据接收框用来接收windows客户端发送的数据。具体的系统的整体的架构如图1所示。

由图的结构可以看出,本次实验的Android客户端主要需要:

(1)连接模块包括IP输入框、port输入框、登录连接button;

(2)数据收发界面包括发送数据输入框、数据发送button、数据接收显示框;

(3)连接线程包括数据发送方法、数据接收解析方法;

(4)同时需要保证Android客户端和Windows客户端处于同一个网络之中。

(2)根据步骤(1)对系统架构的分析,利用Android语言的socket编程编写发送接收程序、利用活动编写界面显示程序、利用xml编写UI界面,从而实现一个模拟C/S通信的Android客户端程序。由于本次实验是基于TCP协议实现C/S双方的通信的,因此需要对TCP的通信过程有一定的了解,TCP的3次握手过程如图2所示。

3. Android客户端代码编写

首先,创建一个Android项目,需要给予APP网络权限:

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

第二步,创建一个类SendThead,内容为发送和接收的代码,首先定义IP,端口port,以及接收/发送’变量,打开套接字实现网络的连接,连接结束关闭套接字,采取多线程实现信息的发送与接收:

package com.example.a14942.smart_see;

/**
 * Created by 14942 on 2018/3/26.
 */

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

class SendThread implements Runnable {

    private String ip;
    private String port;
    BufferedReader in;
    PrintWriter out;
    Handler mainHandler;
    Socket s;
    private String receiveMsg;

    ArrayList<String> list = new ArrayList<String>();

    public SendThread(String ip,String port, Handler mainHandler) {
        this.ip = ip;
        this.port=port;
        this.mainHandler = mainHandler;
    }

    /**
     * 套接字的打开
     */
    void open(){
        try {
            s = new Socket(ip, Integer.parseInt(port));
            //in收单片机发的数据
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                    s.getOutputStream())), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 套接字的关闭
     */
    void close(){
        try {
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        //创建套接字
        open();

        //BufferedReader
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(200);
                        close();
                        open();
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    if (!s.isClosed()) {
                        if (s.isConnected()) {
                            if (!s.isInputShutdown()) {
                                try {
                                    Log.i("mr", "等待接收信息");

                                    char[] chars = new char[1024];
                                    int len = 0;
                                    while((len = in.read(chars)) != -1){
                                        System.out.println("收到的消息:  "+new String(chars, 0, len));
                                        receiveMsg = new String(chars, 0, len);

                                        Message msg=mainHandler.obtainMessage();
                                        msg.what=0x00;
                                        msg.obj=receiveMsg;
                                        mainHandler.sendMessage(msg);
                                    }

                                } catch (IOException e) {
                                    Log.i("mr", e.getMessage());
                                    try {
                                        s.shutdownInput();
                                        s.shutdownOutput();
                                        s.close();
                                    } catch (IOException e1) {
                                        e1.printStackTrace();
                                    }
                                    e.printStackTrace();
                                }
                            }
                        }
                    }

                }
            }

        });
        thread.start();

        while (true) {

            //连接中
            if (!s.isClosed()&&s.isConnected()&&!s.isInputShutdown()) {

                // 如果消息集合有东西,并且发送线程在工作。
                if (list.size() > 0 && !s.isOutputShutdown()) {
                    out.println(list.get(0));
                    list.remove(0);
                }

                Message msg=mainHandler.obtainMessage();
                msg.what=0x01;
                mainHandler.sendMessage(msg);
            } else {
                //连接中断了
                Log.i("mr", "连接断开了");
                Message msg=mainHandler.obtainMessage();
                msg.what=0x02;
                mainHandler.sendMessage(msg);
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                try {
                    out.close();
                    in.close();
                    s.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }
        }

    }

    public void send(String msg) {
        System.out.println("msg的值为:  " + msg);
        list.add(msg);
    }

}

第三步,创建主活动,编写布局(xml)文件,这里我创建了一个四个发送数据框,和一个接收数据框:

<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/screen"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/jj"
    >

    <EditText
        android:id="@+id/edit1"
        android:layout_width="180sp"
        android:layout_height="50sp"
        android:layout_x="37dp"
        android:layout_y="40dp"
        android:background="#ffffff"
        android:maxLines="2" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="50sp"
        android:layout_x="240dp"
        android:layout_y="40dp"
        android:text="send1" />

    <EditText
        android:id="@+id/edit2"
        android:layout_width="180sp"
        android:layout_height="50sp"
        android:layout_x="37dp"
        android:layout_y="110dp"
        android:background="#ffffff"
        android:maxLines="2" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="50sp"
        android:layout_x="240dp"
        android:layout_y="110dp"
        android:text="send2" />

    <EditText
        android:id="@+id/edit3"
        android:layout_width="180sp"
        android:layout_height="50sp"
        android:layout_x="37dp"
        android:layout_y="180dp"
        android:background="#ffffff"
        android:maxLines="2" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="50sp"
        android:layout_x="240dp"
        android:layout_y="180dp"
        android:text="send3" />

    <EditText
        android:id="@+id/edit4"
        android:layout_width="180sp"
        android:layout_height="50sp"
        android:layout_x="37dp"
        android:layout_y="250dp"
        android:background="#ffffff"
        android:maxLines="2" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="50sp"
        android:layout_x="240dp"
        android:layout_y="250dp"
        android:text="send4" />


        <TextView
            android:id="@+id/ttv2"
            android:layout_width="286dp"
            android:layout_height="77dp"
            android:background="#FFFFFF"
            android:layout_x="37dp"
            android:layout_y="320dp"
            android:text=""
            android:textSize="18sp" />

</AbsoluteLayout>

 第四步,编写主活动,包括实现网络连接(SendThead),实现消息的发送和接收,实现点击事件等:

package com.example.a14942.smart_see;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity{

    private String a;
    private String b;
    /*接收发送定义的常量*/
    private SendThread sendthread;
    String receive_Msg;
    String l;
    private Button button1;
    private Button button2;
    private Button button3;
    private Button button4;

    private EditText editText1;
    private EditText editText2;
    private EditText editText3;
    private EditText editText4;
    /*****************************/


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //getSupportActionBar().hide();
        setContentView(R.layout.activity_main);
        Intent intent = getIntent();
         a = intent.getStringExtra("acc");
         b = intent.getStringExtra("pwd");
        /***************连接*****************/
        sendthread = new SendThread(a, b, mHandler);
        Thread1();
        new Thread().start();
        /**********************************/

        editText1 = (EditText) findViewById(R.id.edit1);
        editText2 = (EditText) findViewById(R.id.edit2);
        editText3 = (EditText) findViewById(R.id.edit3);
        editText4 = (EditText) findViewById(R.id.edit4);

        button1 = (Button) findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                String a1 = editText1.getText().toString();
                sendthread.send(a1);
            }

        });

        button2 = (Button) findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                String a2 = editText2.getText().toString();
                sendthread.send(a2);
            }

        });

        button3 = (Button) findViewById(R.id.button3);
        button3.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                String a3 = editText3.getText().toString();
                sendthread.send(a3);
            }

        });

        button4 = (Button) findViewById(R.id.button4);
        button4.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                String a4 = editText4.getText().toString();
                sendthread.send(a4);
            }

        });

    }

    private class FragmentAdapter extends FragmentPagerAdapter {
        List<Fragment> fragmentList = new ArrayList<Fragment>();

        public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
            super(fm);
            this.fragmentList = fragmentList;
        }

        @Override
        public Fragment getItem(int position) {
            return fragmentList.get(position);
        }

        @Override
        public int getCount() {
            return fragmentList.size();
        }

    }


    /*接收线程*******************************************************************************/
    /**
     * 开启socket连接线程
     */
    void Thread1(){
//        sendthread = new SendThread(mIp, mPort, mHandler);
        new Thread(sendthread).start();//创建一个新线程
    }

    Handler mHandler = new Handler()
    {
        public void handleMessage(Message msg)
        {
            TextView text0 = (TextView)findViewById(R.id.ttv2);
            super.handleMessage(msg);
            if (msg.what == 0x00) {

                Log.i("mr_收到的数据: ", msg.obj.toString());
                receive_Msg = msg.obj.toString();
                l = receive_Msg;
                text0.setText(l);
            }
        }
    };
}

第五步,创建LoginloginActivity,将其作为类似于登录界面的样式,即需要在Android端和Windows端通信时,需要知道Windows端的IP和端口号,此时的IP和端口号就是在这输入,界面效果如下图。

 

 

界面XML的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/jj"
    android:orientation="vertical">


    <ImageView
        android:layout_width="132dp"
        android:layout_height="132dp"
        android:layout_gravity="center"
        android:layout_marginTop="68dp"
        android:src="@drawable/icon_avatar_login" />

    <LinearLayout
        android:id="@+id/login_linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="8dp"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <LinearLayout
                android:id="@+id/userId_LinearLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="right"
                android:orientation="horizontal"
                >

                <!-- android:ems="10"表示EditText内最多只显示10个字符,超出不显示 -->
                <!-- android:singleLine="true"表示不能全部显示时,后面用“…”来表示 -->
                <EditText
                    android:id="@+id/account"
                    android:layout_width="0dp"
                    android:layout_height="44dp"
                    android:layout_weight="1"
                    android:background="@null"
                    android:ems="10"
                    android:hint="@string/idtxthint"
                    android:paddingLeft="15dp"
                    android:paddingRight="15dp"
                    android:singleLine="true"
                    android:textColorHint="#999999"
                    android:textSize="18sp" >

                    <requestFocus />
                </EditText>

            </LinearLayout>

            <!-- 横线  -->
            <View
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:background="#CACDD1" />

            <EditText
                android:id="@+id/password"
                android:layout_width="match_parent"
                android:layout_height="44dp"
                android:background="@null"
                android:hint="@string/pwdtxthint"
                android:paddingLeft="15dp"
                android:paddingRight="0dp"
                android:singleLine="true"
                android:textColorHint="#999999"
                android:textSize="18sp" />

        </LinearLayout>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <CheckBox
                android:id="@+id/remember_pass"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="15sp"
                android:text="记住服务器"
                />

        </LinearLayout>

        <Button
            android:id="@+id/login"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:layout_marginTop="20dp"
            android:background="#6699ff"
            android:text="@string/loginbtntext"
            android:textColor="#FFFFFF"
            android:textSize="19sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical" />

</LinearLayout>

第六步,在LoginloginActivity中实现各种事件操作。

package com.example.a14942.smart_see;

        import android.content.Intent;
        import android.content.SharedPreferences;
        import android.os.Bundle;
        import android.preference.PreferenceManager;
        import android.view.View;
        import android.widget.Button;
        import android.widget.CheckBox;
        import android.widget.EditText;
        import android.widget.Toast;

public class LoginloginActivity extends BaseActivity {

    private SharedPreferences pref;

    private SharedPreferences.Editor editor;

    private EditText accountEdit;

    private EditText passwordEdit;

    private Button login;

    private CheckBox rememberPass;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        Toast.makeText(this,"请确保网络已连接", Toast.LENGTH_SHORT).show();

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loginlogin);
        pref = PreferenceManager.getDefaultSharedPreferences(this);
        accountEdit = (EditText) findViewById(R.id.account);
        passwordEdit = (EditText) findViewById(R.id.password);
        rememberPass = (CheckBox) findViewById(R.id.remember_pass);
        login = (Button) findViewById(R.id.login);
        boolean isRemember = pref.getBoolean("remember_password",false);
        if (isRemember){
            String account = pref.getString("account","");
            String password = pref.getString("password","");
            accountEdit.setText(account);
            passwordEdit.setText(password);
            rememberPass.setChecked(true);
        }
        login.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                String account = accountEdit.getText().toString();
                String password = passwordEdit.getText().toString();
                if (password == null || account.equals("")){
                    Toast.makeText(LoginloginActivity.this,"请输入服务器IP",Toast.LENGTH_SHORT).show();
                } else  if (account == null || password.equals("")){
                    Toast.makeText(LoginloginActivity.this,"请输入端口号",Toast.LENGTH_SHORT).show();
                } else {
                    editor = pref.edit();
                    if (rememberPass.isChecked()) {
                        editor.putBoolean("remember_password",true);
                        editor.putString("account",account);
                        editor.putString("password",password);
                    } else {
                        editor.clear();
                    }
                    editor.apply();
                    Intent intent = new Intent(LoginloginActivity.this,MainActivity.class);
                    intent.putExtra("acc", account);
                    intent.putExtra("pwd", password);
                    startActivity(intent);
                    finish();
                }
            }
        });
    }
}

最后,需要修改AndroidManifest.xml中的活动执行顺序,即先执行LoginloginActivity,再执行MainActivity

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.a14942.smart_see">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/tt"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">

        </activity>
        <activity android:name=".LoginloginActivity">
            <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

</manifest>

至此,整个Android编写就完成了,下面介绍一下如何使用。

此次实验用到的程序共分为windows客户端和Android客户端两部分,而在本次实验中,我主要对Android客户端进行编写。具体测试步骤如下。

1Windows客户端作为TCP服务端,Android客户端作为TCP客户端,将windows系统和Android手机连接到同一个局域网下,即本次实验中,是手机给电脑开热点,使其二者在同一个局域网下的。

2)打开windows客户端,并选择协议类型为TCP Server,同时设置IP地址为192.168.27.1,设置端口号为8564,设置完成后就点击开启,这样Windows客户端作为一个TCP服务端就设置完成了。其中Windows客户端右边上部分的空白区域作为服务端接收数据的显示区域,可以显示连接情况,也可以显示由客户端发送过来的数据,右边下部分的空白区域是服务端给客户端发送数据的输入框,输入想要发送的数据后,点击发送按钮就可以将数据发送到Android客户端上。Windows服务端具体如图6所示。

(3)接下来打开安装在手机上的Android客户端,首先需要在服务器IP一栏填写TCP serverIP地址,即192.168.247.1,然后在端口一栏输入服务端的端口,即8564,填写完成后,就进入了Android客户端的主界面上,主界面前四栏可以输入任意信息,点击发送后就可以在Windows客户端上显示出来。具体操作如图7所示。

4)接下来,开始进行Android客户端和Windows服务端的通信,我在Android客户端的发送数据输入框中,分别输入“hello, my name is one”、“hello, my name is two”、“hello, my name is three”、“hello, my name is four”,然后分别点击发送按钮后,可以看到Windows服务端界面分别显示出发送的数据,如图8所示。此外,也可以在服务端发送信息给Android客户端,在Windows服务端的输入框输入“hello, I am Yang Jianlin”,点击发送后,在Android客户端会显示出来,具体如图9所示。

免费完整demo下载网址:https://download.csdn.net/download/weixin_40042248/13456038

Windows端NetAssist助手下载地址:NetAssist网络调试助手4.3.21_netassist下载-系统集成工具类资源-CSDN下载

以上就是全部介绍,水平有限,描述如有不清请留言。

 

 

  • 10
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楊木木8023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值