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

原文地址:https://www.cnblogs.com/moris5013/p/10503507.html

时间: 2024-10-10 13:12:52

java socket编程解决粘包和丢包问题的相关文章

python/socket编程之粘包

python/socket编程之粘包 粘包: 只有TCP有尿包现象,UDP永远不会粘包. 首先需要掌握一个socket收发消息的原理 发送端可以是1k,1k的发送数据而接受端的应用程序可以2k,2k的提取数据,当然也有可能是3k或者多k提取数据,也就是说,应用程序是不可见的,因此TCP协议是面来那个流的协议,这也是容易出现粘包的原因而UDP是面向笑死的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任一字节的数据,这一点和TCP是很同的.怎样定义消息呢?认为对方一次

如何为可扩展系统进行Java Socket编程

从简单I/O到异步非阻塞channel的Java Socket模型演变之旅 上世纪九十年代后期,我在一家在线视频游戏工资工作,在哪里我主要的工作就是编写Unix Unix Berkley Socket和Windows WinSock代码.我的任务是确保视频游戏客户端和一个游戏服务器通信.很幸运有这样的机会写一些Java Socket代码,我对Java流式网络编程和简洁明了的API着迷.这一点都不让人惊讶,Java最初就是设计促进智能设备之间的通信,这一点很好的转移到了桌面应用和服务器应用. 19

【转】Java Socket编程

原文地址:Java Socket编程 Java Socket编程 对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信.这样就有两个Socket了,客户端和服务端各一个. 对于Socket之间的通

Java Socket编程详细解说

Java Socket编程 JavaSocketServerSocket乱码超时 Java Socket编程 对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信.这样就有两个Socket了,客户

Java Socket编程基础篇

原文地址:Java Socket编程----通信是这样炼成的 Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket.像大家熟悉的QQ.MSN都使用了Socket相关的技术.下面就让我们一起揭开Socket的神秘面纱. Socket编程 网络基础知识点: 两台计算机间进行通讯需要以下三个条件 IP地址.协议.端口号: IP地址:定位应用所在机器的网络位置.(比如家庭住址:北京市朝阳区XX街道XX小区) 端口号

20182332 实验四《Java Socket编程 》实验报告

20182332 实验肆<数据结构与面向对象程序设计>实验报告 课程:<程序设计与数据结构> 班级: 1823 姓名: 盛国榕 学号:20182332 实验教师:王志强 实验日期:2019年9月30日 必修/选修: 必修 1.实验内容 (一)Java Socket编程 1.学习蓝墨云上教材<Java和Android编程>"第16章 输入/输出 "和"第22章 网络",学习JavaSocket编程 2.结对编程.结对伙伴A编写客户端

Java Socket编程

对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信.这样就有两个Socket了,客户端和服务端各一个. 客户端写服务端读 服务端代码 public class Server { public s

Java Socket编程readLine返回null,read返回-1的条件

客户端正常关闭socket的时候,服务器端的readLine()方法会返回null,或者read()方法会返回-1 Java Socket编程readLine返回null,read返回-1的条件,布布扣,bubuko.com

【Java】Java Socket编程(1)基本的术语和概念

计算机程序能够相互联网,相互通讯,这使一切都成为可能,这也是当今互联网存在的基础.那么程序是如何通过网络相互通信的呢?这就是我记录这系列的笔记的原因.Java语言从一开始就是为了互联网而设计的,它为实现程序的相互通信提供了许多有用API,这类应用编程接口被称为套接字(Socket).在开始学习Java Socket之前我们需要先来了解一下基本的术语和概念. 1.计算机网络 计算机网络由一组通过通信信道(Communication channel)相互连接的机器组成.这些机器被称为:主机(host