java socket产生半包粘包问题

因为传输一段数据有tcp协议,底层链路层协议的等限制。本次单个tcp包最大的容量为1514个字节【网络】什么是MTU|MTU 优化|最大传输单元_bandaoyu的博客-CSDN博客_mtu最大可设置多大

 比如TCP上传一张图片时,拆分成了多个tcp包

可看出应用层数据字节长度是1460,链路层头+ip层头+tcp层头 字节长度是54

 应用层数据会先放入缓存等待应用程序读取

 模拟服务端

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author yaoct
 * @date 2023/1/12 15:03
 * @description:
 */
public class Server {
    public static void main(String[] args) {
        while (true){
            ServerSocket serverSocket = null;
            Socket socket = null;
            InputStream is = null;
            ByteArrayOutputStream baos = null;
            try {
                //1、创建服务端的ServerSocket,指明自己的端口号
                serverSocket = new ServerSocket(8088);
                serverSocket.setReceiveBufferSize(25000);
                //2、调用accept接收到来自于客户端的socket
                socket = serverSocket.accept();//阻塞式监听,会一直等待客户端的接入,接入了之后才会显示消息
                socket.setReceiveBufferSize(25000);
                System.out.println("接受缓冲区"+socket.getReceiveBufferSize());
                System.out.println("有连接!!!!!!!!!");
                //3、获取socket的输入流
                is = socket.getInputStream();


                //4、读取输入流中的数据
                //ByteArrayOutputStream的好处是它可以根据数据的大小自动扩充
                baos = new ByteArrayOutputStream();
                int len=0;
                byte[] buffer = new byte[65536];

                //判断是否将客户端发的消息读完了
                while ((len=is.read(buffer))!=-1){
                    System.out.println(len);
//                    baos.write(buffer,0,len);
                }
                System.out.println("收到了来自于客户端"+
                        socket.getInetAddress().getHostName()
                        +"的消息:"+baos.size());
//                System.out.println("收到了来自于客户端"+
//                        socket.getInetAddress().getHostName()
//                        +"的消息:"+baos.size()+baos.toString());


            } catch (IOException e) {
                e.printStackTrace();
            } finally {//5、关闭资源
                if(serverSocket!=null){
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(is!=null){
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(baos!=null){
                    try {
                        baos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


    }
}

客户端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {
        //socket对象初始化
        Socket socket = null;

        //输出流 os对象初始化
        OutputStream os = null;
        InputStream inputStream=null;
        try {

            //1、创建Socket对象,它的第一个参数需要的是服务端的IP,第二个参数是服务端的端口
            InetAddress inet = InetAddress.getByName("192.168.5.11");
            socket = new Socket(inet,8088);//inet是服务端ip

            //2、获取一个输出流,用于写出要发送的数据
            os = socket.getOutputStream();
            System.out.println("开始!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            StringBuilder sb=new StringBuilder();
            for(int i=0;i<10000;i++){
                sb.append('b');
            }
            //3、写出数据
//            for(int i=0;i<2;i++){
//                os.write(sb.toString().getBytes());
//            }
//            os.flush();
            for(int i=0;i<100;i++){
                os.write(sb.toString().getBytes());os.write(sb.toString().getBytes());

            }
            os.write(sb.toString().getBytes());
//            try {
                os.flush();
//                Thread.sleep(10010);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            os.write(sb.toString().getBytes());
//            inputStream = socket.getInputStream();
//            StringBuilder sb=new StringBuilder();
//            byte[] buffer=new byte[1024];
//            int len=0;
//            while ((len=inputStream.read(buffer))!=-1){
//                for(int i=0;i<len;i++){
//                    sb.append((char)buffer[i]);
//                }
//            }
//            System.out.println(sb.length());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、释放资源,别忘了哦!!!!
            if(socket!=null){
                try {
                    socket.close();//关闭
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();//关闭
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream!=null){
                try {
                    inputStream.close();//关闭
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

服务端响应数据:

接受缓冲区15000
有连接!!!!!!!!!
10000
10220
10220
10220
4380
5840
2920
6240
10220
10220
10220
10220
9160
10220
10220
10220
10220
10220
4380
4780
2920
7300
1460
8760
10220
4380
5840
9160
10220
5840
4380
4380
5840
2920
6240
2920
7300
1460
8760
10220
7300
2920
5840
4380
5840
2920
5840
4380
4380
5840
2920
7300
1460
8760
10220
10220
7300
1460
5840
4380
4380
5840
4380
5840
2920
7300
1460
8760
8760
10220
7300
2920
5840
4380
4380
5840
1460
7700
10220
7300
2920
4380
5840
2920
7300
1460
8760
10220
8760
7300
1460
1460
7300
2920
7300
2920
5840
4380
4380
5840
4380
4780
4380
5840
4380
5840
2920
7300
2920
7300
1460
7300
2920
7300
1460
8760
1460
8760
10220
8760
1460
7300
2920
5840
2920
7300
2920
5840
4380
5840
4380
4380
5840
4380
4780
5840
4380
4380
5840
4380
5840
2920
7300
2920
7300
1460
7300
2920
7300
1460
8760
4380
5840
4380
5840
2920
6240
4380
5840
2920
7300
2920
7300
1460
8760
1460
8760
10220
8760
10220
10220
8760
1460
7300
2920
7300
2920
5840
4380
5840
2920
7300
2920
5840
4380
5840
4380
4380
5840
4380
4780
10220
8760
1460
7300
2920
7300
2920
5840
4380
10220
8760
4380
5840
1460
8760
10220
10220
7300
2920
4380
4380
4380
5840
2920
7300
1460
8760
10220
7300
2920
4380
4380
4380
5840
1460
8760
10220
4380
5840
1460
7700
10220
7300
2920
7300
2920
5840
4380
5840
4380
4380
5840
4380
4380
4380
5840
4380
5840
2920
7300
1460
8760
1460
8760
8760
1460
8760
10220
10220
8760
1460
7300
2920
7300
2920
1460
7300
1460
8760
1460
8760
1460
8760
10220
10220
9160
1460
8760
1460
8760
10220
10220
8760
10220
4380
5840
5840
4380
1460
8760
10220
4380
5840
5840
2920
4380
5840
2920
7300
1460
8760
5840
4380
7300
2920
4380
5840
2920
5840
4380
5840
1460
8760
10220
7300
2920
4380
5840
2920
5840
1460
8760
7300
2920
5840
4380
4380
5840
2920
6240
2920
7300
1460
8760
10220
5840
4380
4380
5840
2920
6240
1460
8760
10220
10220
5840
4380
2920
7300
1460
7120
收到了来自于客户端DESKTOP-I3P6AHH的消息:0

从中可看出大多数响应为1460的整数倍

javasocket解决半包、粘包问题 - 百度文库

解决半包粘包一般3种思路 

1.特殊字符比如回车/n /r来区分一段内容

2.固定定长

3.在应用层定义数据包长度字段

参考http 协议的结束符 · Issue #34 · jinhailang/blog · GitHub - java技术圈 - java技术社区

当请求头没有 content-length 时,怎么知道请求体结束了?

http 的 header 和 body 之间空行分割的,又因为每个头部项是以 \r\n 作为结束符,所以,数据流中是以 \r\n\r\n 来分割解析请求头(响应头)与请求体(响应体)的。如下图所示:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值