解决回射服务器的粘包问题

TCP传送的是流所以可能产生下图几种粘包问题


传过来的可能是 m1包加m2包

                        m1包加m2包前半部分

                        m1包后半部分加m2包

                        ... ...

   粘包产生的原因:


解决方案:

    本质上是要在应用层维护消息与消息的边界

        定长包

        包尾加\r\n(ftp)

        包头加上包体长度

        更复杂的应用层协议

1、我们可以封装2个函数 readn 和 writen 确定读和写的长度

   server

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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

#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); } while(0)
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;
		/*nread>0*/
		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;
                bufp+=nwritten;
                nleft-=nwritten;
         }
         return count;
}
void do_service(int conn){
	struct packet recvbuf;
	int n;
        while(1){
		memset(&recvbuf,0,sizeof(recvbuf));
                int ret=readn(conn,&recvbuf.len,4);
		if(ret==-1)
			ERR_EXIT("read");
		else if(ret<4){
                        printf("client_close\n");
                        break;
                }
		n=ntohl(recvbuf.len);
		ret=readn(conn,recvbuf.buf,n);
                if(ret==-1)
                        ERR_EXIT("read");
                else if(ret<n){
                        printf("client_close\n");
                        break;
                }
                fputs(recvbuf.buf,stdout);
                writen(conn,&recvbuf,4+n);
           }
}
int main(void){
	int listenfd;
	if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
		ERR_EXIT("socket");
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(5188);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	/*servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");*/
	/*inet_aton("127.0.0.1",&servaddr.sin_addr);*/
	int on=1;
	if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
		ERR_EXIT("setsockop");
	if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
		ERR_EXIT("bind");
	if(listen(listenfd,SOMAXCONN)<0)
		ERR_EXIT("listen");
	struct sockaddr_in peeraddr;
	socklen_t peerlen=sizeof(peerlen);
	int conn;
	pid_t pid;
	while(1){
		if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
			ERR_EXIT("accept");
		printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
		pid=fork();
		if(pid==-1)
			ERR_EXIT("fork");
		if(pid==0){
			close(listenfd);
			do_service(conn);
			exit(EXIT_SUCCESS);
		}
		else 
			close(conn);
	}
	return 0;
}

client:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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

#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); } while(0)
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;
		/*nread>0*/
		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;
                bufp+=nwritten;
                nleft-=nwritten;
         }
         return count;
}
void do_service(int conn){
	struct packet recvbuf;
	int n;
        while(1){
		memset(&recvbuf,0,sizeof(recvbuf));
                int ret=readn(conn,&recvbuf.len,4);
		if(ret==-1)
			ERR_EXIT("read");
		else if(ret<4){
                        printf("client_close\n");
                        break;
                }
		n=ntohl(recvbuf.len);
		ret=readn(conn,recvbuf.buf,n);
                if(ret==-1)
                        ERR_EXIT("read");
                else if(ret<n){
                        printf("client_close\n");
                        break;
                }
                fputs(recvbuf.buf,stdout);
                writen(conn,&recvbuf,4+n);
           }
}
int main(void){
	int listenfd;
	if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
		ERR_EXIT("socket");
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(5188);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	/*servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");*/
	/*inet_aton("127.0.0.1",&servaddr.sin_addr);*/
	int on=1;
	if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
		ERR_EXIT("setsockop");
	if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
		ERR_EXIT("bind");
	if(listen(listenfd,SOMAXCONN)<0)
		ERR_EXIT("listen");
	struct sockaddr_in peeraddr;
	socklen_t peerlen=sizeof(peerlen);
	int conn;
	pid_t pid;
	while(1){
		if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
			ERR_EXIT("accept");
		printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
		pid=fork();
		if(pid==-1)
			ERR_EXIT("fork");
		if(pid==0){
			close(listenfd);
			do_service(conn);
			exit(EXIT_SUCCESS);
		}
		else 
			close(conn);
	}
	return 0;
}

2、包尾加\r\n  readline函数

server:

   

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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

