您的位置:首页 > 理论基础 > 计算机网络

TCP 粘包问题处理[1] ( the solution of sticky package problem of TCP )

2015-11-04 00:49 555 查看
Because TCP is a stream protocol, so in the translation , there may have lots of sticky bags.

The passage offer two method to solve this problem.

1.We use the fixed length package to solve it.

2. We designed the protocol by ourselves , a struct wiht the head and body of the package. When we receive the message the need to receive the head of the message and then get the body of the package.

For the first solution:

Server:

/************************************************************************
> file  name: echoserv.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)

//ssize_t signed integer   size_t unsigned integer
ssize_t readn ( int fd , void *buf , size_t count )    // encapsulate readn as the function of read
{
size_t nleft = count ;
ssize_t nread ;
char *bufp ;
bufp = ( char *) buf ;
while ( nleft > 0 )
{
nread = read ( fd , bufp , nleft ) ;
if( nread  < 0 )
{
if ( errno == EINTR )   // interrupted by signal , we don't think this occasion is not right
{
continue ;
}
return -1 ;
}
else if ( 0 == nread )    //peer closed
{
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 ;
bufp = ( char * ) buf ;
while ( nleft > 0 )
{
if ( ( nwritten = write ( fd , bufp , nleft ) ) < 0 )
{
if ( errno == EINTR )  // just like the occasion in writen
{
continue ;
}
return -1 ;
}
else if ( 0 == nwritten  ) // if 0 == nread , like nothing happened to write ...
{
continue ;
}
bufp += nwritten ;
nleft -= nwritten ;
}
return count ;
}

void do_service ( int conn )
{
char recvbuf[1024] ;
while(1)
{
memset ( recvbuf , 0 , sizeof(recvbuf)) ;
int ret = readn( conn , recvbuf , sizeof(recvbuf) ) ;
printf ("ret = %d\n" , ret ) ;
if ( 0 == ret )
{
printf ("client close") ;
break ;
}
else if ( -1 == ret )
{
ERR_EXIT ("read") ;
}
fputs ( recvbuf , stdout ) ;
writen ( conn , recvbuf , ret ) ;
}
}

int
main ()
{
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) ;		//INTADDR_ANY == 0

//other two means to change the host address to network address
//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;
//inet_aton("127.0.0.1" , &servaddr.sin_addr) ;

/*	   set out the problem that the address has already been used by SO_REUSEADDR
int on = 1 ;
int opt ;
if (( opt = setsockopt (listenfd ,SOL_SOCKET , SO_REUSEADDR, &on , sizeof(on) )) < 0 )
{
ERR_EXIT("setsocketopt") ;
}
*/
int on = 1 ;
if ( setsockopt ( listenfd , SOL_SOCKET , SO_REUSEADDR , &on , sizeof(on) ) < 0 )
{
ERR_EXIT("setsockopt") ;
}
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(peeraddr) ;
int conn ;
while ( 1 )
{
if ( (conn = accept ( listenfd , ( struct sockaddr * )&peeraddr , &peerlen )) < 0 )
{
ERR_EXIT("accept") ;
}

// print the client's address and ip '
printf ("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr) ,ntohs(peeraddr.sin_port) ) ;
pid_t pid ;
pid = fork() ;
if ( -1 == pid )
{
ERR_EXIT("fork") ;
}
if ( 0 == pid )
{
close(listenfd) ;
do_service(conn) ;
exit (EXIT_SUCCESS ) ;
}
else
{
close(conn) ;
}
}
return 0 ;
}


Client :

