android项目中,测试Socket连接时遇到的问题

本文通过一个简单的Android项目,展示了如何使用Socket实现两台设备间的通信。在实现过程中,作者遇到了AsyncTask只能同时运行一个的问题,以及byte[]实例化的问题。解决方案包括使用线程池和调整数据处理方式。通过这个例子,读者可以了解Android中Socket通信的基本流程和常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在学习完一个项目之余,随意找了本书阅读,看到了对AsyncTask及另外几种实现线程的方式,由于讲的比较新颖,于是就放下书本,开始码代码了。没想到,这一写,还真的就碰到了以前不曾注意的问题。
1.AsyncTask在一个项目中只能同时运行一个。
2.byte[]的实例化伴随着其中的byte的实例化。

由于是在闲暇时间完成的,所以仅仅只是一个简单的测试程序,仅此而已。

在这次测试中是利用Socket进行两台android设备之间的通信,完成了两个项目——一个是模拟服务器,一个是模拟客户端。
第一个项目(服务器)的代码:
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

import android.app.Activity;
import android.app.AlertDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
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 TextView text; // 接收到的消息
	private EditText edit; // 发送消息的编辑框
	private Button send; // 发送按钮
	private ServerSocket server; // 服务器
	private Socket client; // 连接的客户端
	BufferedInputStream bis; // 输入流
	PrintStream ps; // 输出流

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		this.text = (TextView) findViewById(R.id.text);
		this.edit = (EditText) findViewById(R.id.edit);
		this.send = (Button) findViewById(R.id.send);

		// 接收数据的线程启动
		AsyncTask<Void, byte[], Void> read = new ReadDataThread();
		read.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

		// 为发送按钮设置点击事件
		this.send.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				String str = edit.getText().toString();
				new SendDataThread().execute(str);
			}
		});
	}

	/**
	 * 发送数据线程
	 * 
	 * @author yufeng
	 * 
	 */
	private class SendDataThread extends AsyncTask<String, String, Void> {

		@Override
		protected Void doInBackground(String... params) {
			if (client != null) {
				ps.print(params[0]);
			} else {
				publishProgress("无客户端连接,请等待。。。");
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(String... values) {
			new AlertDialog.Builder(MainActivity.this).setTitle("提示")
					.setMessage(values[0]).setNeutralButton("取消", null)
					.create().show();
		}
	}

	/**
	 * 接收数据数据线程
	 * 
	 * @author yufeng
	 * 
	 */
	private class ReadDataThread extends AsyncTask<Void, byte[], Void> {

		@Override
		protected Void doInBackground(Void... params) {

			try { // 初始化相关参数
				server = new ServerSocket(8888);
				client = server.accept();
				System.out.println("客户端连接成功");
				bis = new BufferedInputStream(client.getInputStream());
				ps = new PrintStream(client.getOutputStream());
			} catch (Exception e) {
				e.printStackTrace();
			}

			int len;
			byte[] b;
			while (client != null) {
				try {
					len = bis.available();
					if (len > 0) {
						b = new byte[len];
						bis.read(b);
						publishProgress(b);
						b = null;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				SystemClock.sleep(1000);
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(byte[]... values) {
			byte[] by = values[0];
			String msg = new String(by);
			text.setText("消息:" + msg);
		}
	}

	@Override
	protected void onStop() {
		super.onStop();
		try {
			if (bis != null) {
				bis.close();
			}
			if (ps != null) {
				ps.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
该项目就一个类——MainActivity,由于布局比肩简单粗糙,因此省略。
该类中主要就是SendDataThread和ReadDataThread这两个内部类,这两个类都继承自AsyncTask类,项目运行后ReadDataThread就执行,一直等待客户端的连接,之后循环进行数据的读取与UI更新。

第二个项目(客户端)的代码:
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;

import android.app.Activity;
import android.app.AlertDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
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 TextView text; // 接受到的消息
	private EditText edit, ip; // 发送的消息编辑框,IP地址编辑框
	private Button send, setIp; // 发送消息按钮,设置IP按钮
	private Socket client; // 客户端
	BufferedInputStream bis; // 输入流
	PrintStream ps; // 输出流
	private String strIp = null; // IP地址设置

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		this.text = (TextView) findViewById(R.id.text);
		this.edit = (EditText) findViewById(R.id.edit);
		this.send = (Button) findViewById(R.id.send);
		this.ip = (EditText) findViewById(R.id.ip);
		this.setIp = (Button) findViewById(R.id.setIp);

		// 为发送按钮设置点击事件
		this.send.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				String str = edit.getText().toString();
				new SendDataThread().execute(str);
			}
		});

		// 为IP设置按钮设置点击事件
		this.setIp.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				strIp = ip.getText().toString().trim();
				// 为了避免测试时在IP设置之前操作该按钮,在xml文件中将发送按钮设置为了不可见
				send.setVisibility(View.VISIBLE);
				// 执行消息发送线程
				AsyncTask<Void, byte[], Void> read = new ReadDataThread();
				read.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
			}
		});
	}

	/**
	 * 发送数据
	 * 
	 * @author yufeng
	 * 
	 */
	private class SendDataThread extends AsyncTask<String, String, Void> {

		@Override
		protected Void doInBackground(String... params) {
			if (client != null && strIp != null) {
				ps.print(params[0]);
			} else {
				publishProgress("尚未成功连接服务器");
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(String... values) {
			new AlertDialog.Builder(MainActivity.this).setTitle("提示")
					.setMessage(values[0]).setNeutralButton("取消", null)
					.create().show();
		}
	}

	/**
	 * 接收数据
	 * 
	 * @author yufeng
	 * 
	 */
	private class ReadDataThread extends AsyncTask<Void, byte[], Void> {

		@Override
		protected Void doInBackground(Void... params) {
			try { // 初始化相关参数
				if (client == null) {
					client = new Socket(strIp, 8888);
				}
				bis = new BufferedInputStream(client.getInputStream());
				ps = new PrintStream(client.getOutputStream());
			} catch (Exception e) {
				e.printStackTrace();
			}

			int len;
			byte[] b;
			while (client != null) {
				try {
					len = bis.available();
					if (len > 0) {
						b = new byte[len];
						bis.read(b);
						publishProgress(b);
						b = null;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				SystemClock.sleep(1000);
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(byte[]... values) {
			byte[] by = values[0];
			String msg = new String(by);
			text.setText("消息:" + msg);
		}
	}

	@Override
	protected void onStop() {
		super.onStop();
		try {
			if (bis != null) {
				bis.close();
			}
			if (ps != null) {
				ps.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
该项目与服务器大同小异。在客户端的界面中多了一个IP设置,除此之外,两个项目大致相同。
在IP设置正确的前提下,能实现客户端与服务器端的聊天(姑且称之为聊天吧),测试目的也基本完成。
在上面列举的面临的两个问题,第一个问题是利用线程池解决,即执行时将execute()方法替换为executeOnExecutor()方法。
第二个问题发生在数据获取、保存时,原本思路是申请一个足够大的byte[],这就导致输出数据时会附带一串无意义的乱码,解决方式如代码。
测试时间仓促,仅为实现联通,难免有所遗漏,请留言告知。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值