从TCP socket开始写一个即时聊天IM,以下是“socket核心代码块的伪代码”,仅供参考

注: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);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值