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;
}