一个简单的Socket分包程序

socket以流的形式传输不间断的数据,所以呢当传输多个数据包是需要完整的读取消息包内的数据。
为了完整的读取消息,这里我写了个简单的demo,数据包协议为
内容长度+内容。
直接上代码
服务端:

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;


public class SocketStreamServer {

    private boolean running = false;
    private ServerSocket serverSocket;
    private OnMessageListener messageListener;

    private AtomicInteger mConnections = new AtomicInteger(0);

    private static final Executor sThreadPools = Executors.newCachedThreadPool();

    public SocketStreamServer(int port, OnMessageListener messageListener) throws IOException {
        serverSocket = new ServerSocket(port);
        running = true;
        this.messageListener = messageListener;
    }


    public void start() {
        sThreadPools.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        Socket socket = serverSocket.accept();
                        runSocket(socket);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }


    public int getActiveConnetions() {
        return mConnections.get();
    }

    private void runSocket(final Socket socket) {
        sThreadPools.execute(new Runnable() {
            @Override
            public void run() {
                mConnections.incrementAndGet();
                handleReaderServer(socket);
            }
        });
    }

    private void handleReaderServer(final Socket socket) {
        InputStream inputStream = null;
        try {

            inputStream = socket.getInputStream();

            byte[] buff = new byte[8];

            int msgTotalLen = 0; //当前包消息总长度
            int currMsgLen = 0; //已读消息长度

            byte[] remaining = null; //上次读取完成后下个包部分

            ByteBuffer msgBuffer = null; //已读消息部分

            while (true) {

                int len = inputStream.read(buff);

                if (msgTotalLen == 0) {
                    //新的包开始

                    byte[] rF = null;
                    int rlen = 0;

                    if (remaining != null) {
                        //有上次没有读完的部分,合并一起读

                        rlen = remaining.length + len;
                        rF = new byte[rlen];

                        //合并插入最前面
                        System.arraycopy(remaining, 0, rF, 0, remaining.length);
                        System.arraycopy(buff, 0, rF, remaining.length, len);

                        remaining = null;

                    } else {
                        rF = buff;
                        rlen = len;
                    }

                    msgTotalLen = readMsgHeaderLength(rF, 0);

                    if (msgTotalLen + 4 < rlen) {
                    //用于处理一次读取到的流里面包含多个包的情况

                        boolean canFullRead = true;

                        int lastLen = 0;
                        while (canFullRead) {

                            msgBuffer = ByteBuffer.allocate(msgTotalLen);
                            msgBuffer.put(Arrays.copyOfRange(rF, 4 + lastLen, msgTotalLen + 4 + lastLen));
                            onRecvMsg(msgBuffer);

                            lastLen += msgTotalLen + 4;

                            if (lastLen + 4 < rlen) {

                                msgTotalLen = readMsgHeaderLength(rF, lastLen);

                                if (lastLen + msgTotalLen + 4 < rlen) {
                                    canFullRead = true;
                                } else {
                                    canFullRead = false;
                                    remaining = Arrays.copyOfRange(rF, lastLen, rlen);
                                }
                            } else {
                                canFullRead = false;

                                remaining = Arrays.copyOfRange(rF, lastLen, rlen);
                            }

                        }

                        msgTotalLen = 0;
                        currMsgLen = 0;

                    } else {

                        msgBuffer = ByteBuffer.allocate(msgTotalLen);
                        msgBuffer.put(Arrays.copyOfRange(rF, 4, rlen));

                        currMsgLen += (rlen - 4);
                    }

                } else {

                    if (currMsgLen + len >= msgTotalLen) {
                        //当前包要读完了

                        int n = msgTotalLen - currMsgLen; //计算当前数据包分割点

                        msgBuffer.put(buff, 0, n);
                        onRecvMsg(msgBuffer);

                        msgTotalLen = 0;
                        currMsgLen = 0;

                        if (n < len) {
                            //保存下次继续读
                            remaining = Arrays.copyOfRange(buff, n, len);
                        } else {
                            remaining = null;
                        }

                    } else {
                        //没有读完,继续
                        currMsgLen += len;
                        msgBuffer.put(buff);
                    }

                }
            }


        } catch (Exception e) {
            e.printStackTrace();

        } finally {

            mConnections.decrementAndGet();

            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    protected void onRecvMsg(ByteBuffer byteBuffer) {
        byteBuffer.flip();
        if (messageListener != null) {
            messageListener.onRecv(byteBuffer.array());
        }
    }

    private int readMsgHeaderLength(byte[] array, int startPosition) {
        return Utils.byteArrayToInt(Arrays.copyOfRange(array, startPosition, startPosition + 4));
    }

    public void stopServer() {
        running = false;
    }

    public interface OnMessageListener {
        void onRecv(byte[] bytes);
    }

}

客户端:

import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;


public class MySocketServer {

    private static final int PORT=44560;

    public static void main(String[] args) {

        try {

            new SocketStreamServer(PORT, new SocketStreamServer.OnMessageListener() {
                @Override
                public void onRecv(byte[] bytes) {
                    System.err.println("onRecv -->  "+new String(bytes));
                }
            }).start();

            startClient();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static void startClient() {
        new Thread(new Runnable() {
            @Override
            public void run() {

                try {

                    //数据包格式为 内容长度(int)+内容

                    Socket socket = new Socket("127.0.0.1", PORT);
                    final OutputStream stream = socket.getOutputStream();
                    Random random = new Random();
                    Scanner scanner=new Scanner(System.in);

                    while (true) {


//                        String s = getString(random.nextInt(10) + 1);
//                        System.out.println("client out: " + s);

                        System.out.println("input:");
                        String s= scanner.nextLine();
                        stream.write(encodeData(s.getBytes())); //发包
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

    public static byte[] encodeData(byte[] msgData) {
        int len = msgData.length;
        return Utils.mergeArray(Utils.intToByteArray(len),msgData);
    }
}
public class Utils {

    public static byte[] intToByteArray(int value) {
        return new byte[]{
                (byte) (value >>> 24),
                (byte) (value >>> 16),
                (byte) (value >>> 8),
                (byte) value};
    }


    public static int byteArrayToInt(byte[] array) {
        return array[0] << 24 | (array[1] & 0xFF) << 16 | (array[2] & 0xFF) << 8 | (array[3] & 0xFF);
    }

    public static byte[] mergeArray(byte[]... bytes){
        int count=0;
        for (byte[] arr:bytes){
            count+=arr.length;
        }
        byte[] newBytes=new byte[count];
        int destPos=0;
        for (byte[] arr:bytes){
            System.arraycopy(arr,0,newBytes,destPos,arr.length);
            destPos+=arr.length;
        }
        return newBytes;
    }
}

运行之后,每次发送的包都会在头部4位表示里面消息长度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值