android tcp tcp阻塞 线程不运行,物联网综合练习之android TCP客户端

/*************************************************************************************************

PROJECT:        wifi遥控小车

HARDWARE:    STC89C52RC单机, L239D直流电机驱动器, ESP8266安信可wifi模块

SOFTWARE:    Eclipse安卓开发环境

AUTHOR:          DDDDD

DATE:              2014-12-5

*************************************************************************************************/

e2a66640c7d6b5e51ca6489e2f49c170.png

这个小项目有软件和硬件两大块,这里主要阐述在开发android客户端中遇到的问题

1、需要几个普通的Button,控制前进后退等。

findViewById()这个函数将定义的button变量和xml文件中的button控件绑定起来

upBtn = (Button)findViewById(R.id.upbtn);

setOnClickListener()为button控件添加监听器,这样button被按下后监听器就会执行里面的函数

downBtn.setOnClickListener(new runButtonListenner());

如果为每一个button都绑定一个监听器,程序很不友好。最常用的方法就是为所有同种类型的button设置同一个监听器。

upBtn.setOnClickListener(new runButtonListenner());

downBtn.setOnClickListener(new runButtonListenner());

leftBtn.setOnClickListener(new runButtonListenner());

rightBtn.setOnClickListener(new runButtonListenner());

stopBtn.setOnClickListener(new runButtonListenner());

在监听器里,利用switch语句来判断是哪个button被按下,onclick函数喊传入参数v,通过调用参数v的getId方法获取按钮

class runButtonListenner implements OnClickListener