/************************************************************************
>file name : echocli.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)

//ssize_t signed integer   size_t unsigned integer
ssize_t readn ( int fd , void *buf , size_t count )    // encapsulate readn as the function of read
{
size_t nleft = count ;
ssize_t nread ;
char *bufp ;
bufp = ( char *) buf ;
//	printf ("nleft = %d\n" , nleft ) ;
while ( nleft > 0 )
{
nread = read ( fd , bufp , nleft ) ;
if( nread  < 0 )
{
if ( errno == EINTR )   // interrupted by signal , we don't think this occasion is not right
{
continue ;
}
return -1 ;
}
else if ( 0 == nread )    //peer closed
{
return count - nleft ;
//break ;
}
//		printf ("nreaad = %d\n" , nread ) ;
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 ;
bufp = ( char * ) buf ;
while ( nleft > 0 )
{
if ( ( nwritten = write ( fd , bufp , nleft ) ) < 0 )
{
if ( errno == EINTR )  // just like the occasion in writen
{
continue ;
}
return -1 ;
}
else if ( 0 == nwritten  ) // if 0 == nread , like nothing happened to write ...
{
continue ;
}
bufp += nwritten ;
nleft -= nwritten ;
}
return count ;
}

int
main ()
{
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  = htonl(INADDR_ANY) ;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;
//inet_aton("127.0.0.1" , &servaddr.sin_addr) ;
if ( connect ( sock , ( struct sockaddr * )& servaddr, sizeof(servaddr )) < 0 )
{
ERR_EXIT("connet") ;
}
char sendbuf[1024] = {0} ;
char recvbuf[1024] = {0} ;
while( fgets ( sendbuf , sizeof(sendbuf) , stdin ) != NULL )
{
writen ( sock , sendbuf , sizeof(sendbuf) ) ;
readn ( sock , recvbuf , sizeof(recvbuf ) ) ;
fputs ( recvbuf , stdout ) ;
memset ( sendbuf , 0 , sizeof(sendbuf) ) ;
memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
}
close ( sock ) ;
return 0 ;
}


The second solution :

Server :

/************************************************************************
> file  name: echoserv.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)

struct packet
{
int len ;	    // head of the package
char buf[1024] ;    // store the real data , the body of the package
};

//ssize_t signed integer   size_t unsigned integer
ssize_t readn ( int fd , void *buf , size_t count )    // encapsulate readn as the function of read
{
size_t nleft = count ;
ssize_t nread ;
char *bufp ;
bufp = ( char *) buf ;
while ( nleft > 0 )
{
nread = read ( fd , bufp , nleft ) ;
if( nread  < 0 )
{
if ( errno == EINTR )   // interrupted by signal , we don't think this occasion is not right
{
continue ;
}
return -1 ;
}
else if ( 0 == nread )    //peer closed
{
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 ;
bufp = ( char * ) buf ;
while ( nleft > 0 )
{
if ( ( nwritten = write ( fd , bufp , nleft ) ) < 0 )
{
if ( errno == EINTR )  // just like the occasion in writen
{
continue ;
}
return -1 ;
}
else if ( 0 == nwritten  ) // if 0 == nread , like nothing happened to write ...
{
continue ;
}
bufp += nwritten ;
nleft -= nwritten ;
}
return count ;
}

void do_service ( int conn )
{
struct packet recvbuf ;
while(1)
{
memset ( &recvbuf , 0 , sizeof(recvbuf)) ;
int ret = readn( conn , &recvbuf.len , 4 ) ;	//receive the 4 bytes at first
if ( -1 == ret )
{
ERR_EXIT ("read") ;
}
else if ( ret < 4  )		//may interrupted by signal
{
printf ("clien close\n") ;
break ;
}
int n ;
n = ntohl(recvbuf.len);		//receive the length of the data
ret = readn ( conn , recvbuf.buf , n ) ;
if ( -1 == ret )
{
ERR_EXIT("read") ;
}
else if ( ret <  n )
{
printf ("client close\n") ;
break ;
}
fputs ( recvbuf.buf , stdout ) ;
writen ( conn , &recvbuf , 4+n ) ;
}
}

int
main ()
{
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) ;		//INTADDR_ANY == 0

//other two means to change the host address to network address
//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;
//inet_aton("127.0.0.1" , &servaddr.sin_addr) ;

/*	   set out the problem that the address has already been used by SO_REUSEADDR
int on = 1 ;
int opt ;
if (( opt = setsockopt (listenfd ,SOL_SOCKET , SO_REUSEADDR, &on , sizeof(on) )) < 0 )
{
ERR_EXIT("setsocketopt") ;
}
*/
int on = 1 ;
if ( setsockopt ( listenfd , SOL_SOCKET , SO_REUSEADDR , &on , sizeof(on) ) < 0 )
{
ERR_EXIT("setsockopt") ;
}
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(peeraddr) ;
int conn ;
while ( 1 )
{
if ( (conn = accept ( listenfd , ( struct sockaddr * )&peeraddr , &peerlen )) < 0 )
{
ERR_EXIT("accept") ;
}

// print the client's address and ip '
printf ("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr) ,ntohs(peeraddr.sin_port) ) ;
pid_t pid ;
pid = fork() ;
if ( -1 == pid )
{
ERR_EXIT("fork") ;
}
if ( 0 == pid )
{
close(listenfd) ;
do_service(conn) ;
exit (EXIT_SUCCESS ) ;
}
else
{
close(conn) ;
}
}
return 0 ;
}


