Extends:(http://www.cnblogs.com/likwo/p/3641135.html

Android 通过Socket 和服务器通讯,是一种比较常用的通讯方式,时间比较紧,说下大致的思路,希望能帮到使用socket 进行通信的人

(1)开启一个线程发送消息    SocketOutputThread

      消息是放在队列里的,当有消息后,进入队列,线程唤醒,发送消息,并反馈发送是否成功的回调

(2)开启一个线程接受服务器消息 SocketInputThread

       为了防止一直收数据,浪费电池的电,采用NIO的方式读socket的数据,这个是本文的关键

(3)开启一个线程,做心跳,防止socket连接终断 , SocketHeartThread 

(4)构建 SocketThreadManager对以上三个thread进行管理

(5)构建 TCPClient 发送socket消息

     在NIO的方式实现TCP,特别是在接收服务器的数据,不用写个线程定时去读了。

主要代码如下,详细代码在附件里。

package com.example.socketblockdemo;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import android.os.Bundle;
import android.os.Handler;
import android.os.Message; /**
* 客户端写消息线程
*
* @author way
*
*/
public class SocketOutputThread extends Thread
{
private boolean isStart = true;
private static String tag = "socketOutputThread";
private List<MsgEntity> sendMsgList; public SocketOutputThread( )
{ sendMsgList = new CopyOnWriteArrayList<MsgEntity>();
} public void setStart(boolean isStart)
{
this.isStart = isStart;
synchronized (this)
{
notify();
}
} // 使用socket发送消息
public boolean sendMsg(byte[] msg) throws Exception
{ if (msg == null)
{
CLog.e(tag, "sendMsg is null");
return false;
} try
{
TCPClient.instance().sendMsg(msg); } catch (Exception e)
{
throw (e);
} return true;
} // 使用socket发送消息
public void addMsgToSendList(MsgEntity msg)
{ synchronized (this)
{
this.sendMsgList.add(msg);
notify();
}
} @Override
public void run()
{
while (isStart)
{
// 锁发送list
synchronized (sendMsgList)
{
// 发送消息
for (MsgEntity msg : sendMsgList)
{ Handler handler = msg.getHandler();
try
{
sendMsg(msg.getBytes());
sendMsgList.remove(msg);
// 成功消息,通过hander回传
if (handler != null)
{
Message message = new Message();
message.obj = msg.getBytes();
message.what =1;
handler.sendMessage(message);
// handler.sendEmptyMessage(1);
} } catch (Exception e)
{
e.printStackTrace();
CLog.e(tag, e.toString());
// 错误消息,通过hander回传
if (handler != null)
{
Message message = new Message();
message.obj = msg.getBytes();
message.what = 0;;
handler.sendMessage(message); }
}
}
} synchronized (this)
{
try
{
wait(); } catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}// 发送完消息后,线程进入等待状态
}
} }
}
package com.example.socketblockdemo;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset; import android.content.Intent;
import android.text.TextUtils; /**
* 客户端读消息线程
*
* @author way
*
*/
public class SocketInputThread extends Thread
{
private boolean isStart = true; private static String tag = "socket"; // private MessageListener messageListener;// 消息监听接口对象 public SocketInputThread()
{
} public void setStart(boolean isStart)
{
this.isStart = isStart;
} @Override
public void run()
{
while (isStart)
{
// 手机能联网,读socket数据
if (NetManager.instance().isNetworkConnected())
{ if (!TCPClient.instance().isConnect())
{
CLog.e(tag, "TCPClient connet server is fail read thread sleep second" +Const.SOCKET_SLEEP_SECOND ); try
{
sleep(Const.SOCKET_SLEEP_SECOND * 1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} readSocket(); // 如果连接服务器失败,服务器连接失败,sleep固定的时间,能联网,就不需要sleep CLog.e("socket","TCPClient.instance().isConnect() " + TCPClient.instance().isConnect() ); }
}
} public void readSocket()
{
Selector selector = TCPClient.instance().getSelector();
if (selector == null)
{
return;
}
try
{
// 如果没有数据过来,一直柱塞
while (selector.select() > 0)
{
for (SelectionKey sk : selector.selectedKeys())
{
// 如果该SelectionKey对应的Channel中有可读的数据
if (sk.isReadable())
{
// 使用NIO读取Channel中的数据
SocketChannel sc = (SocketChannel) sk.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
try
{
sc.read(buffer);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
// continue;
}
buffer.flip();
String receivedString = "";
// 打印收到的数据
try
{
receivedString = Charset.forName("UTF-8")
.newDecoder().decode(buffer).toString(); CLog.e(tag, receivedString); Intent i = new Intent(Const.BC); i.putExtra("response", receivedString); MainActivity.s_context.sendBroadcast(i ); } catch (CharacterCodingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
buffer.clear();
buffer = null; try
{
// 为下一次读取作准备
sk.interestOps(SelectionKey.OP_READ);
// 删除正在处理的SelectionKey
selector.selectedKeys().remove(sk); } catch (CancelledKeyException e)
{
e.printStackTrace();
} }
}
}
// selector.close();
// TCPClient.instance().repareRead(); } catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClosedSelectorException e2)
{
}
} }
package com.example.socketblockdemo;

import java.io.IOException;

import android.text.TextUtils;

class SocketHeartThread extends Thread
{
boolean isStop = false;
boolean mIsConnectSocketSuccess = false;
static SocketHeartThread s_instance; private TCPClient mTcpClient = null; static final String tag = "SocketHeartThread"; public static synchronized SocketHeartThread instance()
{
if (s_instance == null)
{
s_instance = new SocketHeartThread();
}
return s_instance;
} public SocketHeartThread()
{
TCPClient.instance();
// 连接服务器
// mIsConnectSocketSuccess = connect(); } public void stopThread()
{
isStop = true;
} /**
* 连接socket到服务器, 并发送初始化的Socket信息
*
* @return
*/ private boolean reConnect()
{
return TCPClient.instance().reConnect();
} public void run()
{
isStop = false;
while (!isStop)
{
// 发送一个心跳包看服务器是否正常
boolean canConnectToServer = TCPClient.instance().canConnectToServer(); if(canConnectToServer == false){
reConnect();
}
try
{
Thread.sleep(Const.SOCKET_HEART_SECOND * 1000); } catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
package com.example.socketblockdemo;

import android.os.Handler;
import android.text.TextUtils; public class SocketThreadManager
{ private static SocketThreadManager s_SocketManager = null; private SocketInputThread mInputThread = null; private SocketOutputThread mOutThread = null; private SocketHeartThread mHeartThread = null; // 获取单例
public static SocketThreadManager sharedInstance()
{
if (s_SocketManager == null)
{
s_SocketManager = new SocketThreadManager();
s_SocketManager.startThreads();
}
return s_SocketManager;
} // 单例,不允许在外部构建对象
private SocketThreadManager()
{
mHeartThread = new SocketHeartThread();
mInputThread = new SocketInputThread();
mOutThread = new SocketOutputThread();
} /**
* 启动线程
*/ private void startThreads()
{
mHeartThread.start();
mInputThread.start();
mInputThread.setStart(true);
mOutThread.start();
mInputThread.setStart(true);
// mDnsthread.start();
} /**
* stop线程
*/
public void stopThreads()
{
mHeartThread.stopThread();
mInputThread.setStart(false);
mOutThread.setStart(false);
} public static void releaseInstance()
{
if (s_SocketManager != null)
{
s_SocketManager.stopThreads();
s_SocketManager = null;
}
} public void sendMsg(byte [] buffer, Handler handler)
{
MsgEntity entity = new MsgEntity(buffer, handler);
mOutThread.addMsgToSendList(entity);
} }
package com.example.socketblockdemo;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel; /**
* NIO TCP 客户端
*
*/
public class TCPClient
{
// 信道选择器
private Selector selector; // 与服务器通信的信道
SocketChannel socketChannel; // 要连接的服务器Ip地址
private String hostIp; // 要连接的远程服务器在监听的端口
private int hostListenningPort; private static TCPClient s_Tcp = null; public boolean isInitialized = false; public static synchronized TCPClient instance()
{
if (s_Tcp == null)
{ s_Tcp = new TCPClient(Const.SOCKET_SERVER,
Const.SOCKET_PORT);
}
return s_Tcp;
} /**
* 构造函数
*
* @param HostIp
* @param HostListenningPort
* @throws IOException
*/
public TCPClient(String HostIp, int HostListenningPort)
{
this.hostIp = HostIp;
this.hostListenningPort = HostListenningPort; try
{
initialize();
this.isInitialized = true;
} catch (IOException e)
{
this.isInitialized = false;
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e)
{
this.isInitialized = false;
e.printStackTrace();
}
} /**
* 初始化
*
* @throws IOException
*/
public void initialize() throws IOException
{
boolean done = false; try
{
// 打开监听信道并设置为非阻塞模式
socketChannel = SocketChannel.open(new InetSocketAddress(hostIp,
hostListenningPort));
if (socketChannel != null)
{
socketChannel.socket().setTcpNoDelay(false);
socketChannel.socket().setKeepAlive(true);
// 设置 读socket的timeout时间
socketChannel.socket().setSoTimeout(
Const.SOCKET_READ_TIMOUT);
socketChannel.configureBlocking(false); // 打开并注册选择器到信道
selector = Selector.open();
if (selector != null)
{
socketChannel.register(selector, SelectionKey.OP_READ);
done = true;
}
}
} finally
{
if (!done && selector != null)
{
selector.close();
}
if (!done)
{
socketChannel.close();
}
}
} static void blockUntil(SelectionKey key, long timeout) throws IOException
{ int nkeys = 0;
if (timeout > 0)
{
nkeys = key.selector().select(timeout); } else if (timeout == 0)
{
nkeys = key.selector().selectNow();
} if (nkeys == 0)
{
throw new SocketTimeoutException();
}
} /**
* 发送字符串到服务器
*
* @param message
* @throws IOException
*/
public void sendMsg(String message) throws IOException
{
ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("utf-8")); if (socketChannel == null)
{
throw new IOException();
}
socketChannel.write(writeBuffer);
} /**
* 发送数据
*
* @param bytes
* @throws IOException
*/
public void sendMsg(byte[] bytes) throws IOException
{
ByteBuffer writeBuffer = ByteBuffer.wrap(bytes); if (socketChannel == null)
{
throw new IOException();
}
socketChannel.write(writeBuffer);
} /**
*
* @return
*/
public synchronized Selector getSelector()
{
return this.selector;
} /**
* Socket连接是否是正常的
*
* @return
*/
public boolean isConnect()
{
boolean isConnect = false;
if (this.isInitialized)
{
isConnect = this.socketChannel.isConnected();
}
return isConnect;
} /**
* 关闭socket 重新连接
*
* @return
*/
public boolean reConnect()
{
closeTCPSocket(); try
{
initialize();
isInitialized = true;
} catch (IOException e)
{
isInitialized = false;
e.printStackTrace();
}
catch (Exception e)
{
isInitialized = false;
e.printStackTrace();
}
return isInitialized;
} /**
* 服务器是否关闭,通过发送一个socket信息
*
* @return
*/
public boolean canConnectToServer()
{
try
{
if (socketChannel != null)
{
socketChannel.socket().sendUrgentData(0xff);
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
catch (Exception e){
e.printStackTrace();
return false;
}
return true;
} /**
* 关闭socket
*/
public void closeTCPSocket()
{
try
{
if (socketChannel != null)
{
socketChannel.close();
} } catch (IOException e)
{ }
try
{
if (selector != null)
{
selector.close();
}
} catch (IOException e)
{
}
} /**
* 每次读完数据后,需要重新注册selector,读取数据
*/
public synchronized void repareRead()
{
if (socketChannel != null)
{
try
{
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
} catch (ClosedChannelException e)
{
e.printStackTrace(); } catch (IOException e)
{
e.printStackTrace();
}
}
}
}
如何使用

 // 发送消息,失败或者成功的handler
SocketThreadManager.sharedInstance().sendMsg(str.getBytes(), handler);
代码下载 http://files.cnblogs.com/likwo/SocketBlockDemo.zip

Android 通过Socket 和服务器通讯的更多相关文章

  1. Android连接socket服务器上传下载多个文件

    android连接socket服务器上传下载多个文件1.socket服务端SocketServer.java public class SocketServer { ;// 端口号,必须与客户端一致 ...

  2. Android端简易蓝牙聊天通讯App(原创)

    欢迎转载,但请注明出处!谢谢.http://www.cnblogs.com/weizhxa/p/5792775.html 最近公司在做一个蓝牙串口通讯的App,有一个固定的蓝牙设备,需要实现手机连接相 ...

  3. Android 基于Socket的聊天应用(二)

    很久没写BLOG了,之前在写Android聊天室的时候答应过要写一个客户(好友)之间的聊天demo,Android 基于Socket的聊天室已经实现了通过Socket广播形式的通信功能. 以下是我写的 ...

  4. Android 之 Socket 通信

    Android 之 Socket 通信 联系一下 Socket 编程,之后需要将一个 JavaEE 项目移植到 Android,暂时现尝试写一个简单的 DEMO,理解一下 Socket Server ...

  5. 基于android的Socket通信

    一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...

  6. Linux 下 简单客户端服务器通讯模型(TCP)

    原文:Linux 下 简单客户端服务器通讯模型(TCP) 服务器端:server.c #include<stdio.h> #include<stdlib.h> #include ...

  7. C# Socket的TCP通讯

    Socket的TCP通讯 一. socket的通讯原理 服务器端的步骤如下. (1)建立服务器端的Socket,开始侦听整个网络中的连接请求. (2)当检测到来自客户端的连接请求时,向客户端发送收到连 ...

  8. Android 通过SOCKET下载文件的方法

    本文实例讲述了Android通过SOCKET下载文件的方法.分享给大家供大家参考,具体如下: 服务端代码 import java.io.BufferedInputStream; import java ...

  9. Android USB Host与HID通讯

    前端时间捣鼓一个HID的硬件, 需要和android通信, 网上搜索了一圈,收获不小. 比较好的文章是:      Android USB Host与HID通讯 Android Service创建US ...

随机推荐

  1. iOS基础--UIView的常见属性

    UIView的常见属性以及方法 @property(nonatomic,readonly) UIView *superview; // 获得自己的父控件对象 @property(nonatomic,r ...

  2. 接口(interface)那点事

    1.接口(interface),在 java中有这个类型哦,这是语法哦. public interface MyInterface { } 语法还是很清晰的哦, 类的关键字是class.而接口改为in ...

  3. rails中render 和 redirect_to的区别, each只能用在数组中,如果只有一个或者零个项,用each方法会报错undefined method `each' for #...

    在render中,即使有:action,那么也仅仅是取对应的view中的模板(html.erb)而已,所以这里即使浏览器中的url是/orders/xcreate,但是显示的界面是/app/views ...

  4. rails中path、url路径解析,routes信息,form_for剖析,link_to示例,路由实例说明

    原创,转载请注明http://www.cnblogs.com/juandx/p/3963023.html  rails中path.url路径解析,routes信息,form_for剖析,link_to ...

  5. svg 添加超链接

    <svg>    <a xlink:href="http://www.w3.org//Graphics//SVG//Overview.htm8">      ...

  6. kcp协议详解

    kcp协议是传输层的一个具有可靠性的传输层ARQ协议.它的设计是为了解决在网络拥堵情况下tcp协议的网络速度慢的问题.kcp力求在保证可靠性的情况下提高传输速度.kcp协议的关注点主要在控制数据的可靠 ...

  7. android NavigationBar 显示

    android连接7inch屏时,虚拟按键会显示到右侧,变成一条黑边,并且只有back功能. 在连接10inch的时候,虚拟按键就正常,显示在屏幕的底部.有back,home,recent app三个 ...

  8. Python3.4下使用sqlalchemy

    一. 1.用sudo apt-get install python3-numpy之后,会默认把numpy安装到  /usr/lib/python3/dist-packages目录下,而且版本比较低. ...

  9. nodejs基础 -- 回调函数

    Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了. 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都 ...

  10. HDU 1556 Color the ball 树状数组 题解

    Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电动 ...