{

@Override

public void onClick(View v)

{

// TODO Auto-generated method stub

switch(v.getId())

{

case R.id.upbtn:

break;

case R.id.upbtn:

break;

}

}

2、连接服务器的按钮应该是个开关型按钮,按下和松开是两种状态。在android里有ToggleButton类,这就是个按钮控件,但是个人感觉用着

很不舒服,因此就自定义了一个开关型的button。其实就是一个普通的button,通过判断语句来实现开关的功能,关于这一部分功能可以参

考我的源代码

3、socket编程,这个项目里TCP服务器已经由硬件实现,android只需要实现TCP客户端

(1)socket是要访问网络的,因此需要在AndroidManifset中申明网络访问权限

这句话代表访问网络权限,加在application标签后

(2)在开发时遇到一个问题,android4.0以上的版本不支持在同一个线程里访问网络,因此必须创建一个新线程

//创建一个线程,将ip和端口号传入,在新的线程里创建socket

TcpClientThread tcpClientThread = new TcpClientThread(ip, port);

//启动线程

tcpClientThread.start();

由于创建socket时IP和端口应该由外部输入,不能定死,因此需要在创建新线程时传递参数。这样就需要

在新的线程类中有带参数的构造函数

public class TcpClientThread extends Thread

{

String ip = null;

int port = 0;

//在构造函数里得到参数ip 和port

public TcpClientThread(String Ip, int Port)

{

// TODO Auto-generated constructor stub

ip = Ip;

port = Port;

}

@Override

//run方法会自动调用

public void run()

{

}

}

(3)创建线程的函数new Socket(dstName, dstPort)是一个阻塞的过程,如果服务器没有启动,那么程序就会一直等待,

因此用下面这种方法来创建一个socket

//创建一个socket

socket = new Socket();

//获取socket地址

SocketAddress socketAddress = new InetSocketAddress(ip, port);

//连接socket

socket.connect(socketAddress);

4、源代码

(1)MainActivity.java

点击(此处)折叠或打开

package com.example.tcp_client;

import java.io.IOException;

import java.io.OutputStream;

import android.os.Bundle;

import android.app.Activity;

import android.app.AlertDialog;

import android.content.DialogInterface;

import android.view.Menu;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

public class MainActivity extends Activity

{

//按钮控件,有前后左右,停车

private Button upBtn = null;

private Button downBtn = null;

private Button leftBtn = null;

private Button rightBtn = null;

private Button stopBtn = null;

//按钮控件,连接或者断开服务器

private Button tcpBtn = null;

//EditText 输入服务器ip和端口号

private EditText ipEdit = null;

private EditText portEdit = null;

//TextView 显示信息

private TextView tcpInfoText = null;

private TextView carInfoText = null;

private String IP = null;

private int PORT;

//如果已经连接到服务器,那么按钮上就应该显示断开服务器

private String linked = "断开服务器";

//如果服务器已经断开,那么按钮上就应该显示;连接服务器

private String unlink = "连接服务器";

//检测自定义ToggleButton的状态,false代表还没按下,true代表按下

private boolean isChecked = false;

//自定义的TcpClient类实体

private TcpClient tcpClient = null;

//输出流,用于socket

static OutputStream outStream = null;

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

//指定布局文件

setContentView(R.layout.activity_main);

//将控件找到

upBtn = (Button)findViewById(R.id.upbtn);

downBtn = (Button)findViewById(R.id.downbtn);

leftBtn = (Button)findViewById(R.id.leftbtn);

rightBtn = (Button)findViewById(R.id.rightbtn);

stopBtn = (Button)findViewById(R.id.stopBtn);

//为控件绑定监听器Listener

upBtn.setOnClickListener(new runButtonListenner());

downBtn.setOnClickListener(new runButtonListenner());

leftBtn.setOnClickListener(new runButtonListenner());

rightBtn.setOnClickListener(new runButtonListenner());

stopBtn.setOnClickListener(new runButtonListenner());

//连接服务器的按钮式一个button控件,自定义为ToggleButton

tcpBtn = (Button)findViewById(R.id.tcpToggleBtn);

tcpBtn.setOnClickListener(new switchButtonListener());

tcpBtn.setText(unlink);

ipEdit = (EditText)findViewById(R.id.ipEdit);

portEdit = (EditText)findViewById(R.id.portEdit);

tcpInfoText = (TextView)findViewById(R.id.tcpInfoText);

carInfoText = (TextView)findViewById(R.id.carInfoText);

}

//关于小车状态按钮的监听器

class runButtonListenner implements OnClickListener

{

@Override

public void onClick(View v)

{

// TODO Auto-generated method stub

switch(v.getId())

{

case R.id.upbtn:

if(outStream != null)

{

tcpClient.sendBuffer(outStream, "#up*");

carInfoText.setText("小车正在前进");

}

break;

case R.id.downbtn:

if(outStream != null)

{

tcpClient.sendBuffer(outStream, "#dn*");

carInfoText.setText("小车正在后退");

}

break;

case R.id.leftbtn:

if(outStream != null)

{

tcpClient.sendBuffer(outStream, "#zz*");

carInfoText.setText("小车正在左转");

}

break;

case R.id.rightbtn:

if(outStream != null)

{

tcpClient.sendBuffer(outStream, "#yz*");

carInfoText.setText("小车正在右转");

}

break;

case R.id.stopBtn:

if(outStream != null)

{

tcpClient.sendBuffer(outStream, "#sp*");

carInfoText.setText("小车已经停车");

}

default:

break;

}

}

}

//自定义ToggleButton控件的监听器,按下和松开是两种显示状态

class switchButtonListener implements OnClickListener

{

@Override

public void onClick(View v)

{

// TODO Auto-generated method stub

//每次点击按钮之后,按钮的状态就应该取反

isChecked = !isChecked;

//如果按下按钮,ToggleButton的状态时true

if(isChecked)

{

//从ip的EditText获取IP

IP = ipEdit.getText().toString();

//如果没有输入IP,那么获取的字符串长度就是0,按钮的状态还是false

if(IP.length() == 0)

{

//没有IP,ToggleButton的状态还是false

isChecked = false;

//弹出提示对话框,提示用户输入IP

new AlertDialog.Builder(MainActivity.this)

.setTitle("提示")

.setIcon(android.R.drawable.ic_dialog_info)

.setMessage("请输入服务器IP地址")

.setPositiveButton("确定", new DialogInterface.OnClickListener(){

@Override

public void onClick(DialogInterface arg0, int arg1) {

// TODO Auto-generated method stub

}})

.show();

}

else

{

//如果没有输入端口号,那么从端口edit获取的字符串长度也是0

if(portEdit.getText().toString().length() == 0)

{

//没有端口号,ToggleButton的状态还是false

isChecked = false;

//弹出对话框,提示用户输入端口

new AlertDialog.Builder(MainActivity.this)

.setTitle("提示")

.setIcon(android.R.drawable.ic_dialog_info)

.setMessage("请输入服务器端口号")

.setPositiveButton("确定", new DialogInterface.OnClickListener(){

@Override

public void onClick(DialogInterface arg0, int arg1) {

// TODO Auto-generated method stub

}})

.show();

}

try{

//将获取的端口号转为一个整数

PORT = Integer.parseInt(portEdit.getText().toString());

}catch (Exception e){

e.printStackTrace();

}

//创建TcpClient实体

tcpClient = new TcpClient();

//创建socket,并且获取到一个outputStream

tcpClient.createTcpCient(IP, PORT);

if(outStream != null)

{

//如果得到了OutputStream,在信息框提示连接成功自定义ToggleButton就显示

tcpInfoText.setText("OK,连接服务器成功");

//已经连接到服务器,ToggleButton显示“断开连接”

tcpBtn.setText(linked);

}

else

{

//没有得到OutputStream,自定义ToggleButton的属性还是false

isChecked = false;

}

}

}

else

{

//如果ToggleButton的状态是false,那么就断开socket

try

{

TcpClient.socket.close();

} catch (IOException e)

{

// TODO Auto-generated catch block

e.printStackTrace();

}

//释放资源

outStream = null;

tcpClient = null;

//将ToggleButton的内容设置为“连接服务器”

tcpBtn.setText(unlink);

//信息edit提示服务器断开

tcpInfoText.setText("注意,服务器已断开");

}

}

}

@Override

public boolean onCreateOptionsMenu(Menu menu)

{

// Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.main, menu);

return true;

}

}