Client :

/************************************************************************
>file name : echocli.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)

struct packet
{
int len ;	    // the haad of the package
char buf[1024] ;    // store the real data  , the body of the package
};

//ssize_t signed integer   size_t unsigned integer
ssize_t readn ( int fd , void *buf , size_t count )    // encapsulate readn as the function of read
{
size_t nleft = count ;
ssize_t nread ;
char *bufp ;
bufp = ( char *) buf ;
//	printf ("nleft = %d\n" , nleft ) ;
while ( nleft > 0 )
{
//		printf ("nleft = %d\n" , nleft ) ;
nread = read ( fd , bufp , nleft ) ;
//                printf ("nread = %d\n" , nread ) ;
if( nread  < 0 )
{
if ( errno == EINTR )   // interrupted by signal , we don't think this occasion is not right
{
continue ;
}
return -1 ;
}
else if ( 0 == nread )    //peer closed
{
return count - nleft ;
//break ;
}
//		printf ("nreaad = %d\n" , nread ) ;
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 ;
bufp = ( char * ) buf ;
while ( nleft > 0 )
{
if ( ( nwritten = write ( fd , bufp , nleft ) ) < 0 )
{
if ( errno == EINTR )  // just like the occasion in writen
{
continue ;
}
return -1 ;
}
else if ( 0 == nwritten  ) // if 0 == nread , like nothing happened to write ...
{
continue ;
}
bufp += nwritten ;
nleft -= nwritten ;
}
return count ;
}

int
main ()
{
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  = htonl(INADDR_ANY) ;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;
//inet_aton("127.0.0.1" , &servaddr.sin_addr) ;
if ( connect ( sock , ( struct sockaddr * )& servaddr, sizeof(servaddr )) < 0 )
{
ERR_EXIT("connet") ;
}
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) ;        // n == the length of that package
sendbuf.len = htonl(n) ;           // change host byte order to network byte order
writen ( sock , &sendbuf , 4+n ) ;     // the former 4 bytes and  the real data(n)

int ret = readn( sock , &recvbuf.len , 4 ) ;    //receive the 4 bytes at first
//printf ("ret = %d\n" , ret ) ;
if ( -1 == ret )
{
ERR_EXIT ("read") ;
break ;
}
else if ( ret < 4  )            //may interrupted by signal
{
printf ("clien close\n") ;
break ;
}
int n ;
n = ntohl(recvbuf.len) ;         //receive the length of the data
ret = readn ( sock , recvbuf.buf , n ) ;
if ( -1 == ret )
{
ERR_EXIT("read") ;
}
else if ( ret <  n )
{
printf ("client close\n") ;
break ;
}
fputs ( recvbuf.buf , stdout ) ;
memset ( &sendbuf , 0 , sizeof(sendbuf) ) ;
memset ( &recvbuf , 0 , sizeof(recvbuf) ) ;
}
close ( sock ) ;
return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: