TCP客户端(处理粘包)实战

(已经经过测试没有问题了!)
服务端发送数据,粘包处理:

int dataLength =xystr.size();
            //将数据前四个字节放入数据长度以解决粘包
            int realLength = dataLength + sizeof(int);
            char *sendData = new char[realLength];
            memset(sendData, 0, realLength*sizeof(char));
            unsigned int num1, num2, num3, num4;
            char *p = (char *)&dataLength;
            num1 = ((int)*p);
            num2 = ((int)*(p+1));
            num3 = ((int)*(p+2));
            num4 = ((int)*(p+3));
            memcpy(sendData, &num4, sizeof(int));
            memcpy(sendData+sizeof(char), &num3, sizeof(int));
            memcpy(sendData+2*sizeof(char), &num2, sizeof(int));
            memcpy(sendData+3*sizeof(char), &num1, sizeof(int));
            memcpy(sendData+sizeof(int), xystr.c_str(), xystr.size());

客户端循环接收数据并处理粘包,解析json,客户端因为没有考虑到包头可能会在recvbuf的后四个字节里或者被分割,导致包头数值不正确,并且第二次取出的recvbuf也需要跳过剩余包头,不然会导致json解析失败,所以又加入了jump变量。处理包头的地方一定要注意细节!!!很容易出错的,并且不容易复现(因为包头恰巧在recvbuf后面四个字节的概率很小)

   #include<stdio.h> 
#include <sys/socket.h>   //connect,send,recv,setsockopt等 
#include <sys/types.h>      
#include <netinet/in.h>     // sockaddr_in, "man 7 ip" ,htons 
#include <poll.h>             //poll,pollfd 
#include <arpa/inet.h>   //inet_addr,inet_aton 
#include <unistd.h>        //read,write 
#include <netdb.h>         //gethostbyname 
#include <error.h>         //perror 
#include <stdio.h> 
#include <errno.h>         //errno 
#include <string.h>          // memset 
#include <iostream> 
#include<stdlib.h>
#include<boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using namespace boost::property_tree;
static int count=0;
void json_parase(std::string str)
{
	count++;
	std::stringstream s1(str);
	ptree pt1;
	try{
		read_json(s1,pt1);
		BOOST_FOREACH(boost::property_tree::ptree::value_type &v,pt1)
		{
			std::stringstream s2;
			write_json(s2,v.second);
			ptree pt2;
			read_json(s2,pt2);
			double x=pt2.get<double>("x");
			double y=pt2.get<double>("y");
			printf("\"X\":%lf,\"Y\":%lf\n",x,y);
		}
	}
	catch(ptree_error &e)
	{
		return;
	}
} 
#ifdef HAVE_NETINET_IN_H 
#include <netinet/in.h> 
#endif
#define ERR_EXIT(m)\
	do\
{\
	perror(m);\
	exit(EXIT_FAILURE);\
}while(0) 
int main(void) { 
	int sock; 
	if((sock =socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) 
	{ 
		ERR_EXIT("ERROR"); 
	} 
	struct sockaddr_in servaddr; 
	memset(&servaddr,0,sizeof(servaddr)); 
	servaddr.sin_family=AF_INET; 
	servaddr.sin_port = htons(1606); 
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
	if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))< 0) 
	{ 
		ERR_EXIT("connect"); 
	} 
	char sendbuf[1024]={0}; 
	char recvbuf[1024]={0}; 
	int j=0;
	char *data=NULL;
	char *data1;
	int len =0;
	int nianbaolen=0;
	int jump=0;//假如包头四个字节有的到第二条数据里了,就需要跳过jump个字节
	std::string value("");
	while(1) 
	{
		if(recv(sock,recvbuf,sizeof(recvbuf),MSG_WAITALL)>0)
		{
			if(len==0&&nianbaolen==0)
			{
				char *cint=(char *)&len;
				for(int i=4;i>0;i--)
				{
					(*(cint+i-1))=(int)recvbuf[4-i];
				}
				for(int i=4;i<1024;i++)
				{
					value.push_back(recvbuf[i]);
				}
				memset(recvbuf,0,sizeof(recvbuf));
				printf("收到数据包头显示的长度:%d\n",len);
				continue;
			}else{
				if(nianbaolen!=0)
				{
					printf("nianbaolen:%d\n",nianbaolen);
					char *cint=(char *)&len;
					jump=0;
					for(int i=nianbaolen;i>0;i--)
					{
						(*(cint +i-1))=(int)recvbuf[jump];
						jump++;
					}
					nianbaolen=0;
					printf("粘包头长度:%d\n",len);
					
				}
				if(len-value.size()>1024)
				{
					for(int i=jump;i<1024;i++)
					{
						value.push_back(recvbuf[i]);
					}
					jump=0;
					memset(recvbuf,0,sizeof(recvbuf));
					continue;

				}
				else{
					int i=jump;
					while(value.size()!=len)
					{
						value.push_back(recvbuf[i]);
						i++;
					}
					jump=0;
					len=0;
					printf("第%d次循环!",count+1);
					printf("最终字符串长度:%d\n",value.size());
					FILE *fp=fopen("data.txt","w");
					fprintf(fp,"%s",value.c_str());
					fclose(fp);
					json_parase(value);
					value="";
					if(i<1020)
					{
						printf("i<1020");
						for(int k=0;k<4;k++)
						{

							char *cint=(char *)&len;
							*(cint+k)=(int)recvbuf[i+3-k];
						}
						i+=4;
						while(i<1024)
						{
							value.push_back(recvbuf[i]);
							i++;
						}
						nianbaolen=0;
						printf("粘包包头长度:%d\n",len);
						
					}else{
						printf("i不<1020\n\n\n\n\n\n\n\n\n");
						nianbaolen=4;
						char *cint=(char *)&len;
						printf("i的值为:%d\n",i);
						while(i<1024)
						{
							*(cint+nianbaolen-1)=recvbuf[i];
							i++;
							nianbaolen--;
						}
						printf("还差%d个字节的包头长度:%d\n",nianbaolen,len);
					}
					memset(recvbuf,0,sizeof(recvbuf));
				}	
			}

		}
	} 
	close(sock); 
	return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值