Android Socket 通信实例...【Pnoker】

项目推荐

推荐个人GItHub开源项目(iot-dc3)一个开源的、分布式的物联网(IOT)平台,用于快速开发物联网项目和管理拓展。https://github.com/pnoker/iot-dc3


这几天一直在学习Java套接字,昨天做了个小实验,发现仅简单的把Java Socket通信的代码放在Android上是会报错的,而且问题还很大,于是乎,我就搜集了一些资料发现,要想实现Android Socket 还得注意一下几点。

下来我们就具体实施一下,本人一贯认为,实践才是硬道理,不过理论知识也是很重要的,首先讲讲理论知识,有助于你理解。

##Socket

Socket(套接字)是一种通信机制,可以实现单机或跨网络进行通信,其创建需要明确的区分C(客户端)/S(服务器端),支持多个客户端连接到同一个服务器。有两种传输模式:

  • 面向连接的传输:基于TCP协议,可靠性高,但效率低;
  • 面向无连接的传输:基于UDP协议,可靠性低,但效率高;

Android中,直接采用Socket通信应该是我们遇到的最低级的网络运用。尽管已经作了很大程度的抽象,但是纯粹的Socket通信,仍然给开发者留下很多细节需要处理,尤其在服务器端,开发者需要处理多线程以及数据缓冲等的设计问题。相对而言,处于更高抽象层的HTTP等,已经对Socket通信中需要处理的技术细节进行了很好的封装,开发者无须关心,因此,HTTP在网络开发中通常具有决定性的优势。

Android在其核心库的java包中,提供了用于客户端的Socket class和用于服务器端的ServerSocket class,分别位于:
$SOURCE/libcore/luni/src/main/java/java/net/Socket.java
$SOURCE/libcore/luni/src/main/java/java/net/ServerSocket.java
文件中。
分析两个class的源码,可以看出封装考虑的很全面,只构造方法一向每个class都考虑了很多种使用情况。由于本人只是初学者,很多理解的不深入,这里只抛砖引玉的对两个class的构造方法分别介绍一种,就是我下面的程序中用到的:

Socket(String dstName, int dstPort):创建一个以流的方式(基于TCP协议)连接到目标机(这里可以理解为服务器)的客户端Socket;dstName是目标机的IP地址,dstPort是要连接的目标机的端 口号。这里要注意对端口的理解,它不能理解为物理上的一个接口,而是对计算机中一块特殊内存区域的形象表述。

ServerSocket(int aport):创建一个绑定到本机指定端口的服务端Socket;aport就是指定的本机端口。与上述客户端Socket对应,通过TCP连接时,ServerSocket创建后需要在aport端口上进行监听,等待客户端的连接。

##功能描述

  • 简单的基于Socket的数据通信;
  • 采用TCP方式连接;
  • 采用C/S结构,但服务端只支持一个连接;
  • 客户端能够向服务端发送数据,并显示服务端的返回信息;
  • 服务端能够接收客户端的数据,并将收到的数据以特定的方式返回给客户端;
    ##程序实现思路
  • 服务端:设计为在后台执行的service,用一个独立的线程来处理客户端的连接请求、数据接收和返回。为了启动该service,编写个简单的Activity。
  • 客户端:设计为一个Activity,界面由三部分组成:显示服务端返回信息的文本区域(一个文本框);进行数据输入的编辑区域(一个编辑框);以及触发连接请求并执行数据发送的触发区域(一个按钮)。

##问题分析

(这一步很重要,正是应为这种问题,才导致Android 用用程序报错)

##回顾线程创建
继承 Thread 类,覆盖方法 run(),我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。下面是一个例子:

public class MyThread extends Thread {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new MyThread(i+1).start();
}
}

##回顾Runable()接口方法
实现 Runnable 接口 Runnable 接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数public Thread(Runnable target);来实现。下面是一个例子:

 public class MyThread implements Runnable {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println("线程 " + number + ":计数 " + count);
if(++count== 6) return;
} 
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new Thread(new MyThread(i+1)).start();
}
}

Runnable说白了只是个回调接口,并不是所有的Runnable都是用于线程的;如果把它做为线程:传给Thread类传给Executor类

  • 要把Handle对象传递的东西放在一个消息循环的队列当中,如果没有这个消息循环队列的话,就会报错!

  • 在做比较简单线程通讯的时候,我们只是在主Activity线程中使用Handler.sendMessage(Message)方法来实现消息的发送,用Handler.handleMessage(Message)类实现消息的获取,这似乎没有什么错误,而且如果在我们自己开启的普通线程中向主Activity线程发送消息似乎也不会有什么错误,可以捕获我们想要的消息。
    3.但是如果我们接收消息的线程不在主线程那么而是一个普通线程,那么就会报错。为什么会这样呢?原因就是因为在我们自定义的普通线程中没有消息循环队列。那么怎么来实现一个消息循环队列呢?(假设我们都复写了handleMessage(Message)方法)

  • 这是最简单的方法,是在实例化Handler对象的时候传入调用HandlerThread.getLooper()获得的Looper对象,这个Looper就是我们需要的消息循环队列。当然这个HandlerThread对象必须调用start()方法去让他开启的线程运行。

  • 这个方法是模仿HandlerThread类的run()方法写出来的。就是在定义Handler对象的前加上Looper.prepare()方法,让消息循环开始准备,在定义完以后在调用Looper.loop();然消息循环开始运行。其实这两个方法本质上是没有没有区别的,都是给一个普通线程添加了一个消息队列。