#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); } while(0)
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;
		/*nread>0*/
		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;
                bufp+=nwritten;
                nleft-=nwritten;
         }
         return count;
}
ssize_t recv_peek(int sockfd,void *buf,size_t len){
	while(1){
		int ret=recv(sockfd,buf,len,MSG_PEEK);
		if(ret==-1&&errno==EINTR)
			continue;
		return ret;	
	}
}
ssize_t readline(int sockfd,void *buf,size_t maxline){
	int ret;
	int nread;
	char *bufp=buf;
	int nleft=maxline;
	while(1){
		ret=recv_peek(sockfd,bufp,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]=='\n'){
				ret=readn(sockfd,bufp,i+1);
				if(ret!=i+1)
					exit(EXIT_FAILURE);
				return ret;
			}
		}	
		if(nread>nleft)
			exit(EXIT_FAILURE);
		bufp+=nread;
	}
	return -1;
}
void do_service(int conn){
	char recvbuf[1024];
        while(1){
		memset(recvbuf,0,sizeof(recvbuf));
                int ret=readline(conn,recvbuf,1024);
		if(ret==-1)
			ERR_EXIT("readline");
		else if(ret==0){
                        printf("client_close\n");
                        break;
                }
                fputs(recvbuf,stdout);
                writen(conn,recvbuf,strlen(recvbuf));
           }
}
int main(void){
	int listenfd;
	if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
		ERR_EXIT("socket");
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(5188);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	/*servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");*/
	/*inet_aton("127.0.0.1",&servaddr.sin_addr);*/
	int on=1;
	if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
		ERR_EXIT("setsockop");
	if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
		ERR_EXIT("bind");
	if(listen(listenfd,SOMAXCONN)<0)
		ERR_EXIT("listen");
	struct sockaddr_in peeraddr;
	socklen_t peerlen=sizeof(peerlen);
	int conn;
	pid_t pid;
	while(1){
		if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
			ERR_EXIT("accept");
		printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
		pid=fork();
		if(pid==-1)
			ERR_EXIT("fork");
		if(pid==0){
			close(listenfd);
			do_service(conn);
			exit(EXIT_SUCCESS);
		}
		else 
			close(conn);
	}
	return 0;
}

client:

   

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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

#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); } while(0)
size_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;
                /*nread>0*/
                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;
                bufp+=nwritten;
                nleft-=nwritten;
         }
         return count;
}
ssize_t recv_peek(int sockfd,void *buf,size_t len){
        while(1){
                int ret=recv(sockfd,buf,len,MSG_PEEK);
                if(ret==-1&&errno==EINTR)
                        continue;
                return ret;
        }
}
ssize_t readline(int sockfd,void *buf,size_t maxline){
        int ret;
        int nread;
        char *bufp=buf;
	int nleft=maxline;
        while(1){
                ret=recv_peek(sockfd,bufp,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]=='\n'){
                                ret=readn(sockfd,bufp,i+1);
                                if(ret!=i+1)
                                        exit(EXIT_FAILURE);
                                return ret;
                        }
                }
                if(nread>nleft)
                        exit(EXIT_FAILURE);
                bufp+=nread;
        }
        return -1;
}

int main(void){
	int sock;
	if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
		ERR_EXIT("socket");
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(5188);
	servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
	
	if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)	
		ERR_EXIT("connect");
	struct sockaddr_in localaddr;
	socklen_t addrlen=sizeof(localaddr);
	if(getsockname(sock,(struct sockaddr*)&localaddr,&addrlen)<0)
		ERR_EXIT("getsockname");
	printf("ip=%s port=%d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));
	char sendbuf[1024]={0};
	char recvbuf[1024]={0};
	while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL){
		writen(sock,sendbuf,strlen(sendbuf));
		int ret=readline(sock,recvbuf,sizeof(recvbuf));
                if(ret==-1)
                        ERR_EXIT("readline");
                else if(ret==0){
                        printf("client_close\n");
                        break;
                }
		fputs(recvbuf,stdout);
		memset(sendbuf,0,sizeof(sendbuf));
		memset(recvbuf,0,sizeof(recvbuf));
	}	
	close(sock);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值