“粘包”问题的起因与解决

本文参考于:徐晓鑫女士的《后台开发》

(1)因为nagle算法。什么是nagle算法,简而言之是为了避免浪费资源,将包合并发送的算法。就好比你一个人打车必然不划算,可以约几个和你一道的人一起打车,宾主尽欢。但是随之而来的问题是,因为一起发送,故接收方不知道一个包的骑士==起始和截止位置。

(2)因为我们网络编程时,其实是将接受到的数据置入缓冲区,待应用层读取,但若应用层阻塞于其他事情而归期未至,则缓冲区的包又难以区分。

解决办法:封包,解包。

封包:读取包的长度,将之发送

解包:读取包,将之视为要发送包的长度,循环读取。

//package_ser.cpp
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#define Def_port 6666
using namespace std;
int MyRecv(int isock,char *msg,size_t count)
{
	size_t tBytes=0;
	int ithislen;
	while(tBytes<count)
	{
		do{
			ithislen=read(isock,msg,count-tBytes);
		}while(ithislen<0&&errno==EINTR);
		if(ithislen<0)
			return (ithislen);
		else if(ithislen==0)
			return (tBytes);
		tBytes+=ithislen;
		msg+=ithislen;
	}
}

int main(int argc,char **argv)
{
	struct sockaddr_in ser,cli;
	memset(&ser,0,sizeof(ser));
	memset(&cli,0,sizeof(cli));
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd==-1)
	{
		perror("sockfd");
		return -1;
	}
	cout<<"crete sockfd success,is:"<<sockfd<<endl;
	ser.sin_family=AF_INET;
	ser.sin_port=htons(Def_port);
	ser.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(sockfd,(struct sockaddr *)&ser,sizeof(struct sockaddr))==-1)
	{
		perror("bind");
		return -1;
	}
	cout<<"bind success"<<endl;
	if(listen(sockfd,10)==-1)
	{
		perror("listen");
		return -1;
	}
	cout<<"listening......"<<endl;
	socklen_t len=sizeof(ser);
	char msg[10];
	int acceptfd=accept(sockfd,(struct sockaddr *)&ser,&len);
	if(acceptfd<0)
	{
		close(sockfd);
		perror("accept");
		return -1;
	}
	cout<<"accept success"<<endl;
	ssize_t readlen=MyRecv(acceptfd,msg,sizeof(int));
	if(readlen<0)
	{
		cout<<"read failured"<<endl;
		return -1;
	}
	int slen=( int)ntohl(*( int*)msg);//这一句耐人寻味,将字符串的前int位读取并强制转换为int,即包长
	cout<<"the length is:"<<slen<<endl;
	readlen=MyRecv(acceptfd,msg,slen);
	if(readlen<0)
	{
		cout<<"read failured"<<endl;
		//perror("readlen");
		//cout<<strerror(errno)<<endl;
		return -1;
	}
	msg[slen]='\0';
	cout<<msg<<endl;
	close(acceptfd);
	return 0;
}
//package_cli.cpp
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#define Def_port 6666
using namespace std;
 int MySend(int isockfd,char *msg,size_t tlen)
{
	int ithisSend;
	unsigned int isend=0;
	if(tlen==0)
		return 0;
	while(isend<tlen)
	{	do{
			ithisSend=send(isockfd,msg,tlen-isend,0);
		}while(ithisSend<0&&errno==EINTR);
		if(ithisSend<0)
			return isend;
		isend+=ithisSend;
		msg+=ithisSend;
	}
	return tlen;
}

int main(int argc,char **argv)
{
	struct sockaddr_in cli;
	memset(&cli,0,sizeof(cli));
	if(argc<2)
	{	cout<<"Uasge: client [server ip address]\n";
		return -1;
	}	
	cli.sin_family=AF_INET;
	cli.sin_port=htons(Def_port);
	cli.sin_addr.s_addr=inet_addr(argv[1]);
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		perror("socket");
		return -1;
	}
	cout<<"create sockfd success! is:"<<sockfd<<endl;
	if(connect(sockfd,(struct sockaddr*)&cli,sizeof(cli))<0)
	{
		perror("connect");
		return -1;
	}
	cout<<"connect success!"<<endl;
	char *sendmsg="0123456789";
	ssize_t wlen;
	int len=strlen(sendmsg);
	cout<<"the length is :"<<len<<endl;
	int ilen=0;
	char *buf=new char[100];
	*(int*)(buf+ilen)=htonl(len);
	ilen+=sizeof(int);    //前int位置包长
	memcpy(buf+ilen,sendmsg,len);
	ilen+=len;
	wlen=MySend(sockfd,buf,ilen);
	if(wlen<0)
	{
		cout<<"send data failured"<<endl;
		close(sockfd);
		return 0;
	}
	cout<<"send data success,the length is"<<wlen<<"     the data is:"<<sendmsg<<endl;
	close(sockfd);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/h17802535087/article/details/89301167
今日推荐