TCP 组包和拆包算法

/*************************************
文件名: server.c
TCP 组包和拆包实现算法

作者:   马中海
QQ:     284358503
Email: [email protected]
*/
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUF_SIZE 1024*5

//#define TCP_PACK_DEBUG 1

int main()
{

    int nBufuseLen=0;       //缓冲区里用到的长度
    int nRevOnceLen=0;      //read 一次读到的长度
    int nPackLen=0;         //每一包的长度,包括头和两个长度和尾字节
    int bufSize=BUF_SIZE;

    unsigned char buf[BUF_SIZE];     //read 的缓存

    int i=0;

    int sfp,nfp; /* 定义两个描述符 */
    struct sockaddr_in s_add,c_add;
    int sin_size;
    unsigned short portnum=6000; /* 服务端使用端口 */
    printf("Hello,welcome to my server !\r\n");
    sfp = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sfp)
    {
        printf("socket fail ! \r\n");
        return -1;
    }
    printf("socket ok !\r\n");
    /* 填充服务器端口地址信息,以便下面使用此地址和端口监听 */
    bzero(&s_add,sizeof(struct sockaddr_in));
    s_add.sin_family=AF_INET;
    s_add.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */
    s_add.sin_port=htons(portnum);
    /* 使用bind进行绑定端口 */
    if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
    {
        printf("bind fail !\r\n");
        return -1;
    }
    printf("bind ok !\r\n");
    /* 开始监听相应的端口 */
    if(-1 == listen(sfp,5))
    {
        printf("listen fail !\r\n");
        return -1;
    }
    printf("listen ok\r\n");
    //  while(1)
    {

        //    char buf[1027];
        //    int readlen;

        sin_size = sizeof(struct sockaddr_in);
        /* accept服务端使用函数,调用时即进入阻塞状态,等待用户进行连接,在没有客户端进行连接时,程序停止在此处,
           不会看到后面的打印,当有客户端进行连接时,程序马上执行一次,然后再次循环到此处继续等待。
           此处accept的第二个参数用于获取客户端的端口和地址信息。
            */
        nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
        if(-1 == nfp)
        {
            printf("accept fail !\r\n");
            return -1;
        }
        printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));
        /* 这里使用write向客户端发送信息,也可以尝试使用其他函数实现 */
        if(-1 == write(nfp,"hello,welcome to my server \r\n",32))
        {
            printf("write fail!\r\n");
            return -1;
        }

//头0xF9   两个长度字节   数据   尾0xF8
        while(1)
        {
#ifdef TCP_PACK_DEBUG
            {
                printf("bufSize = %d ,nBufuseLen = %d\n",bufSize,nBufuseLen);
            }
#endif
            if(bufSize>nBufuseLen)
            {
                nRevOnceLen=read(nfp,buf+nBufuseLen,bufSize-nBufuseLen);
                nBufuseLen=nBufuseLen+nRevOnceLen;

#ifdef TCP_PACK_DEBUG
                {
                    printf("nBufuseLen = %d>3\n",nBufuseLen);
                }
#endif
                /*
                                printf(" nRevOnceLen data:\n");
                                for(i=0; i<nRevOnceLen; i++)
                                {
                                    printf(" 0x%x ",buf[i]&0xFF);
                                }
                                printf(" \n");
                */

            }
            else
            {
                printf("buf if full\n");
            }

            while(nBufuseLen>3)            //用到的大于3个字节
            {

                /*            printf(" nBufuseLen data:\n");
                            for(i=0; i<nBufuseLen; i++)
                            {
                                printf(" 0x%x ",buf[i]&0xFF);
                            }
                            printf(" \n");
                */

#ifdef TCP_PACK_DEBUG
                {
                    printf("buf[0] = 0x%x \n",buf[0]&0xFF);
                }
#endif
                if((buf[0]&0xFF)==0xF9)         //头
                {
                    nPackLen=(buf[1]*256)+buf[2];

#ifdef TCP_PACK_DEBUG
                    {
                        printf("nBufuseLen=%d ,nPackLen=%d\n",nBufuseLen,nPackLen);
                    }
#endif

                    if(nBufuseLen>=nPackLen)                //大于和=
                    {
#ifdef TCP_PACK_DEBUG
                        {
                            printf("buf[nPackLen-1] = 0x%x \n",buf[nPackLen-1]&0xFF);
                        }
#endif

                        if((buf[nPackLen-1]&0xFF)==0xF8)                  //尾
                        {
                            //找到一包数据,所有的数据都往前移动nPackLen个字节
#ifdef TCP_PACK_DEBUG
                            {
                                printf("head 0x%x length %d tail 0x%x \n", buf[0]&0xFF,nPackLen,buf[nPackLen-1]&0xFF);
                            }
#endif

                            for(i=0; i<nBufuseLen-nPackLen; i++)
                            {
                                buf[i]=buf[nPackLen+i];
                            }

                            nBufuseLen=nBufuseLen-nPackLen;      //用到的字节变小了
                            //   continue;           //不要read了
                        }
                        else
                        {

#ifdef TCP_PACK_DEBUG
                            printf("buf[%d-1]=0x%x,(buf[0]!=0xF8)\n",nPackLen,(buf[nPackLen-1]!=0xF8));        //这里应该不会执行
                            for(i=0; i<5; i++)
                            {
                                printf(" 0x%x ",buf[i]&0xFF);
                            }
                            printf(" \n");
#endif
                            printf(" (buf[nPackLen-1]&0xFF)!=0xF8 \n");
                            nBufuseLen=0;           //清除错误的包   //这里应该不会执行

                            break;

                        }
                    }
                    else
                    {

#ifdef TCP_PACK_DEBUG
                        printf(" nBufuseLen<=nPackLen\n");
#endif
                        break;
                    }
                }
                else
                {
                    printf(" (buf[0]!=0xF9)\n");        //这里应该不会执行
                    nBufuseLen=0;           //清除错误的包   //这里应该不会执行
                    break;
                }
            }

            /*
                        if((0xFF&buf[0])!=0xF2)
                        {
                            printf("readlen = %d buf  0x%x  0x%x  0x%x \n",readlen,0xFF&buf[0],0xFF&buf[1],0xFF&buf[2]);
                        }

                        if(readlen!=1027)
                            printf("readlen = %d buf  0x%x  0x%x  0x%x \n",readlen,0xFF&buf[0],0xFF&buf[1],0xFF&buf[2]);

            */
        }
        //  close(nfp);
    }
    // close(sfp);
    return 0;
}
时间: 2024-10-13 11:43:29

