zoukankan      html  css  js  c++  java
  • socket编程解决粘包和丢包问题

    ##socket 丢包粘包解决方式

    采用固定头部长度(一般为4个字节),包头保存的是包体的长度

    header+body

    包头+包体 

    下面的例子不是按照上图中规定的格式编写的,但是思路都是一样的,先读出一个包头,得到包体的长度,解析出包体

    public class SocketServer {
        public static void main(String args[]) {
            ServerSocket serverSocket;
            try {
                serverSocket = new ServerSocket();
                serverSocket.bind(new InetSocketAddress(8089));
                System.out.println("启动服务端~");
                while (true) {
                    Socket socket = serverSocket.accept();
                    new ReceiveThread(socket).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        static class ReceiveThread extends Thread {
            public static final int PACKET_HEAD_LENGTH = 4;// 包头长度
            private Socket socket;
            private volatile byte[] bytes = new byte[0];
    
            public ReceiveThread(Socket socket) {
                this.socket = socket;
            }
    
            //将b数组 下标从begin到end-1的值追加到a数组的后面,并返回
            public byte[] mergebyte(byte[] a, byte[] b, int begin, int end) {
                byte[] add = new byte[a.length + end - begin];
                int i = 0;
                for (i = 0; i < a.length; i++) {
                    add[i] = a[i];
                }
                for (int k = begin; k < end; k++, i++) {
                    add[i] = b[k];
                }
                return add;
            }
    
            @Override
            public void run() {
                int count = 0;
                while (true) {
                    try {
                        InputStream reader = socket.getInputStream();
                        {  //这里可以保证正好读取到4个字节的包头
                            if (bytes.length < PACKET_HEAD_LENGTH) { //【第一次进来,或者经过一次循环bytes的长度被置为0】
                                byte[] head = new byte[PACKET_HEAD_LENGTH - bytes.length];
                                int couter = reader.read(head);
                                if (couter < 0) {
                                    continue;
                                }
                                bytes = mergebyte(bytes, head, 0, couter);
                                if (couter < PACKET_HEAD_LENGTH) {
                                    continue;
                                }
                            }
                        }
                        
                        // 取出包体长度
                        byte[] temp = new byte[0];
                        temp = mergebyte(temp, bytes, 0, PACKET_HEAD_LENGTH);
                        int bodylength = ByteUtil.byteArrayToInt(temp);// 包体长度
                        
                        //完整读取一个包
                        if (bytes.length < bodylength + PACKET_HEAD_LENGTH) {// 不够一个包
                            byte[] body = new byte[bodylength + PACKET_HEAD_LENGTH - bytes.length];// 剩下应该读的字节(凑一个包)
                            int couter = reader.read(body);
                            if (couter < 0) {
                                continue;
                            }
                            bytes = mergebyte(bytes, body, 0, couter);
                            if (couter < body.length) {
                                continue;
                            }
                        }
                        
                        //把包体的内容读取出来
                        byte[] body = new byte[0];
                        body = mergebyte(body, bytes, PACKET_HEAD_LENGTH, bytes.length);
                        count++;
                        System.out.println("server receive body:  " + count + new String(body));
                        //为读取下一个包将数组长度重置为空数组,长度为0
                        bytes = new byte[0];
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public class ClientSocket {
        public static void main(String args[]) throws IOException {
            System.out.println("启动客户端~");
            Socket clientSocket = new Socket();
            clientSocket.connect(new InetSocketAddress(8089));
            new SendThread(clientSocket).start();
     
        }
     
        static class SendThread extends Thread {
            Socket socket;
            public SendThread(Socket socket) {
                this.socket = socket;
            }
     
            @Override
            public void run() {
                String reqMessage = "HelloWorl !  from clientsocket this is test half packages!";
                for (int i = 0; i < 100; i++) {
                    sendPacket(reqMessage+ "u "+ i);
                }
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
     
            }
     
            public void sendPacket(String message) {
                byte[] contentBytes = message.getBytes();// 包体内容
                int contentlength = contentBytes.length;// 包体长度
                byte[] headbytes = ByteUtil.intToByteArray(contentlength);// 包头字节数组
                byte[] bytes = new byte[headbytes.length + contentlength];// 包=包头+包体
                int i = 0;
                for (i = 0; i < headbytes.length; i++) {// 包头
                    bytes[i] = headbytes[i];
                }
                for (int j = i, k = 0; k < contentlength; k++, j++) {// 包体
                    bytes[j] = contentBytes[k];
                }
                try {
                    OutputStream writer = socket.getOutputStream();
                    writer.write(bytes);
                    writer.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
     
    }
    public class ByteUtil {
    
        public static void main(String[] args) {
            byte[] res =  intToByteArray(10);
            System.out.println(byteArrayToInt(res));
            
        }
        
        public static byte[] intToByteArray(int i) {  
            byte[] result = new byte[4];  
            // 由高位到低位  
            result[0] = (byte) ((i >> 24) & 0xFF);  
            result[1] = (byte) ((i >> 16) & 0xFF);  
            result[2] = (byte) ((i >> 8) & 0xFF);  
            result[3] = (byte) (i & 0xFF);  
            return result;  
        } 
        
        public static int byteArrayToInt(byte[] bytes) {  
            int value = 0;  
            // 由高位到低位  
            for (int i = 0; i < 4; i++) {  
                int shift = (4 - 1 - i) * 8;  
                value += (bytes[i] & 0x000000FF) << shift;// 往高位游  
            }  
            return value;  
        } 
    }

    转自: https://blog.csdn.net/nongfuyumin/article/details/78298380?utm_source=blogxgwz5

  • 相关阅读:
    Linux基础命令---sudo
    Linux基础命令---yes
    Linux基础命令---shutdown
    Divide Two Integers
    java 判断两个数是否异号
    字节顺序的详细解释(转)
    java无符号移位(>>>)和有符号移位(>>)
    java 参数化类型
    c++事件内核对象(event)进程间激活(转)
    windows多线程编程(一)(转)
  • 原文地址:https://www.cnblogs.com/moris5013/p/10503507.html
Copyright © 2011-2022 走看看