—————————————————————————————————————————————————————————————————————————————
开发环境:eclipse +4.0android SDK+串口调试工具
前言:这周的任务是为了 完成socket客户端在安卓平台的开发,能够和服务端正常进行收发信息(还未实现汉字)。开始在网上找了很多程序用来参考,结果都不行,发现android3.0以后就不能在主线程(ui线程)中进行socket网络通信!!看来找的程序比较老了,后来有幸找到了极客学院的一个视频,按照上面的方法直接实现了一下,它的比较简单,所以我又稍微优化了一下,而且它的程序还是有一点小bug的,我修改了一下,不过还有待加强。
ps:模拟器和手机上都能成功运行
首先附上极客学院的视频地址:http://www.iqiyi.com/w_19rtlpgvl1.html
效果图片:
客户端(界面)
服务端:(串口调试助手)
正常通信
超时,即socket连接不上
极客学院中的视频的小bug就是在doInBackground中使用了toast,这个应该是不允许的。
还有就是它的代码中用到了异步通信AsyncTask,我遇到的困难都是因为对它不了解,导致出了很多低级的错误,浪费了我很多的时间,给大家一个链接可以学习一下。
自己的错误也在代码中进行了注释,希望能帮助到大家。
大家可以参考这个链接学习 http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html
自己代码的小特色:
1.可以自己手动输入ip地址和端口号,特别是端口号我们是需要获取数字的,所以这里用了一个Integer.parseInt的方法,当然还增加了一个断开连接。
2.利用了socket.connect这个接口,来判断socket是否连接超时,即服务器连接不上,错误的原因有很多,ip不对啊,端口不对,服务器关闭等等。
个人感觉 android socket客户端的操作流程大致是:
1.首先new 一个socket,然后用ip和端口去初始化它。
2.然后初始化它的outputstream和inputstream,输入输出流,当客户端发数据是操作它的输出流,接受数据是操作它的输入流,将他们放入我们的buf中,最后再对buf进行操作。
ps:其实是比较简单,对我来说很难的应该就是语法吧,很多类的规则和方法不知道怎么去调用,特别是doinbackground中不能操作主线程中的控件和变量真是让我浪费了很久的时间。
附上代码:
代码中我作了一点注释
package com.lzj.example.msocketclient;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private EditText ip=null,port=null;
private EditText editTest=null;
private TextView text=null;
private Button send_btn=null,con_btn=null,discon_btn=null;
private int port_num=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lmain);
ip=(EditText)findViewById(R.id.ip);
port=(EditText)findViewById(R.id.port);
editTest=(EditText)findViewById(R.id.editText);
text=(TextView)findViewById(R.id.textView);
text.setMovementMethod(ScrollingMovementMethod.getInstance());//使能textview滚动属性
con_btn=(Button)findViewById(R.id.con_btn);
con_btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
connect();
send_btn.setEnabled(true);
editTest.setEnabled(true);
}
});
send_btn=(Button) findViewById(R.id.send_btn);
send_btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
send();
}
});
discon_btn=(Button)findViewById(R.id.discon_btn);
discon_btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
close();
}
});
send_btn.setEnabled(false);
editTest.setEnabled(false);
}
//-------------socket 实现--------------------------
Socket socket=null;
BufferedWriter bw=null;
BufferedReader br=null;
/*android4.0后不能在主线程(UI线程)中初始化socket,进行socket网络通信,所以我们用asynctask(异步)来将这个过程放到后台*/
public void connect(){
AsyncTask read=new AsyncTask(){
/*doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。
* 在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。*/
protected Void doInBackground(Void... arg0) {//doinbackground该方法中不能对UI当中的空间进行设置和修改
//将输入端口的editText中获取的string转化为int
port_num=Integer.parseInt(port.getText().toString().trim());
/*字节流转换成字符流可以用 InputSteamReader OutputStreamWriter
转换成BufferdReader BufferedWriter 他们具有缓冲区 */
socket=new Socket();
String line;
//用connect延时3秒来看客户端是否连接服务器
try {
socket.connect(new InetSocketAddress(ip.getText().toString(),port_num ),3000);
bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
publishProgress("@连接成功");
//readline是以\n结尾,所以send函数中的write方法中需要加+\n
/*这个地方是我出错的地方,只是把上面3句用try catch包围!当第一次输入错误ip的时候
* 程序会退出,是因为我把下面这个循环用try catch包围了,因为下面用到了br变量
* 如果前面出错那么这个变量则不会被初始化!!
* 同样不能再catch中用toast,因为不能在非ui线程中使用它,doinbackground是在后台运行
* 切记!花了我太久太久的时间了!*/
while ((line=br.readLine())!=null){
publishProgress(line);
}
} catch (IOException e1) {
try {//异常检查,如果超时或者断开连接则则执行下面操作
publishProgress("@连接失败(超时or断开)");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
e1.printStackTrace();
}
return null;
}
/*onProgressUpdate(Progress…)
* 可以使用进度条增加用户体验度。
* 此方法在主线程执行,用于显示任务执行的进度。
* 这里是可以执行主线程中的设置,所以可以用toast*/
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
if(values[0].equals("@连接成功")){
Toast.makeText(MainActivity.this,"连接成功" ,Toast.LENGTH_SHORT).show();
}
text.append("服务器:"+values[0]+"\n");
}
};
read.execute();//执行
}
/*发送信息的函数*/
public void send() {
try {
text.append("本机:"+editTest.getText().toString()+"\n");
bw.write(editTest.getText().toString()+"\n");
bw.flush();
editTest.setText("");
} catch (IOException e) {
e.printStackTrace();
}
}
/*关闭socket函数,即断开连接*/
public void close(){
try {
socket.close();
Toast.makeText(MainActivity.this,"断开连接" ,Toast.LENGTH_SHORT).show();
send_btn.setEnabled(false);
editTest.setEnabled(false);
} catch (IOException e) {
e.printStackTrace();
}
}
}