基于TCP传输的粘包问题

来源:互联网 发布:js获取选择框的值 编辑:程序博客网 时间:2024/05/21 06:33

1我们都知道TCP传输,是基于字节流传输的,所以流与流直接传输就会产生边界问题,我个人对粘包的理解就是,TCP传输无法获悉不同包与包之间的“界限”。

如果对等接受方彼此直接没有约定好传输数据大小的话,就会出现解析数据不准确问题,而且传输数据小于约定大小空间的话,也会出现浪费空间问题,为了解决这种问题,通常才有包头+包体传输,这样对等方就可以分辨出不同的包,所对应的数据。(该办法解决的是发送不定长包)

2.UDP是基于数据包协议,所以也就不存在粘包问题,虽然UDP是不可靠的传输协议,可能存在丢包,失序,错序,重复等问题,但是他的效率是比UDP高的多,我们知道TCP可靠性方面有一个超时重连机制,要让UDP实现可靠传输的话,就需要模拟一套TCP传输机制。

粘包问题解决方法代码如下:回射服务器代码,数据包格式:包头+包体

服务端代码:

/*************************************************************************> File Name: ser.cpp> Created Time: Sun 29 Oct 2017 09:00:58 PM CST ************************************************************************/#include <iostream>using namespace std;#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <stdio.h>#include <string.h>//处理粘包问题//封装read函数struct packet{    int  len;        //包头    char buf[1024];  //包体,实际长度};ssize_t readn(int fd, void* buf, size_t count){    size_t nleft = count;//剩余的字节数    ssize_t nread;       //已经接受的字节数    char* bufp = (char*)buf;    while(nleft > 0)    {        if((nread = read(fd, bufp, nleft))<0)        {            cout << " < 0" << endl;            if(errno == EINTR) //信号打断                continue;            return -1;        }        else if(nread == 0)        {            return count-nleft;        }        bufp += nread;//偏移到屁股        nleft -= nread;    }    return count;}//封装write方法ssize_t writen(int fd, const void* buf, size_t count){    size_t nleft = count;    ssize_t nwritten;    char* bufp = (char*)buf;    while(nleft > 0)    {        if((nwritten = write(fd, bufp, nleft)) < 0)        {            if(errno == EINTR) //信号中断                continue;            return -1;        }        else if(nwritten == 0)        {            continue;           //return count-nleft;        }        bufp += nwritten;        nleft -= nwritten;    }    return count;}/////////////////////////////////////void do_server(int conn){    struct packet recvbuf;    while(1)    {        memset(recvbuf.buf, 0, sizeof(recvbuf.buf));        int ret = readn(conn, &recvbuf.len, 4);        if(ret == -1)        {            perror("read");            exit(1);        }        else if(ret < 4)        {            cout << "clien close\n";            break;        }        int m = recvbuf.len;      cout << "n:" << m << endl;         int n = ntohl(recvbuf.len);      cout << "n:" << n << endl;         ret = readn(conn, &recvbuf.buf, n);        if(ret == -1)        {            perror("read");            exit(1);        }        if(ret < n)        {            cout << "clien close\n";            break;        }        fputs(recvbuf.buf, stdout);        writen(conn, &recvbuf, 4+n);    }    close(conn);}int main(){    int listenfd;    //初始化监听套接字(默认是主动套接字,发起连接)    if((listenfd = socket(AF_INET, SOCK_STREAM, 0))<0)    {        perror("socket created failed");        exit(1);    }    //bind函数,bind一个本地地址到套接字    struct sockaddr_in servaddr;    memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family =  AF_INET;    servaddr.sin_port = htons(8881);    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1")*/////////////////////////////getsockopt///////////        int on = 1;//开启地址重复利用    if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)    {        perror("setsockopt");    }//////////////////////////////////////////////////    if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)    {        perror("bind a local addr to listenfd failed...");        exit(1);    }    //listen 讲套接字用于监听进入的连接,讲套接字从close状态转换成监听状态    //也就是说调用listen函数就将套接字由主动变为被动套接字(接受连接)    if(listen(listenfd, SOMAXCONN) < 0)//SOMAXCONN 默认最大队列    {        perror("listenfd to listen client connect failed..");        exit(1);    }        //accept 从已经连接的队列中返回是第一个连接,队列为空就阻塞    //成功返回一个新的套接字,也就是连接套接字,并且这个新套接字是主动套接字    struct sockaddr_in peeraddr;    socklen_t peerlen = sizeof(peeraddr);//必须初始值    int conn;///多进程处理多客户端连接    pid_t pid;    while(1)    {        if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)        {            perror("accept client connect failed...");            exit(1);        }        cout << "Ip:" << inet_ntoa(peeraddr.sin_addr) << " port:" << ntohs(peeraddr.sin_port) << endl;                pid = fork();        if(pid < 0)        {            perror("fork");            exit(1);        }        if(pid == 0) // 子进程处理客户端连接        {            close(listenfd);            do_server(conn);        }    }    return 0;}
回射服务器,客户端代码如下

/*************************************************************************> File Name: ser.cpp> Created Time: Sun 29 Oct 2017 09:00:58 PM CST ************************************************************************/#include <iostream>using namespace std;#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <stdio.h>#include <string.h>struct packet{    int  len;        //包头    char buf[1024];  //包体,实际长度};ssize_t readn(int fd, void* buf, size_t count){    size_t nleft = count;    ssize_t nread;    char* bufp = (char*)buf;    while(nleft > 0)    {        if((nread = read(fd, bufp, nleft))<0)        {            if(errno == EINTR)                continue;            return -1;        }        else if(nread == 0)        {            return count-nleft;        }        bufp += nread;        nleft -= nread;    }    return count;}ssize_t writen(int fd, const void* buf, size_t count){    size_t nleft = count;    ssize_t nwritten;    char* bufp = (char*)buf;    while(nleft > 0)    {        if((nwritten = write(fd, bufp, nleft)) < 0)        {            if(errno == EINTR)                continue;            return -1;        }        else if(nwritten == 0)        {            continue;            //return count-nleft;        }        bufp += nwritten;        nleft -= nwritten;    }    return count;}/////////////////////////////////////int main(){    int sock;    //初始化监听套接字(默认是主动套接字,发起连接)    if((sock = socket(AF_INET, SOCK_STREAM, 0))<0)    {        perror("socket created failed");        exit(1);    }    struct sockaddr_in servaddr;    memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family =  AF_INET;    servaddr.sin_port = htons(8881);    //servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    //connect ,发起连接    if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)     {        perror("connect ser is failed\n");        exit(1);    }    struct packet sendbuf;    struct packet recvbuf;    memset(&sendbuf, 0, sizeof(sendbuf));    memset(&recvbuf, 0, sizeof(recvbuf));    int n;    while(fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL)    {        n = strlen(sendbuf.buf);        sendbuf.len = htonl(n); //网络字节序                writen(sock, &sendbuf, 4+n);//sizeof()发送定长包                int ret = readn(sock, &recvbuf.len, 4);        if(ret == -1)        {            perror("read");            exit(1);        }        else if(ret < 4)        {            cout << "clien close\n";            break;        }               n = ntohl(recvbuf.len);        ret = readn(sock, &recvbuf.buf, n);        if(ret == -1)        {            perror("read");            exit(1);        }        if(ret < n)        {            cout << "clien close\n";            break;        }                fputs(recvbuf.buf,stdout);        memset(recvbuf.buf, 0, sizeof(recvbuf.buf));        memset(sendbuf.buf, 0 ,sizeof(sendbuf.buf));    }        close(sock);        return 0;}






原创粉丝点击