##代码实例解析
Android APP当客户端进行数据的发送
Java程序当服务器端进行数据的接收
###以下是Android Project下的代码实例:
MainActivity.java

package com.example.lightled;

import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import java.net.SocketTimeoutException;
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@SuppressLint("NewApi")
public class MainActivity extends Activity implements OnClickListener {
	public Button mbut1;
	public Button mbut2;
	public Button mbut3;
	public Button mbut4;
	@TargetApi(Build.VERSION_CODES.GINGERBREAD)
	@SuppressLint("NewApi")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mbut1 = (Button) findViewById(R.id.butled1);
		mbut2 = (Button) findViewById(R.id.butled2);
		mbut3 = (Button) findViewById(R.id.butled3);
		mbut4 = (Button) findViewById(R.id.butled4);
		mbut1.setOnClickListener(this);
		mbut2.setOnClickListener(this);
		mbut3.setOnClickListener(this);
		mbut4.setOnClickListener(this);
		StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
				.detectDiskReads().detectDiskWrites().detectNetwork()
				.penaltyLog().build());
		StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
				.detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath()
				.build());
	}
	public static Handler myHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == 0x11) {
				Bundle bundle = msg.getData();
			}
		}
	};
	@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;
	}
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
		case R.id.butled1:
			ClientThread clientThread1 = new ClientThread();
			clientThread1.run("1");
			break;
		case R.id.butled2:
			ClientThread clientThread2 = new ClientThread();
			clientThread2.run("2");
			break;
		case R.id.butled3:
			ClientThread clientThread3 = new ClientThread();
			clientThread3.run("3");
			break;
		case R.id.butled4:
			ClientThread clientThread4 = new ClientThread();
			clientThread4.run("4");
			break;
		}
	}
	private static class ClientThread extends Thread {
		public void run(String s) {
			String host = "192.168.244.216";
			int port = 1234;
			Socket client = null;
			Message msg = new Message();
			msg.what = 0x11;
			try {
				client = new Socket(host, port);
				Writer writer = new OutputStreamWriter(client.getOutputStream());
				writer.write(s);
				writer.flush();
				myHandler.sendMessage(msg);
				writer.close();
				client.close();
			} catch (SocketTimeoutException aa) {
				// TODO Auto-generated catch block
				myHandler.sendMessage(msg);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/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" >

    <LinearLayout
        android:id="@+id/lin1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/tex1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Light Led " 
            android:textColor="#ee0000"/>

        <Button
            android:id="@+id/butled1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Led 1" 
            android:textColor="#123456"/>

        <Button
            android:id="@+id/butled2"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Led 2" 
            android:textColor="#341291"/>

        <Button
            android:id="@+id/butled3"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Led 3" 
            android:textColor="#234128"/>

        <Button
            android:id="@+id/butled4"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Led 4" 
            android:textColor="#542387"/>
    </LinearLayout>

</RelativeLayout>

AndroidManifest.xml

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

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.lightled.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

###以下是Java Project下的代码实例:
Server.java

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	public static void main(String args[]) throws IOException {
		while (true) {
			int port = 1234;
			ServerSocket server = new ServerSocket(port);
			Socket socket = server.accept();
			Reader reader = new InputStreamReader(socket.getInputStream());
			char chars[] = new char[64];
			int len;
			StringBuilder content = new StringBuilder();
			String temp = null;
			int index;
			while ((len = reader.read(chars)) != -1) {
				temp = new String(chars, 0, len);
				content.append(temp);
			}
			// System.out.println("from client: " + content);
			// System.out.println("from client: " + temp);
			int s = Integer.parseInt(temp);
			// System.out.println(s);
			switch (s) {
			case 1:
				System.out.println("********************Led1 on");
				break;
			case 2:
				System.out.println("********************Led2 on");
				break;
			case 3:
				System.out.println("********************Led3 on");
				break;
			case 4:
				System.out.println("********************Led4 on");
				break;
			}

			reader.close();
			socket.close();
			server.close();
			/**
			 * Writer writer = new OutputStreamWriter(socket.getOutputStream());
			 * writer.write("I am Server,are you Client?."); writer.flush();
			 * writer.close();
			 * 
			 * reader.close(); socket.close(); server.close();
			 **/
		}
	}
}

##运行结果
这里写图片描述
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值