TCP 组包和拆包算法的相关文章

TCP粘包,拆包及解决方法

粘包拆包问题是处于网络比较底层的问题,在数据链路层.网络层以及传输层都有可能发生.我们日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因此粘包拆包问题只发生在TCP协议中. 什么是粘包.拆包? 假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下: 第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象,此种情况不在本文的讨论范围内. 第二种情况,接收端只收到一个数据包,由于TC

关于TCP粘包和拆包的终极解答

关于TCP粘包和拆包的终极解答 程序员行业有一些奇怪的错误的观点(误解),这些误解非常之流行,而且持有这些错误观点的人经常言之凿凿,打死也不相信自己有错,实在让人啼笑皆非.究其原因,还是因为这些错误观点所对应的正确观点不符合人的正常思维习惯,是扭曲人的直观感受的. 有两个错误观点非常之经典,一而再,再而三的出现,就跟韭菜一样,割不完,还越长越多.一是经典的"服务器最多65536个连接"误解,打开链接看介绍.另一个就是这里要讲的TCP"粘包"和"拆包&quo

TCP粘包和拆包

TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包.这样,接收端,就难于分辨出来了,必须提供科学的拆包机制.即面向流的通信是无消息保护边界的.        图解TCP的粘包和拆包 假设客户端分别发送了两个数据包D1和

netty 解决TCP粘包与拆包问题(二)

TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识消息的总长度 一.采用指定分割符解决粘包与拆包问题 服务端 1 package com.ming.netty.nio.stickpack; 2 3 4 5 import java.net.InetSocketAddress; 6 7 import io.netty.bootstrap.ServerB

TCP粘包的拆包处理

因为TCP是流式处理的,所以包没有边界,必须设计一个包头,里面表示包的长度(一般用字节表示),根据这个来逐个拆包.如果对于发送/接收频率不高的话,一般也就不做拆包处理了,因为不大可能有粘包现象. 以下是粘包和拆包的分析: http://blog.csdn.net/zhangxinrun/article/details/6721495 用Qt的TCPSocket读出的数据来拆: http://www.aiuxian.com/article/p-1732805.html 我是根据以上链接例子Qt的逻

封装一个带大小的封包,防止Tcp粘包、拆包

C++ 头文件 #ifndef TCPWRAP_H #define TCPWRAP_H #include <memory> #include <netinet/in.h> #include <string.h> #pragma pack(push,1) typedef struct { size_t length; //包头(包体长度) char* body; //包体 }Packet; #pragma pack(pop) class TcpDataWrap { pri

netty 解决TCP粘包与拆包问题(三)

今天使用netty的固定长度进行解码 固定长度解码的原理就是按照指定消息的长度对消息自动解码. 在netty实现中,只需要采用FiexedLengthFrameDecoder解码器即可... 以下是服务端代码 1 package com.ming.netty.nio.stickpack; 2 3 4 5 import java.net.InetSocketAddress; 6 7 import io.netty.bootstrap.ServerBootstrap; 8 import io.net

TCP粘包/拆包问题

无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. TCP粘包/拆包问题说明 假设客户

Netty(三)TCP粘包拆包处理

tcp是一个“流”的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 粘包.拆包问题说明 假设客户端分别发送数据包D1和D2给服务端,由于服务端一次性读取到的字节数是不确定的,所以可能存在以下4种情况. 1.服务端分2次读取到了两个独立的包,分别是D1,D2,没有粘包和拆包: 2.服务端一次性接收了两个包,D1和D2粘在一起了,被成为TCP粘包; 3.服务端分2次读取到了两个数据包,第一次读取到了完整的D1和D2包的部