一、Socket通信简介
Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信。两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据。而 Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求。那么,什么是socket?Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信。通过建立socket连接,可为通信双方的数据传输 传提供通道。socket的主要特点有数据丢失率低,使用简单且易于移植。
1.1什么是Socket Socket
是一种抽象层,应用程序通过它来发送和接收数据,使用Socket可以将应用程序添加到网络中,与处于同一网络中的其他应用程序进行通信。简单来说,Socket提供了程序内部与外界通信的端口并为通信双方的提供了数据传输通道。
1.2Socket的分类
根据不同的的底层协议,Socket的实现是多样化的。本指南中只介绍TCP/IP协议族的内容,在这个协议族当中主要的Socket类型为流套接字(streamsocket)和数据报套接字(datagramsocket)。流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。数据报套接字使用UDP协议,提供数据打包发送服务。下面,我们来认识一下这两种Socket类型的基本实现模型。
二、Socket 基本通信模型
三、Socket基本实现原理
3.1基于TCP协议的Socket
服务器端首先声明一个ServerSocket对象并且指定端口号,然后调 用Serversocket的accept()方法接收客户端的数据。accept()方法在没有数据进行接收的处于堵塞状态。 (Socketsocket=serversocket.accept()),一旦接收到数据,通过inputstream读取接收的数据。
客户端创建一个Socket对象,指定服务器端的ip地址和端口号(Socketsocket=newSocket("172.168.10.108",8080);),通过inputstream读取数据,获取服务器 发出的数据(OutputStreamoutputstream=socket.getOutputStream()),最后将要发送的数据写入到outputstream即可进行TCP协议的socket数据传输。
3.2基于UDP协议的数据传输
服务器端首先创建一个DatagramSocket对象,并且指点监听的端 口。接下来创建一个空的DatagramSocket对象用于接收数据 (bytedata[]=newbyte[1024;]DatagramSocketpacket=newDatagramSocket(data,data.length)), 使用DatagramSocket的receive方法接收客户端发送的数据,receive()与serversocket的accepet()类似, 在没有数据进行接收的处于堵塞状态。
客户端也创建个DatagramSocket对象,并且指点监听的端口。接下来创建一个InetAddress对象,这个对象类似与一个网络的发送地址(InetAddressserveraddress=InetAddress.getByName("172.168.1.120")).定义要发送的 一个字符串,创建一个DatagramPacket对象,并制定要讲这个数据报包发送到网络的那个地址以及端口号,最后使用DatagramSocket 的对象的send()发送数据。*(Stringstr="hello";bytedata[]=str.getByte();DatagramPacketpacket=new DatagramPacket(data,data.length,serveraddress,4567);socket.send(packet);)
四、android 实现socket简单通信
<span style="font-size:18px;">• <!--允许应用程序改变网络状态-->
• <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
•
• <!--允许应用程序改变WIFI连接状态-->
• <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
•
• <!--允许应用程序访问有关的网络信息-->
• <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
•
• <!--允许应用程序访问WIFI网卡的网络信息-->
• <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
•
• <!--允许应用程序完全使用网络-->
• <uses-permission android:name="android.permission.INTERNET"/</span>
4.1建立Socket操作类
<span style="font-size:18px;">package com.ecjtu.smartparking.tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import com.ecjtu.smartparking.data.Carport;
/**
* Socket连接操作类
*
* @author Esa
*/
public class TCPSocketFactory {
private Socket mSocket;// socket连接对象
private DataOutputStream out;
private DataInputStream in;// 输入流
private byte[] buffer = new byte[1024*1];// 缓冲区字节数组,信息不能大于此缓冲区
private byte[] tmpBuffer;// 临时缓冲区
private TCPSocketCallback callback;// 信息回调接口
private int timeOut = 1000 * 30;
/**
* 构造方法传入信息回调接口对象
*
* @param sdi
* 回调接口
*/
public TCPSocketFactory(TCPSocketCallback callback) {
this.callback = callback;
}
/**
* 连接网络服务器
*
* @throws UnknownHostException
* @throws IOException
*/
public void connect(String ip, int port) throws Exception {
mSocket = new Socket();
SocketAddress address = new InetSocketAddress(ip, port);
mSocket.connect(address, timeOut);// 连接指定IP和端口
if (isConnected()) {
out = new DataOutputStream(mSocket.getOutputStream());// 获取网络输出流
in = new DataInputStream(mSocket.getInputStream());// 获取网络输入流
if (isConnected()) {
callback.tcp_connected();
}
}
}
public void setTimeOut(int timeOut) {
this.timeOut = timeOut;
}
/**
* 返回连接服是否成功
*
* @return
*/
public boolean isConnected() {
if (mSocket == null || mSocket.isClosed()) {
return false;
}
return mSocket.isConnected();
}
/**
* 发送数据
*
* @param buffer
* 信息字节数据
* @throws IOException
*/
public void write(byte[] buffer) throws IOException {
if (out != null) {
out.write(buffer);
out.flush();
}
}
/**
* 断开连接
*
* @throws IOException
*/
public void disconnect() {
try {
if (mSocket != null) {
if (!mSocket.isInputShutdown()) {
mSocket.shutdownInput();
}
if (!mSocket.isOutputShutdown()) {
mSocket.shutdownOutput();
}
}
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
if (mSocket != null && !mSocket.isClosed()) {// 判断socket不为空并且是连接状态
mSocket.close();// 关闭socket
}
} catch (Exception e) {
e.printStackTrace();
} finally {
callback.tcp_disconnect();
out = null;
in = null;
mSocket = null;// 制空socket对象
}
}
/**
* 读取网络数据
*
* @throws IOException
*/
public void read() throws IOException {
String msg=null;
if (in != null) {
int len = 0;// 读取长度
while ((len = in.read(buffer)) > 0) {
tmpBuffer = new byte[len];// 创建临时缓冲区
// String t=new String(tmpBuffer);
System.arraycopy(buffer, 0, tmpBuffer, 0, len);// 将数据拷贝到临时缓冲区
msg=new String(tmpBuffer,"GBK");
System.out.println("tttttt="+msg);
callback.tcp_receive(tmpBuffer);// 调用回调接口传入得到的数据
tmpBuffer = null;
}
}
}
}</span>
4.2连接服务器线程类
<span style="font-size:18px;">package com.ecjtu.smartparking.tcp;
import java.util.Vector;
import android.os.Handler;
import android.util.Log;
/**
* 连接服务器线程类
*
* @author Esa
*/
public class TCPSocketConnect implements Runnable {
public boolean isConnect = false;// 是否连接服务器
private boolean isWrite = false;// 是否发送数据
private static Vector<byte[]> datas = new Vector<byte[]>();// 待发送数据队列
private Object lock = new Object();// 连接锁对象
private TCPSocketFactory mSocket;// socket连接
private WriteRunnable writeRunnable;// 发送数据线程
private String ip = null;
private int port = -1;
/**
* 创建连接
*
* @param callback
* 回调接口
* @param executor
* 线程池对象
*/
public TCPSocketConnect(TCPSocketCallback callback) {
mSocket = new TCPSocketFactory(callback);// 创建socket连接
writeRunnable = new WriteRunnable();// 创建发送线程
}
@Override
public void run() {
if (ip == null || port == -1) {
return;
}
isConnect = true;
while (isConnect) {
synchronized (lock) {
try {
Log.i("TCP",">TCP连接服务器<");
mSocket.connect(ip, port);// 连接服务器
} catch (Exception e) {
try {
Log.e("TCP",">TCP连接服务器失败, 6秒后重新连接<");
resetConnect();// 断开连接
lock.wait(6000);
continue;
} catch (InterruptedException e1) {
continue;
}
}
}
Log.e("TCP",">TCP连接服务器成功<");
isWrite = true;// 设置可发送数据
new Thread(writeRunnable).start();// 启动发送线程
try {
mSocket.read();// 获取数据
} catch (Exception e) {
Log.e("TCP",">TCP连接异常<", e);
} finally {
Log.e("TCP",">TCP连接中断<");
resetConnect();// 断开连接
}
}
Log.e("TCP",">=TCP结束连接线程=<");
}
/**
* 关闭服务器连接
*/
public void disconnect() {
synchronized (lock) {
isConnect = false;
lock.notify();
resetConnect();
}
}
/**
* 重置连接
*/
public void resetConnect() {
Log.w("TCP",">TCP重置连接<");
writeRunnable.stop();// 发送停止信息
mSocket.disconnect();
}
/**
* 向发送线程写入发送数据
*/
public void write(byte[] buffer) {
writeRunnable.write(buffer);
}
/**
* 设置IP和端口
*
* @param ip
* @param port
*/
public void setAddress(String host, int port) {
this.ip = host;
this.port = port;
}
/**
* 发送数据
*/
private boolean writes(byte[] buffer) {
try {
mSocket.write(buffer);
Thread.sleep(1);
return true;
} catch (Exception e) {
resetConnect();
return false;
}
}
/**
* 发送线程
*
* @author Esa
*/
private class WriteRunnable implements Runnable {
private Object wlock = new Object();// 发送线程锁对象
@Override
public void run() {
Log.e("TCP",">TCP发送线程开启<");
while (isWrite) {
synchronized (wlock) {
if (datas.size() <= 0) {
try {
wlock.wait();// 等待发送数据
} catch (InterruptedException e) {
continue;
}
}
while (datas.size() > 0) {
byte[] buffer = datas.remove(0);// 获取一条发送数据
if (isWrite) {
writes(buffer);// 发送数据
} else {
wlock.notify();
}
}
}
}
Log.e("TCP",">TCP发送线程结束<");
}
/**
* 添加数据到发送队列
*
* @param buffer
* 数据字节
*/
public void write(byte[] buffer) {
synchronized (wlock) {
datas.add(buffer);// 将发送数据添加到发送队列
wlock.notify();// 取消等待
}
}
public void stop() {
synchronized (wlock) {
isWrite = false;
wlock.notify();
}
}
}
}</span>
4.3获取网络数据回调类
<span style="font-size:18px;">package com.ecjtu.smartparking.tcp;
/**
* 获取网络数据回调类
*
* @author Esa
*
*/
public abstract interface TCPSocketCallback {
/**
* 断开连接
*/
public static final int TCP_DISCONNECTED = 0;
/**
* 已连接
*/
public static final int TCP_CONNECTED = 1;
/**
* 连接获得数据
*/
public static final int TCP_DATA = 2;
/**
* 当建立连接是的回调
*/
public abstract void tcp_connected();
/**
* 当获取网络数据回调接口
*
* @param buffer
* 字节数据
*/
public abstract void tcp_receive(byte[] buffer);
/**
* 当断开连接的回调
*/
public abstract void tcp_disconnect();
}
</span>
4.4启动线程
//线程连接
TCPSocketConnect connect;
connect = new TCPSocketConnect(new TCPSocketCallback(){
@Override
public void tcp_connected() {
// TODO Auto-generated method stub
}
@Override
public void tcp_receive(byte[] buffer) {
// TODO Auto-generated method stub
System.out.println("---"+new String(buffer));
ArrayList<String> dataCar=new ArrayList<String>();
for(int i=1;i<buffer.length;i++){
byte tem=buffer[i];
new String(new byte[] {tem});
}
}
@Override
public void tcp_disconnect() {
// TODO Auto-generated method stub
}});
// connect.clientHandler=this.handler;
}
connect.setAddress(host, port);
new Thread(connect).start();