注:Log为日志打印 和 AsyncTask为异步请求,请参考我其它博客。
那就直接上代码吧,注释都写在代码里面了。
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* TCP socket 的*****伪代码*****
* 1.缺心跳包逻辑,像微信3.5分钟一次,主要是检测长连接是否在线,避免TCP的"半关闭"即服务器突然关机(TCP3个半还有半打开半连接)。
* 2.缺断线重连逻辑,比如心跳失败3次,发消息一直失败,断网复网,后台下发断客户端报文等会调。记住socket重连一定要new Socket();
* 3.一般IM流程是:账密登录拿到令牌,调socket连接,然后把令牌发给服务器,服务器回应,即长连接建立成功
* 4.发消息调socket.getOutputStream().write();可以写个堵塞队列ArrayBlockingQueue,然后一个死循环take发送报文去write,平时发消息只需要put进该队列
* 5.收消息定义100k的缓存区Buffer,死循环从socket.getInputStream().read(),一般先读第一个字节,如果异常就不再读,如果第一个字节正常,根据流大小一个一个字节的读出来,回调给上层
* 6.如果是仿xmpp,一般在上层组装好json,转成xml,然后转成String,然后转成byte,进入消息发送,接收消息反之。
* 7.如果报文过大也考虑拆包组包。
* 8.创建Socket关键点如下:
* socket.connect(new InetSocketAddress(host, port), 20000);// 这里TCP连接并不知道是否真正的成功,需要发一下报文,收到后台回应才算是真正的成功
* socket.setKeepAlive(true);// 定期检测连接是否断开,意义不大,因为TCP间隔2小时才检测一次
* socket.setSoTimeout(0);// 读取超时,设置为零不超时
* socket.setTcpNoDelay(true);// 避免粘包,每次只发一条报文,只收一条报文
* inputStream = socket.getInputStream();// 绑定socket输入出流
* outputStream = socket.getOutputStream();
*/
public class SocketClient implements MessageWriterProtocolListener, MessageReaderProtocolListener {
private String TAG = SocketClient.class.getSimpleName();
/**
* socket 连接
*/
protected Socket socket;
/**
* 输入流
*/
private InputStream inputStream;
/**
* 输出流
*/
private OutputStream outputStream;
/**
* 报文封装,以及发送
*/
private SocketWriterProtocol mMessageWriterProtocol;
/**
* 报文解析,以及接收
*/
private SocketReaderProtocol mMessageReaderProtocol;
/**
* 是否创建连接
*/
private boolean socketCreated = false;
/**
* 报文收发处理情况监听
*/
private SocketClientListener mIMSocketClientListener;
/**
* 创建连接
*/
public boolean connect() {
boolean bRet = false;
socketCreated = false;
onIMSocketClientState(SocketClientListener.Type.SOCKET_CREATE, SocketClientListener.State.DEF);
try {
createSocket();
initProtocol();
} catch (Exception e) {
e.printStackTrace();
}
if (socketCreated) {
Log.d(TAG, "connect socketCreated");
onIMSocketClientState(SocketClientListener.Type.SOCKET_CREATE_SUCCESSFUL, SocketClientListener.State.DEF);
} else {
onIMSocketClientState(SocketClientListener.Type.SOCKET_CREATE_FAIL, SocketClientListener.State.DEF);
}
return bRet;
}
/**
* 创建Socket.
*/
private void createSocket() throws Exception {
String host = "192.168.0.1";
int port = 8080;
Log.i(TAG, "创建Socket 主机地址:" + host + " 端口号:" + port);
try {
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), 20000);// 这里TCP连接并不知道是否真正的成功,需要发一下报文,收到后台回应才算是真正的成功
socket.setKeepAlive(true);// 定期检测连接是否断开,意义不大,因为TCP间隔2小时才检测一次
socket.setSoTimeout(0);// 读取超时,设置为零不超时
socket.setTcpNoDelay(true);// 避免粘包,每次只发一条报文,只收一条报文
inputStream = socket.getInputStream();// 绑定socket输入出流
outputStream = socket.getOutputStream();
Log.d(TAG, "创建Socket成功 host= " + host + "\n port= " + port);
} catch (UnknownHostException uhe) {
Log.e(TAG, "创建Socket失败 UnknownHostException 错误信息:" + uhe.getMessage());
throw uhe;
} catch (Exception ioe) {
Log.e(TAG, "创建Socket失败 错误信息:" + ioe.getMessage());
throw ioe;
}
}
public void disconnect() throws Exception {
Log.d(TAG, "正在关闭长链接");
socketCreated = false;
if (mMessageWriterProtocol != null) {
mMessageWriterProtocol.shutdown();
}
if (mMessageReaderProtocol != null) {
mMessageReaderProtocol.shutdown();
}
closeSocket();
Log.d(TAG, "长连接已经成功关闭");
}
private void closeSocket() {
Log.d(TAG, "closeSocket");
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable ignore) { /* ignore */
}
inputStream = null;
}
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable ignore) { /* ignore */
}
outputStream = null;
}
if (socket != null) {
try {
socket.close();
} catch (Exception e) { /* ignore */
}
socket = null;
}
}
/**
* 初始化协议连接
*/
protected void initProtocol() throws Exception {
Log.d(TAG, "initProtocol");
try {
mMessageWriterProtocol = new SocketWriterProtocol(outputStream, this);
mMessageReaderProtocol = new SocketReaderProtocol(inputStream, this);
socketCreated = true;
} catch (Exception ex) {
// An exception occurred in setting up the connection. Make sure we
// shut down the
// readers and writers and close the socket.
if (mMessageWriterProtocol != null) {
mMessageWriterProtocol.shutdown();
mMessageWriterProtocol = null;
}
if (mMessageReaderProtocol != null) {
mMessageReaderProtocol.shutdown();
mMessageReaderProtocol = null;
}
closeSocket();
socketCreated = false;
throw ex;
}
}
public boolean isConnected() {
Log.d(TAG, "isConnected");
return socketCreated;
}
/**
* 发送协议包
*
* @param protocolPacket
* @since 3.0.0
*/
public boolean sendPacket(IMessageProtocol protocolPacket) {
boolean isRet = false;
if (!isConnected()) {
Log.w(TAG, "正在处理发送给服务器的报文 连接不存在无法发送");
return isRet;
}
try {
mMessageWriterProtocol.sendIMProtocol(protocolPacket);
isRet = true;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "正在处理发送给服务器的报文 发送时出错:" + e.toString() + " message:" + protocolPacket.getData());
}
return isRet;
}
/**
* 响应socket状态.
*
* @param state
*/
private void onIMSocketClientState(int type, int state) {
if (mIMSocketClientListener != null) {
mIMSocketClientListener.onIMSocketClientState(type, state);
}
}
@Override
public void onIMProtocolReaderSuccess(IMessageProtocol imProtocol) {
// 读成功
onIMProtocolSendState(imProtocol, SocketClientListener.Type.IMPROTOCOL_SEND_SUCCESS);
// 处理接收到后台服务器返回的报文
processIMProtocol(imProtocol);
}
@Override
public void onIMProtocolReaderError(int code) {
// 读失败
try {
Log.d(TAG, "disconnect !!!!");
disconnect();
} catch (Exception e) {
e.printStackTrace();
}
onIMSocketClientState(SocketClientListener.Type.SOCKET_DISCONNECT_BY_SERVER, SocketClientListener.State.DEF);
}
@Override
public void onIMProtocolWriterSuccess(IMessageProtocol imProtocol) {
// 写成功
onIMProtocolSendState(imProtocol, SocketClientListener.Type.IMPROTOCOL_SEND_SUCCESS);
}
@Override
public void onIMProtocolWriterError(IMessageProtocol imProtocol) {
// 写失败
onIMProtocolSendState(imProtocol, SocketClientListener.Type.IMPROTOCOL_SEND_FAIL);
}
private void onIMProtocolSendState(IMessageProtocol imProtocol, int code) {
// 读写回调
}
/**
* 处理接收到后台服务器返回的报文
*/
private void processIMProtocol(IMessageProtocol imProtocol) {
}
}
import java.nio.ByteBuffer;
public class BaseMessageProtocolImpl implements IMessageProtocol{
private byte[] dataBytes;
@Override
public void setData(byte[] bytes) {
dataBytes = bytes;
}
@Override
public byte[] getData() {
return dataBytes;
}
@Override
public byte[] getIMProtocolBytes() {
int length;
if (dataBytes == null) {
length = 0;
} else {
length = dataBytes.length;
}
int size = length;
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
// byteBuffer.put(version);加上这些必须扩大size
// byteBuffer.put(type);
// byteBuffer.put(cL);
if (dataBytes != null) {
byteBuffer.put(dataBytes);
}
return byteBuffer.array();
}
}
import java.nio.ByteBuffer;
public class BaseMessageProtocolImpl implements IMessageProtocol{
private byte[] dataBytes;
@Override
public void setData(byte[] bytes) {
dataBytes = bytes;
}
@Override
public byte[] getData() {
return dataBytes;
}
@Override
public byte[] getIMProtocolBytes() {
int length;
if (dataBytes == null) {
length = 0;
} else {
length = dataBytes.length;
}
int size = length;
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
// byteBuffer.put(version);加上这些必须扩大size
// byteBuffer.put(type);
// byteBuffer.put(cL);
if (dataBytes != null) {
byteBuffer.put(dataBytes);
}
return byteBuffer.array();
}
}
/**
* TCP报文接口类
*/
public interface IMessageProtocol {
/**
* 设置数据内容
*
* @param bytes
*/
void setData(byte[] bytes);
/**
* 获取数据内容
*
* @return
*/
byte[] getData();
byte[] getIMProtocolBytes();
}
/**
* 读取协议监听器.
* 用于监听Socket协议层报文解析结果或错误信息
*/
public interface MessageReaderProtocolListener {
/**
* 输入流结束,此时需要重新建立socket连接
*/
int ERROR_INPUTSTREAM_END = 2;
/**
* 响应协议报文接收.
*
* @param imProtocol
*/
void onIMProtocolReaderSuccess(IMessageProtocol imProtocol);
/**
* 响应协议解析错误.
*
* @param code
*/
void onIMProtocolReaderError(int code);
}
/**
* 写入协议监听器.
* 用于监听将协议写入Socket的状态
*/
public interface MessageWriterProtocolListener {
/**
* 响应协议报文接收.
* @param imProtocol
* @since 3.0.0
*/
void onIMProtocolWriterSuccess(IMessageProtocol imProtocol);
/**
* 响应协议报文接收.
* @param imProtocol
* @since 3.0.0
*/
void onIMProtocolWriterError(IMessageProtocol imProtocol);
}
import java.io.IOException;
public interface SocketClientListener {
interface Type {
/**
* 正在创建socket
*/
int SOCKET_CREATE = 1;
/**
* 创建socket成功
*/
int SOCKET_CREATE_SUCCESSFUL = 2;
/**
* 创建socket失败
*/
int SOCKET_CREATE_FAIL = 3;
/**
* 连接socket被服务器断开
*/
int SOCKET_DISCONNECT_BY_SERVER = 9;
/**
* 发送失败
*/
int IMPROTOCOL_SEND_FAIL = 7;
/**
* 发送成功
*/
int IMPROTOCOL_SEND_SUCCESS = 8;
/**
* 解析失败
*/
int ONIMPROTOCOL_PARSE_ERROR = 10;
}
interface State {
int DEF = 1;
/**
* 超时
*/
int FAIL_TIMEOUT = 1;
/**
* 使用登陆的loginsession失效
*/
int FAIL_LOGINSESSION_UNAVAILABLE = 5;
}
/**
* 响应协议发送.
*
* @param protocolPacket
* @param state 发送状态
*/
void onIMProtocolSendState(IMessageProtocol protocolPacket, int state);
/**
* 响应协议接收.
*
* @param protocolPacket
*/
void onIMProtocolReceive(IMessageProtocol protocolPacket) throws IOException;
/**
* 响应Socket连接状态.
*
* @param type
* @param state
*/
void onIMSocketClientState(int type, int state);
}
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Future;
/**
* 报文读取协议
*/
public class SocketReaderProtocol {
private static final String TAG = SocketReaderProtocol.class.getSimpleName();
private Future<?> readerThread;
private InputStream inputStream;
private MessageReaderProtocolListener readerProtocolListener;
/**
* 当前协议对象
*/
private IMessageProtocol currentIMProtocol = null;
private boolean done = false;
/**
* 构造协议读取器
*
* @param inputStream 从Socket中得到的输入流
* @param readerProtocolListener 监听协议解析结果,如果从流中解析到一个完整的协议报文,则将其从此监听器返回.
*/
public SocketReaderProtocol(InputStream inputStream, MessageReaderProtocolListener readerProtocolListener) {
super();
done = false;
this.inputStream = inputStream;
this.readerProtocolListener = readerProtocolListener;
readerThread = AsyncTask.getInstance().submit(new Runnable() {
@Override
public void run() {
parse();
}
});
}
/**
* 解析报文
*/
private void parse() {
IMessageByteBuffer byteBuffer = new IMessageByteBuffer(102400);
while (!done) {
try {
Log.i(TAG, "waiting for receive data...");
int firstByte = inputStream.read();
Log.w(TAG, "firstByte:" + firstByte);
if (firstByte == -1) {
Log.e(TAG, "inputStream read end ~! " + inputStream.toString());
done = true;
onIMProtocolReaderError(MessageReaderProtocolListener.ERROR_INPUTSTREAM_END);
break;
}
byteBuffer.put((byte) firstByte);
//读写操作前先得知数据流里有多少个字节可以读取
int count = inputStream.available();
if (count != 0) {
byteBuffer.put(readCountFromInputStream(count));
}
parseNextProtocol(byteBuffer);
} catch (Exception e) {
done = true;
e.printStackTrace();
Log.e(TAG, "error " + e.toString());
readerProtocolListener.onIMProtocolReaderError(MessageReaderProtocolListener.ERROR_INPUTSTREAM_END);
break;
}
}
}
/**
* 从inputStream中读取指定字节.
*
* @param count
* @return
* @throws IOException
*/
private byte[] readCountFromInputStream(int count)
throws IOException {
byte[] bytes = new byte[count];
int readCount = 0; // 已经成功读取的字节的个数
while (readCount < count) {
readCount += inputStream.read(bytes, readCount, count
- readCount);
}
return bytes;
}
/**
* 从缓冲区获取指定长度的字节.
*
* @param byteBuffer
* @param length
* @return
*/
private byte[] getByteFromBuffer(IMessageByteBuffer byteBuffer, int length) {
if (byteBuffer.availableCount() < length) {
return null;
} else {
byte[] bs = new byte[length];
byteBuffer.get(bs);
return bs;
}
}
/**
* 从缓冲区解析下一个协议包.
*
* @param byteBuffer
* @return
*/
private boolean parseNextProtocol(IMessageByteBuffer byteBuffer) {
if (currentIMProtocol == null) {
currentIMProtocol = (IMessageProtocol) byteBuffer;
if (currentIMProtocol == null) {
return false;
}
}
Log.i(TAG, "接收到后台报文" + currentIMProtocol.toString());
onIMProtocolReaderSucess(currentIMProtocol);
//解析成功,则解析下一个协议
currentIMProtocol = null;
if (byteBuffer.availableCount() > 0) {
if (parseNextProtocol(byteBuffer)) {
parseNextProtocol(byteBuffer);
return true;
} else {
return false;
}
} else {
//数据缓冲区已经没有可读数据,则清空
byteBuffer.clear();
return false;
}
}
public boolean isDone() {
return done;
}
public void shutdown() {
this.done = true;
}
/**
* 响应协议报文接收.
*
* @param imProtocol
*/
void onIMProtocolReaderSucess(IMessageProtocol imProtocol) {
if (readerProtocolListener != null) {
readerProtocolListener.onIMProtocolReaderSuccess(imProtocol);
}
}
/**
* 响应协议解析错误.
*
* @param code
*/
void onIMProtocolReaderError(int code) {
if (readerProtocolListener != null) {
readerProtocolListener.onIMProtocolReaderError(code);
}
}
}
import java.io.OutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Future;
/**
* 报文写入协议
*/
public class SocketWriterProtocol {
public static final String TAG = SocketWriterProtocol.class.getSimpleName();
private Future<?> writerThread;
private OutputStream outputStream;
private final BlockingQueue<IMessageProtocol> queue;
private MessageWriterProtocolListener writerProtocolListener;
private boolean done = false;
public SocketWriterProtocol(OutputStream outputStream, MessageWriterProtocolListener writerProtocolListener) {
super();
done = false;
//put(E e):把 e 加到 BlockingQueue 里,如果 BlockQueue 没有空间,则调用此方法的线程被阻断直到 BlockingQueue 里面有空间再继续
//take():取走 BlockingQueue 里排在首位的对象,若 BlockingQueue 为空,阻断进入等待状态直到 Blocking 有新的对象被加入为止
this.queue = new ArrayBlockingQueue<IMessageProtocol>(500, true);
this.outputStream = outputStream;
this.writerProtocolListener = writerProtocolListener;
writerThread = AsyncTask.getInstance().submit(new Runnable() {
@Override
public void run() {
doSend();
}
});
}
private void doSend() {
while (!done) {
IMessageProtocol imProtocol = null;
try {
imProtocol = nextIMProtocol();
if (imProtocol != null) {
outputStream.write(imProtocol.getIMProtocolBytes());
outputStream.flush();
onIMProtocolWriterSucess(imProtocol);
}
} catch (Exception e) {
Log.e(TAG, "Socket写入流报错 Socket即将断开:" + e.toString());
done = true;
e.printStackTrace();
onIMProtocolWriterError(imProtocol, e);
break;
}
}
}
/**
* 发送IM协议报文.
*
* @param imProtocol
*/
public void sendIMProtocol(IMessageProtocol imProtocol) {
try {
queue.put(imProtocol);
} catch (InterruptedException ie) {
Log.e(TAG, "正在处理发送给服务器的报文 发送时出错:" + ie.toString());
ie.printStackTrace();
onIMProtocolWriterError(imProtocol, ie);
return;
}
}
/**
* @return
*/
private IMessageProtocol nextIMProtocol() {
IMessageProtocol imProtocol = null;
try {
imProtocol = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return imProtocol;
}
public boolean isDone() {
return done;
}
public void shutdown() {
this.done = true;
writerThread.cancel(true);
}
/**
* 响应协议报文接收.
*
* @param imProtocol
*/
void onIMProtocolWriterSucess(IMessageProtocol imProtocol) {
Log.i(TAG, "正在处理发送给服务器的报文 报文发送成功 [V:" + imProtocol.getData().toString());
if (writerProtocolListener != null) {
writerProtocolListener.onIMProtocolWriterSuccess(imProtocol);
}
}
/**
* 响应协议报文接收.
*
* @param imProtocol
*/
void onIMProtocolWriterError(IMessageProtocol imProtocol, Exception e) {
Log.e(TAG, "正在处理发送给服务器的报文 发送时出错:" + e.toString());
if (writerProtocolListener != null) {
writerProtocolListener.onIMProtocolWriterError(imProtocol);
}
}
}