粘包

粘包及处理方法

粘包:由于缓冲区及下层硬件的原因,第n个包与第n+1个包合并在一起发送,或第n个包与第n+1个包的一部分合并在一起发送,等等。如果接收方要求格式良好,那我们就必须控制发送与接收方式。
处理方法:
1.发送定长包,发送方每次固定发送n个字节长,同时接受方固定每次读n个字节长。缺点:数据不长时,浪费宽带和低效率。
2.发送包头+包体,先发送定长的n字节包头(用于表示数据长度),再发送由包头中所指定长度的数据
3.以行(' ')为单位进行读取

定长包

在2.5版添加如下函数,然后在main函数中将read/write修改为readn和writen即可

ssize_t readn(int fd,void *buff,size_t count){
    char *buffp;
    ssize_t nread;
    size_t nleft;
 
    buffp=(char *)buff;
    nleft=count;
    while(nleft > 0){
        if((nread = read(fd,buffp,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nread == 0)
            break;
        nleft -= nread;
        buffp += nread;
    }
    return count-nleft;
}
 
ssize_t writen(int fd,const void *buff,size_t n){
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
 
    ptr=buff;
    nleft=n;
    while(nleft > 0){
        if((nwritten=write(fd,ptr,nleft)) < 0){
            if(nwritten < 0 && errno == EINTR)
                continue;
            else
                return -1;
        }else if(nwritten == 0)
            break;
        nleft -= nwritten;
        ptr += nwritten;
    }
    return n-nleft;
}

包头+包体

注意点:包头中定义包体的长度的int需要以网络字节序来记录, 所以在发送时要用htonl,在接收时要用ntohl
server.c

#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
void err_quit(const char *s){
    perror(s);
    exit(1);
}
 
void handler(int signo){
    printf("program terminated
");
    exit(0);
}
 
struct packet{
    int len;
    char buf[1024];
};
 
ssize_t readn(int fd,void *buff,size_t count){
    char *buffp;
    ssize_t nread;
    size_t nleft;
 
    buffp=(char *)buff;
    nleft=count;
    while(nleft > 0){
        if((nread = read(fd,buffp,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nread == 0)
            break;
        nleft -= nread;
        buffp += nread;
    }
    return count-nleft;
}
 
ssize_t writen(int fd,const void *buff,size_t n){
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
 
    ptr=buff;
    nleft=n;
    while(nleft > 0){
        if((nwritten=write(fd,ptr,nleft)) < 0){
            if(nwritten < 0 && errno == EINTR)
                continue;
            else
                return -1;
        }else if(nwritten == 0)
            break;
        nleft -= nwritten;
        ptr += nwritten;
    }
    return n-nleft;
}
 
int main(int argc,char *argv[]){
    int sockfd,connfd;
    pid_t child;
    socklen_t len;
    struct sockaddr_in addr,client;
 
    if((sockfd=socket(PF_INET,SOCK_STREAM,0)) < 0)
        err_quit("sockfd");
 
    bzero(&addr,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    addr.sin_port=htons(5566);
 
    int on=1;
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) <0)
        err_quit("setsockopt");
 
    if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr))<0)
        err_quit("bind");
 
    if(listen(sockfd,10)<0)
        err_quit("listen");
 
    len=sizeof(client);
    connfd=accept(sockfd,(struct sockaddr *)&client,&len);
    if(connfd < 0)
        err_quit("accept");
 
    struct packet pac;
    bzero(&pac,sizeof(pac));
    int n;
 
    if((child=fork())<0){
        err_quit("fork");
    }else if(child  == 0){
        while(1){
            int ret=readn(connfd,&pac.len,4);
            if(ret == -1)
                err_quit("read");
            else if(ret < 4){
                printf("peer closed
");
                break;
            }
 
            n=ntohl(pac.len);
            ret=readn(connfd,pac.buf,n);
            if(ret < 1){
                err_quit("read");
            }
            if(ret < n){
                printf("peer closed
");
                break;
            }
 
            fputs(pac.buf,stdout);
            bzero(&pac,sizeof(pac));
        }
        kill(getppid(),SIGUSR1);
    }else{
        signal(SIGUSR1,handler);
        while(fgets(pac.buf,sizeof(pac.buf),stdin) != NULL){
            n=strlen(pac.buf);
            pac.len=htonl(n);
            writen(connfd,&pac,n+4);
        }
    }
    exit(0);
}

client.c

#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
void err_quit(const char *s){
    perror(s);
    exit(1);
}
 
void handler(int signo){
    printf("program terminated
");
    exit(0);
}
 
struct packet{
    int len;
    char buf[1024];
};
 
ssize_t readn(int fd,void *buff,size_t count){
    size_t nleft;
    ssize_t nread;
    char *ptr;
 
    nleft=count;
    ptr=(char *)buff;
    while(nleft > 0){
        if((nread=read(fd,ptr,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nread == 0)
            break;
 
        ptr += nread;
        nleft -= nread;
    }
 
    return count - nleft;
}
 
ssize_t writen(int fd,const void *buff,size_t n){
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
 
    nleft=n;
    while(n > 0){
        if((nwritten=write(fd,buff,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nwritten == 0)
            break;
 
        nleft -= nwritten;
        ptr += nwritten;
    }
 
    return n-nleft;
}
 
int main(int argc,char *argv[]){
    int sockfd;
    struct sockaddr_in servaddr;
    pid_t child;
 
    if((sockfd=socket(PF_INET,SOCK_STREAM,0)) < 0)
        err_quit("socket");
 
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    servaddr.sin_port=htons(5566);
 
    if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) <0)
        err_quit("connect");
 
    struct packet pac;
    bzero(&pac,sizeof(pac));
    int n;
 
    if((child=fork()) <0)
        err_quit("fork");
    else if(child == 0){
        signal(SIGUSR1,handler);
        while(fgets(pac.buf,sizeof(pac.buf),stdin) != NULL){
            n=strlen(pac.buf);
            pac.len=htonl(n);
            writen(sockfd,&pac,n+4);
        }
    }else{
        while(1){
            int ret=readn(sockfd,&pac.len,4);
            if(ret == -1)
                err_quit("read");
            if(ret < 4){
               printf("perr closed
");
               break;
            }
 
            n=ntohl(pac.len);
            ret=readn(sockfd,pac.buf,n);
            if(ret == -1)
                err_quit("read");
            if(ret < n){
                printf("peer closed
");
                break;
            }
            fputs(pac.buf,stdout);
            bzero(&pac,sizeof(pac));
        }
        kill(child,SIGUSR1);
    }
    exit(0);
}

行为单位进行读取

非常适用于终端消息的性状

server.c

#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
void err_quit(const char *s){
    perror(s);
    exit(1);
}
 
void handler(int signo){
    printf("program terminated
");
    exit(0);
}
 
ssize_t readn(int fd,void *buff,size_t count){
    char *buffp;
    ssize_t nread;
    size_t nleft;
 
    buffp=(char *)buff;
    nleft=count;
    while(nleft > 0){
        if((nread = read(fd,buffp,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nread == 0)
            break;
        nleft -= nread;
        buffp += nread;
    }
    return count-nleft;
}
 
ssize_t writen(int fd,const void *buff,size_t n){
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
 
    ptr=buff;
    nleft=n;
    while(nleft > 0){
        if((nwritten=write(fd,ptr,nleft)) < 0){
            if(nwritten < 0 && errno == EINTR)
                continue;
            else
                return -1;
        }else if(nwritten == 0)
            break;
        nleft -= nwritten;
        ptr += nwritten;
    }
    return n-nleft;
}
 
ssize_t recv_peek(int fd,void *buf,size_t len){
    ssize_t ret;
    while(1){
        ret=recv(fd,buf,len,MSG_PEEK);
        if(ret == -1 && errno == EINTR)
            continue;
        return ret;
    }
}
 
ssize_t readline(int fd,void *buf,size_t maxline){
    ssize_t ret;
    size_t nread;
    size_t nleft;
    char *bufp;
 
    bufp=buf;
    nleft=maxline;
    while(1){
        ret=recv_peek(fd,buf,nleft);
        if(ret < 0)
            return ret;
        else if(ret == 0)
            return ret;
 
        nread=ret;
        int i;
        for(i=0;i<nread;i++){
            if(bufp[i] == '
'){
                ret=readn(fd,bufp,i+1);
                if(ret != i+1)
                    err_quit("readn");
 
                return ret;
            }
        }
 
        if(nread > nleft)
            err_quit("readn");
 
        nleft -= nread;
        ret=readn(fd,bufp,nread);
        if(ret != nread)
            err_quit("readn");
 
        bufp += nread;
    }
 
    return -1;
}
 
int main(int argc,char *argv[]){
    int sockfd,connfd;
    pid_t child;
    socklen_t len;
    struct sockaddr_in addr,client;
 
    if((sockfd=socket(PF_INET,SOCK_STREAM,0)) < 0)
        err_quit("sockfd");
 
    bzero(&addr,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    addr.sin_port=htons(5566);
 
    int on=1;
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) <0)
        err_quit("setsockopt");
 
    if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr))<0)
        err_quit("bind");
 
    if(listen(sockfd,10)<0)
        err_quit("listen");
 
    len=sizeof(client);
    connfd=accept(sockfd,(struct sockaddr *)&client,&len);
    if(connfd < 0)
        err_quit("accept");
 
    char buf[1024];
    bzero(buf,sizeof(buf));
 
    if((child=fork())<0){
        err_quit("fork");
    }else if(child  == 0){
        while(1){
            int ret=readline(connfd,buf,sizeof(buf));
            if(ret == -1)
                err_quit("readline");
            else if(ret == 0){
                printf("peer closed
");
                break;
            }
 
            fputs(buf,stdout);
            bzero(buf,sizeof(buf));
        }
        kill(getppid(),SIGUSR1);
    }else{
        signal(SIGUSR1,handler);
        while(fgets(buf,sizeof(buf),stdin) != NULL){
            writen(connfd,buf,strlen(buf));
        }
    }
    exit(0);
}

client.c

#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
void err_quit(const char *s){
    perror(s);
    exit(1);
}
 
void handler(int signo){
    printf("program terminated
");
    exit(0);
}
 
 
ssize_t readn(int fd,void *buff,size_t count){
    size_t nleft;
    ssize_t nread;
    char *ptr;
 
    nleft=count;
    ptr=(char *)buff;
    while(nleft > 0){
        if((nread=read(fd,ptr,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nread == 0)
            break;
 
        ptr += nread;
        nleft -= nread;
    }
 
    return count - nleft;
}
 
ssize_t writen(int fd,const void *buff,size_t n){
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
 
    nleft=n;
    while(n > 0){
        if((nwritten=write(fd,buff,nleft)) < 0){
            if(errno == EINTR)
                continue;
            else
                return -1;
        }else if(nwritten == 0)
            break;
 
        nleft -= nwritten;
        ptr += nwritten;
    }
 
    return n-nleft;
}
/* if have any data then return */
ssize_t recv_peek(int fd,void *buf,size_t len){
    ssize_t ret;
    while(1){
        ret=recv(fd,buf,len,MSG_PEEK);
        if(ret == -1 && errno == EINTR)
            continue;
        return ret;
    }
}
 
ssize_t readline(int fd,void *buf,size_t maxline){
    ssize_t ret;
    size_t nread;
    size_t nleft;
    char *bufp;
 
    bufp=buf;
    nleft=maxline;
    while(1){
        ret=recv_peek(fd,buf,nleft);
        if(ret < 0)
            return ret;
        else if(ret == 0)
            return ret;
 
        nread=ret;
        int i;
        for(i=0;i<nread;i++){
            if(bufp[i] == '
'){
                ret=readn(fd,bufp,i+1);
                if(ret != i+1)
                    err_quit("readn");
 
                return ret;
            }
        }
 
        if(nread > nleft)
            err_quit("nread");
 
        nleft -= nread;
        ret=readn(fd,bufp,nread);
        if(ret != nread)
            err_quit("readn");
 
        bufp += nread;
    }
 
    return -1;
}
 
int main(int argc,char *argv[]){
    int sockfd;
    struct sockaddr_in servaddr;
    pid_t child;
 
    if((sockfd=socket(PF_INET,SOCK_STREAM,0)) < 0)
        err_quit("socket");
 
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    servaddr.sin_port=htons(5566);
 
    if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) <0)
        err_quit("connect");
 
    char buf[1024];
    bzero(buf,sizeof(buf));
 
    if((child=fork()) <0)
        err_quit("fork");
    else if(child == 0){
        signal(SIGUSR1,handler);
        while(fgets(buf,sizeof(buf),stdin) != NULL){
            writen(sockfd,buf,strlen(buf));
        }
    }else{
        while(1){
            int ret=readline(sockfd,buf,sizeof(buf));
            if(ret == -1)
                err_quit("readline");
            if(ret == 0){
                printf("peer closed
");
                break;
            }
            fputs(buf,stdout);
            bzero(buf,sizeof(buf));
        }
        kill(child,SIGUSR1);
    }
    exit(0);
}
原文地址:https://www.cnblogs.com/cfans1993/p/6131900.html