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

Function : 

In this passage , we offer another method  to set out the sticky package. The method is that we encapsulate deadline function to realise  to read by line. We set '\n' as the boundary of the message. 


Server : 

/**********************************************************************
    > filename: echoserv2.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 the signed integer , size_t the unsigned integer 
ssize_t readn ( int fd , void *buf , size_t count )   //encapsulate like read function 
{
	size_t nleft = count ;
	ssize_t nread ;
	char *bufp ;
	bufp = ( char *) buf ;
	while ( nleft > 0 ) 
	{
		//nread = read ( fd , bufp , nleft ) ;
		//printf ("nread = %d\n" , nread ) ;
		if ( (nread = read ( fd , bufp , nleft )) < 0  ) 
		{
			if ( EINTR  == errno ) // interrupted by the signal , we don't think this occasion is a mistake 
			{
				continue ;
			}
			return -1 ;  		//failure 
		}   
		else if ( 0 == nread )  // peer close 
		{
			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 ( EINTR == errno ) 
			{
				continue ;	
			}
			return -1 ;
		}
		else if ( 0 == nwritten )    // like nothing happened to write function   
		{
			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 ) ;   
//only let buf read the data from sockfd but not remove the data from that sockfd(socket) 
//in comparison with read function it receive the data and remove the data in the socket 
		if ( -1 == ret && EINTR == errno ) 
		{
			continue ;			//interrupted by signal 
		}
		return ret ;
	}
}

                                                                                                                                                                                                                                                      
ssize_t readline ( int sockfd , void *buf  , size_t maxline ) 
{
	int ret ;
	int nread ;
	char *bufp ;
	bufp = (char*) buf ;
	int nleft ;
	nleft = maxline ;
	while (1 ) 
	{
		ret = recv_peek ( sockfd, bufp , nleft ) ;
		if ( ret < 0 )    //failure 
		{
			return ret ;
		}
		else if ( 0 == ret )		// peer closed  
		{
			return ret ;
		}
		nread = ret ;
		int i ;
		for ( i = 0 ; i < nread ; i ++ ) 
		{
			if ( '\n' ==  bufp[i]) 
			{
				ret = readn (sockfd,bufp , i+1) ;
				if ( ret != i + 1 ) 
				{
					exit(EXIT_FAILURE) ;
				}
				return ret ;
			}
		}
		if ( nread > nleft )   // the data that we read(nread) can't be more than the real data in the socket 
		{
			exit ( EXIT_FAILURE ) ;
		}
		nleft -= nread ;
		ret = readn ( sockfd , bufp , nread ) ;  // read n characters from the socket ( don't contain '\n') 
		if ( ret != nread ) 
		{
			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 , sizeof(recvbuf) ) ;
		 if ( -1 == ret ) 
		 {
			 ERR_EXIT("readline") ;
		 }
		if ( 0 == ret ) 
		{
			//	ERR_EXIT ("client close\n") ;
			printf("client close\n") ;
			break ;
		}
		fputs ( recvbuf , stdout ) ;
		writen ( conn , recvbuf , strlen(recvbuf) ) ; 
	 }
}

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 bind has already used by SO_REUSEADDR 
	int on = 1 ;
	int opt ;
	if (( opt = setsockopt (listenfd ,SOL_SOCKET , SO_REUSEADDR, &on , sizeof(on) )) < 0 )
	{
		ERR_EXIT("setsocketopt") ;
	}
	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) )  ;
		int pid ;
		pid = fork()  ;  // creat the child process to set out the multi-connection from clients 
		if ( -1 == pid ) 
		{
			ERR_EXIT ("fork") ;
		}
		if ( 0 == pid ) 
		{
			close (listenfd) ; // the son's pid didn't need to process the listen , so we close listenfd ;
			do_service (conn) ;
			exit(EXIT_SUCCESS) ;
		}
		else 
		{
			close (conn) ;    // the father's pid didn't need  to process the acceept ,so we 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 ) 
	{
//		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 ;
}

ssize_t recv_peek ( int sockfd , void *buf , size_t len ) 
{
	while ( 1 ) 
	{
		int ret = recv ( sockfd , buf , len , MSG_PEEK ) ;   
//only let buf read the data from sockfd but not remove the data from that sockfd(socket) 
//in comparison with read function it receive the data and remove the data in the socket 
		if ( -1 == ret && EINTR == errno ) 
		{
			continue ;			//interrupted by signal 
		}
		return ret ;
	}
}

                                                                                                                                                                                                                                                      
ssize_t readline ( int sockfd , void *buf  , size_t maxline ) 
{
	int ret ;
	int nread ;
	char *bufp ;
	bufp = (char*) buf ;
	int nleft ;
	nleft = maxline ;
	while (1 ) 
	{
		ret = recv_peek ( sockfd, bufp , nleft ) ;
		if ( ret < 0 )    //failure 
		{
			return ret ;
		}
		else if ( 0 == ret )		// peer closed  
		{
			return ret ;
		}
		nread = ret ;
		int i ;
		for ( i = 0 ; i < nread ; i ++ ) 
		{
			if ( '\n' ==  bufp[i]) 
			{
				ret = readn (sockfd,bufp , i+1) ;
				if ( ret != i + 1 ) 
				{
					exit(EXIT_FAILURE) ;
				}
				return ret ;
			}
		}
		if ( nread > nleft )   // the data that we read(nread) can't be more than the real data in the socket 
		{
			exit ( EXIT_FAILURE ) ;
		}
		nleft -= nread ;
		ret = readn ( sockfd , bufp , nread ) ;  // read n characters from the socket ( don't contain '\n') 
		if ( ret != nread ) 
		{
			exit(EXIT_FAILURE ) ;	
		}
		bufp += nread ;
	}
	return -1 ;
}



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} ;
	memset ( sendbuf , 0 , sizeof(sendbuf) ) ;
	memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
	int n ;
	while( fgets ( sendbuf , sizeof(sendbuf) , stdin ) != NULL ) 
	{
		//fgets get the data with '\n' in default 
		writen ( sock , sendbuf ,strlen(sendbuf) ) ;     // the former 4 bytes and  the real data(n) 
		int ret = readline ( sock , recvbuf , sizeof(recvbuf) ) ;
                if ( -1 == ret )
                {
                        ERR_EXIT ("readline") ;
                }
                else if ( 0 == ret  )            //may interrupted by signal 
                {
                        printf ("clien________ 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、付费专栏及课程。

余额充值