(2)TcpClient.java

点击(此处)折叠或打开

package com.example.tcp_client;

import java.io.IOException;

import java.io.OutputStream;

import java.io.UnsupportedEncodingException;

import java.net.InetSocketAddress;

import java.net.Socket;

import java.net.SocketAddress;

public class TcpClient

{

public static Socket socket = null;

public void createTcpCient(String ip, int port)

{

//创建一个线程,将ip和端口号传入,在新的线程里创建socket

TcpClientThread tcpClientThread = new TcpClientThread(ip, port);

//启动线程

tcpClientThread.start();

}

public class TcpClientThread extends Thread

{

String ip = null;

int port = 0;

//在构造函数里得到参数ip 和port

public TcpClientThread(String Ip, int Port)

{

// TODO Auto-generated constructor stub

ip = Ip;

port = Port;

}

@Override

//run方法会自动调用

public void run()

{

// TODO Auto-generated method stub

super.run();

try{

//创建一个socket

socket = new Socket();

//获取socket地址

SocketAddress socketAddress = new InetSocketAddress(ip, port);

//连接socket

socket.connect(socketAddress);

//从socket获取一个outputstream

MainActivity.outStream = socket.getOutputStream();

//将buffer的内容写入outputstream

}catch    (IOException e){

e.printStackTrace();

}

}

}

//通过socket发送数据

public void sendBuffer(OutputStream outputStream, String buf)

{

byte buffer[] = null;

try

{

//将String转为byte类型

buffer = buf.getBytes("ISO-8859-1");

} catch (UnsupportedEncodingException e)

{

// TODO Auto-generated catch block

e.printStackTrace();

}

try

{

//输出数据

outputStream.write(buffer, 0, buffer.length);

outputStream.flush();

} catch (IOException e)

{

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

(3)activity_main.xml

点击(此处)折叠或打开

xmlns:tools=""

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=".MainActivity" >

android:id="@+id/upbtn"

style="?android:attr/buttonStyleSmall"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/downbtn"

android:text="前进" />

android:id="@+id/leftbtn"

style="?android:attr/buttonStyleSmall"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@+id/upbtn"

android:layout_marginRight="28dp"

android:layout_toLeftOf="@+id/upbtn"

android:text="左转" />

android:id="@+id/rightbtn"

style="?android:attr/buttonStyleSmall"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignBaseline="@+id/stopBtn"

android:layout_alignBottom="@+id/stopBtn"

android:layout_marginLeft="24dp"

android:layout_toRightOf="@+id/stopBtn"

android:text="右转" />

android:id="@+id/stopBtn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignBottom="@+id/leftbtn"

android:layout_alignLeft="@+id/upbtn"

android:layout_alignRight="@+id/upbtn"

android:layout_below="@+id/upbtn"

android:text="停车" />

android:id="@+id/downbtn"

style="?android:attr/buttonStyleSmall"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@+id/stopBtn"

android:layout_centerHorizontal="true"

android:text="后退" />

android:id="@+id/ipEdit"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:hint = "请输入服务器IP"

android:inputType="none"

android:layout_below="@+id/downbtn"

/>

android:id="@+id/portEdit"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:hint = "请输入服务器端口"

android:inputType="none"

android:layout_below="@+id/ipEdit"

/>

android:id="@+id/tcpInfoText"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_below="@+id/tcpToggleBtn"

android:singleLine="false"

/>

android:id="@+id/carInfoText"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_below="@+id/tcpInfoText"

android:singleLine="false"

/>

android:id="@+id/tcpToggleBtn"

style="@android:style/MediaButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/portEdit"

android:layout_alignRight="@+id/portEdit"

android:layout_below="@+id/portEdit"

android:textOn="断开服务器"

android:textOff="连接服务器"

/>

(4)AndroidManifest.xml

点击(此处)折叠或打开

package="com.example.tcp_client"

android:versionCode="1"

android:versionName="1.0" >

android:minSdkVersion="8"

android:targetSdkVersion="18" />

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

android:name="com.example.tcp_client.MainActivity"

android:label="@string/app_name" >

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值